这里不介绍基本的AOP,只记录我在学习过程中自己发现需要注意的点。
需要注意的点
1. 启用AOP
AOP需要显式的启用才能使用,除了在切面编程类上声明@Aspect
之外,如果使用Java config的配置方式,需要在配置类上注明@EnableAspectJAutoProxy
.如果是xml配置的话,需要使用<aop:config/>
然后Aop的类,也需要通过声明@compent之类的纳入spring管理
2. JoinPoint 和 ProceedingJoinPoint
除了环绕增强,其他类型的增强都是使用JoinPoint的,而环绕增强,需要在切面方法里面显式调用被代理的方法,所以使用ProceedingJoinPoint,调用其中新增的proceed
方法。
3. 代理方式
Spring 使用动态代理,可以是JDK自己的动态代理,也可以是字节码增强技术。其中如果使用动态代理方式的话,则能被代理的对象一定要扩展了接口。这在AOP的设置里也会是有的。一般都是用JDK自己的动态代理技术,进而,如果没有接口的话,代理失败。
Spring 的切面,囿于动态代理的方式,只能提供方法级别的切面,实质是通过代理类包装被代理类,拦截对方法的调用。对静态方法、字段是没有办法做切面的。
4. Spring 和AspectJ 有差异
Spring AOP 只提供了AspectJ的一个子集的能力,AspectJ是可以实现对静态方法和字段的切面的。此外,AspectJ还提供了更丰富的API。一些在Spring AOP中模棱两可的概念,在AspectJ中都有不同的用途。
尤其 ApsectJ中除了execution还有call的用法,对target,this的含义有扩展,这里不表
例如
1. 重要!!! target VS this
在Spring AOP中,target代表的是动态代理中,被代理的对象,this代表的是代理对象本身(见这里)[https://stackoverflow.com/a/25962099]。
这会带来一个使用target 和 this,表现不一样的地方:
例如target(com.test.Test)
按理说会拦截所有被代理对象是Test的方法,但是,这个切点,也会拦截Object上的toString方法。
原因就是上面提到的,通过target来做切面,被拦截到的是所有被代理的对象是Test的,而不管当前被拦截的对象实际是什么,通过断点调试,我们可以看到,拦截到toString方法的时候,实际被拦截到的方法是Object.toString()。其实这个方法和我们想要的啥关系都没有,只是代理对象继承了Object的toString方法而已。但是由于被代理对象,也就是target还是Test,所以也被拦截了。
而对于this(com.test.Test)
,上面这种情况拦截到Object的toString方法的情况就不会发生,因为this判断的代理对象的类型,所有继承来的方法(没有被重写),实际切点拦截到的类型不是Tests,而是父类,所以不在拦截的范畴内。
所以看到有些博客写着这么一句话:
使用this做切面,不会拦截到继承的方法,等效于getDeclearMethod得到的方法,去掉其中的私有方法(因为私有方法不会被外部调用,不能代理),使用target会拦截到继承而来的方法,等效于getMethod得到的方法
3. @annotation VS @with
@annotation 只能对方法级别注解做切面,表示对指定注解标记的方法 做节点
@with 只能对类级别注解使用,表示对被指定注解 标记的类下的方法做切点
4. 拦截所有实现某个接口或者类的子类
例如,如果想拦截所有实现了com.test.Test
的子类,使用com.test.Test+
可以做到,注意这里的加号