Spring AOP
和 AspectJ
是Java
中最流行的 AOP
框架
AOP
AOP
实现的关键就在于 AOP
框架自动创建的 AOP
代理,AOP
代理则可分为静态代理和动态代理两大类,其中静态代理是指在编译阶段修改目标类字节码,属于静态编入,不需要代理类,因此也称为编译时增强;而动态代理则在运行时借助于 JDK
动态代理、CGLIB
等在内存中“临时”生成 AOP
动态代理类,因此也被称为运行时增强。其中aspectj
为静态代理,Spring AOP
是动态代理
AOP核心概念
Advice
:通知,是指拦截到jointpoint
之后所要做的事情即特定的Jointpoint
处运行的代码。JoinPoint
:连接点,它定义在哪里(哪些点)加入你的逻辑功能,基本每个方法的前后(两者都有也行),或抛出异常时都可以是连接点,spring
只支持方法连接点PointCut
:切入点的集合,即一组Joinpoint
,就是说一个Advice
可能在多个地方织入。比如一个类里,有15个方法,那就有几十个连接点了,让切点来筛选连接点,选中那几个你想要的方法。Aspect
:切面,实际是通知和切入点的组合,通知说明了干什么和什么时候干(什么时候通过方法名中的before
,after
,around
等),而切入点说明了在哪干(指定到底是哪个方法)。Weaving
:织入,把切面应用到目标对象来创建新的代理对象的过程。
Aspectj
ApectJ
主要采用的是编译期织入,在这个期间使用AspectJ
的acj
编译器把aspect
类编译成class
字节码后,在java目标类编译时织入,即先编译aspect
类再编译目标类。
举例
安装 AspectJ
见:https://www.ibm.com/developerworks/cn/java/j-lo-springaopcglib/index.html
- 业务逻辑
|
|
- 切面
Aspect
使用AspectJ
编写一个Aspect
|
|
ajc
编译
|
|
|
|
可以发现切面代码织入了HelloWorld.class
|
|
从Aspect
编译后的class
文件可以更明显的看出执行的逻辑。proceed
方法就是回调执行被代理类中的方法。
- 执行结果
|
|
Spring AOP
Spring AOP
使用的是动态代理增强,动态代理不会改变类的字节码,而是动态的生成代理对象。Spring AOP
的动态代理机制有两种方式:JDK动态代理和CGLIB动态代理。
JDK动态代理
JDK动态代理需要被代理的类必须实现一个接口,底层通过反射实现
CGLIB动态代理
CGLIB动态代理可以不用需要被代理类必须实现接口,底层通过继承实现,因此如果某个类被标记为
final
,那么它是无法使用CGLIB做动态代理的
创建AOP代理
|
|
如果配置了<aop:aspectj-autoproxy proxy-target-class="true"/>
或者目标类没有实现接口,则使用CGLIB
代理;如果目标类实现了接口,默认使用JDK
代理
Spring AOP vs ApectJ
Spring AOP
与 ApectJ
的目的一致,都是为了统一处理横切业务,但与AspectJ
不同的是:
Spring AOP
并不尝试提供完整的AOP功能,Spring AOP
更注重的是与Spring IOC
容器的结合,并结合该优势来解决横切业务的问题,因此在AOP
的功能完善方面,相对来说AspectJ
具有更大的优势。AspectJ
在AOP
的实现方式上依赖于特殊编译器(ajc编译器),而Spring
回避了这点,转向采用动态代理技术的实现原理来构建Spring AOP
的内部机制(动态织入),这是与AspectJ
(静态织入)最根本的区别
注意:
Spring 2.0
后便使用了与AspectJ
一样的注解,新增了对AspectJ
切点表达式支持。需要注意Spring
只是使用了与 AspectJ
一样的注解,但仍然没有使用 AspectJ
的编译器,底层仍然是动态代理技术的实现