1.@Aspect注解
(1) @Aspect注解用于声明一个切面类,我们可在该类中来自定义切面,早在Spring之前,AspectJ框架中就已经存在了这么一个注解,而Spring为了提供统一的注解风格,因此采用了和AspectJ框架相同的注解方式,这便是@Aspect注解的由来,换句话说,在Spring想做AOP框架之前,AspectJ AOP框架就已经很火了,而直接把AspectJ搬过来又不现实,因此,Spring想了一个折中的方案,即只使用AspectJ框架的声明,写法和定义方式(比如@Aspect注解),而底层由Spring自己实现,这样,就避免了我们程序员从AspectJ AOP切换到Spring AOP后,还要再去学一套新的写法了,也正因为如此,如果想要使用Spring AOP,就必须依赖aspectjweaver.jar包(不然谁来提供写法和定义方式),我们可以通过maven进行导入,如下
<!-- 添加对AspectJ框架的依赖 -->
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjrt</artifactId>
<version>1.9.5</version>
</dependency>
<!-- 除了上面的方式外,也可以直接使用spring-aspects依赖,它里面包含了对AspectJ的依赖 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
<version>${spring.framework.version}</version>
</dependency>
(2) 同时还需使用@EnableAspectJAutoProxy注解来开启Spring对于AspectJ注解的支持,如下
@Configuration
@EnableAspectJAutoProxy
public class Config {
}
如果是基于xml的配置,可通过如下标签进行开启
<aop:aspectj-autoproxy/>
2.自定义一个切面类
(1) 在基于注解的配置下,除了使用@Aspect注解外,还需要声明该切面是一个bean,否则,spring在扫描过程中是会忽略掉这个类的,如下
@Aspect
@Component
public class Logger {
}
(2) 对上面的例子,基于xml配置的写法如下
@Aspect
public class Logger {
}
<!-- xml配置文件中 -->
<beans ...>
<!-- 无论何种配置方式,不要忘了将切面类注册为spring的一个bean -->
<bean id="logger" class="cn.example.spring.boke.Logger"></bean>
</beans>
(3) 由@Aspect注解标注的类,称之为切面类,与普通的类一样,都有成员方法与成员变量,不同的是,切面类还可以包含连接点,通知,引介等与AOP有关的东西
(4) 切面不能再被增强,如果想拿一个切面来增强另一个切面,是不可能的,Spring会将切面类从自动代理(auto-proxying)中排除
3.自定义一个切入点
(1) Spring AOP中的切入点目前只可能是bean中的方法,而对于一个普通类中的方法,是不可能成为切入点的,在Spring中,声明一个切入点主要包括两个部分:一个切入点签名以及一个切入点表达式,如下
//如下定义了一个叫做anyExampleAMethod的切入点,这个切入点会匹配cn.example.spring.boke包下的ExampleA类中的任何方法
//其中,(1)就代表的是切入点表达式,(2)就代表的是切入点签名,注意,这个签名的返回值必须是void
@Pointcut("execution(* cn.example.spring.boke.ExampleA.*(..))") //(1)
public void anyExampleAMethod() {} //(2)
(2) Spring AOP的切入点表达式中,支持如下等切入点标识符
-
execution:最为常用,用于匹配某个包,某个类中的方法
-
within:进行类型匹配,用于匹配某个包下所有类的所有方法或某个指定类中的所有方法,如下
//指定了within的类型,这个切入点会匹配cn.example.spring.boke包下ExampleA类中的任何方法
@Pointcut("within(cn.example.spring.boke.ExampleA)")
public void withinDesignator(){}
- this:进行类型匹配,用于匹配生成的代理对象的类型是否为指定类型,如下
//此前我们提到过,Spring AOP中的底层实现分为jdk动态代理和cglib动态代理,jdk动态代理基于接口,要求目标对象必须实现某个接口,而cglib动态代理基于继承,因此不同的实现方式下,导致Spring生成的代理对象的类型可能不同,这就是this标识符的基础
//首先定义一个接口
public interface Parent {
void register();
void sendEmail();
}
//让我们的ExampleA类,实现这个接口
@Component
public class ExampleA implements Parent{
public void register() {
}
public void sendEmail() {
}
}
//设置@EnableAspectJAutoProxy注解中的proxyTargetClass属性值为false,表示使用jdk动态代理,为true,表示使用cglib动态代理,默认值为false,不过我们这里显式的声明出来
@Configuration
@EnableAspectJAutoProxy(proxyTargetClass = false)
@ComponentScan(basePackages = "cn.example.spring.boke")
public class Config {
}
//切面类,在其中声明一个this标识符,并指定类型为ExampleA
@Aspect
@Component
public class Logger {
/**
* this标识符,进行类型匹配,用于匹配代理对象的类型是否为指定类型
*/
@Pointcut(value = "this(cn.example.spring.bo