转发地址:https://www.iteye.com/blog/elim-2394762
2 基于Aspectj注解的Spring Aop简单实现
Spring Aop是基于Aop框架Aspectj实现的,它不是完完全全的对Aspectj框架进行扩展和改造,而是利用Aspectj里面的一些功能来实现自己的Aop框架,其中就包括对Aspectj提供的注解的解析。之前已经提过Spring Aop和Aspectj实现的Aop之间的差别,这里就不再赘述。本文主要描述的是如何利用Aspectj提供的注解来实现Spring Aop功能,旨在让大家对Spring Aop、对使用Aspectj注解开发Spring Aop有一个初步印象。
2.1 启用对Aspectj注解的支持
使用Aspectj注解来实现Spring Aop时我们首先需要启用Spring对Aspectj注解支持的功能,这是通过配置来进行的。当我们的Spring配置是以配置文件为主时,我们可以通过在Spring的配置文件中引入aop相关的schema,然后通过<aop:aspectj-autoproxy/>
来启用对Aspectj注解的支持。
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop" xsi:schemaLocation="
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd">
<context:annotation-config/>
<context:component-scan base-package="com.elim.test"/>
<!-- 启用对Aspectj注解的支持 -->
<aop:aspectj-autoproxy/>
</beans>
当我们的配置是通过使用@Configuration标注的配置类来进行的时候,我们就可以通过在该配置类上使用@EnableAspectJAutoProxy进行标注来启用对Aspectj注解的支持。
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.EnableAspectJAutoProxy;
@Configuration
@EnableAspectJAutoProxy
public class AppConfig {
}
2.2 定义切面类
定义切面类比较简单,只需要定义一个简单的使用@Aspect进行标注的类即可。@Aspect是Spring Aop识别切面类的一个标记,但是光使用@Aspect对切面类进行标注还不行。因为Spring Aop只能对定义在bean容器中的bean发生作用,对应的切面类也必须是定义在bean容器中的bean对象时其才能发现。@Aspect不具备默认让Spring扫描到它,把对应的类实例化为Spring bean的功能,所以我们必须要在bean容器中定义该bean。可以通过配置文件定义,也可以通过使用@Component进行标注等。
@Aspect
@Component
public class MyAspect {
}
使用@Aspect标注的切面类也可以像普通类一样定义普通的属性和方法,如果有需要也可以把它当做一个普通的bean使用。
2.3 定义Pointcut
Pointcut是用来定义切面类需要作用的JoinPoint的,在Spring Aop中这些JoinPoint其实就是一些我们需要进行切入的方法执行,因为之前我们说过Spring Aop只支持对bean方法执行的切入。基于Aspect注解形式定义的Pointcut的核心是@Pointcut注解,我们需要在Aspect类中定义一个没有返回值的方法,方法类型可任意,然后在该方法上使用@Pointcut进行标注,表示其是一个Pointcut定义,对应的方法名即表示该Pointcut的名称。@Pointcut有一个value属性,其通常是一个表达式,通过它可以指定当前Pointcut需要作用的JoinPoint。表达式可以有很多种写法,这个在后续会专门讲解,通常用的最多的就是execution,表示方法的执行。如我们想定义一个Pointcut的JoinPoint为所有add方法的执行,那么我们可以如下定义。
@Aspect
@Component
public class MyAspect {
@Pointcut("execution(* add(..))")
private void pointcut() {}
}
2.4 定义Advice
Advice需要与Pointcut绑定在一起,用以定义需要在指定的Pointcut匹配的JoinPoint处执行的操作。Advice主要有三种类型,before、after和around,Aspectj对它们都有对应的注解进行支持。基于Aspectj注解的advice定义是通过对应的注解来指定的,我们需要在切面类中定义一个方法,然后在该方法上使用对应的注解进行标注。对应的advice注解都有一个value属性,我们需要通过它来指定与之绑定的Pointcut,对应的Pointcut需要通过Pointcut定义的类全名称.方法名()来指定,如果是在当前切面类中定义的Pointcut则可以省略对应的类名称。这里主要拿before来做一个示例,如下,我们在切面类中定义了一个方法before,并用@Before注解标注了该方法,同时指定了其所绑定的Pointcut为同一个切面类中定义的pointcut。
@Aspect
@Component
public class MyAspect {
@Pointcut("execution(* add(..))")
private void pointcut() {}
@Before("com.elim.test.spring.aop.MyAspect.pointcut()")
private void before(JoinPoint joinPoint) {
System.out.println(joinPoint.getTarget() + "----------------------Before---------------------");
}
}
至此,当我们在访问Spring bean容器中任意bean对象的add方法前就会调用MyAspect切面类中定义的before方法了。
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applicationContext.xml")
public class AopTest {
@Autowired
private UserService userService;
@Test
public void test1() {
User user = new User(1, "ZhangSan");
userService.add(user);
}
}
(注:本文是基于Spring4.1.0所写,写于2016年9月5日星期一)