深入理解Spring AOP之基本概念
AOP到底是什么
-
面向切面——Spring提供了面向切面编程的丰富支持,允许通过分离应用的业务逻辑与系统级服务(例如审计(auditing)和事务(transaction)管理)进行内聚性的开发。应用对象只实现它们应该做的——完成业务逻辑——仅此而已。它们并不负责(甚至是意识)其它的系统级关注点,例如日志或事务支持。
如下图所示:日志信息,权限信息,安全服务在三个服务中都存在,使用AOP使得这三个服务能够解耦出来
AOP的一些概念
通知(Advice)
- Before
在方法被调用之前调用
- After
在方法完成后调用通知,无论方法是否执行成功
- After-returning
在方法成功执行之后调用通知
- After-throwing
在方法抛出异常后调用通知
- Around
通知了好、包含了被通知的方法,在被通知的方法调用之前后调用之后执行自定义的行为
连接点(Join point)
比如:方法调用、方法执行、字段设置/获取、异常处理执行、类初始化、甚至是 for 循环中的某个点
理论上, 程序执行过程中的任何时点都可以作为作为织入点, 而所有这些执行时点都是 Joint point
但 Spring AOP 目前仅支持方法执行 (method execution)
切点(Pointcut)
-
直接指定 Jointpoint 所在的方法名, 功能比较单一, 通常只支持方法级别的 AOP 框架
-
正则表达式
-
特定的描述语言, 如 AspectJ 提供的 Pointcut 描述语言
切面(Aspect)
引入(Introduction)
织入(Weaving)
Spring 对AOP的支持
- AspectJ
- JBoss AOP
- Spring AOP
Spring提供了四种各具特色的AOP支持
- 基于代理的经典AOP
- @AspectJ 注解驱动的切面
- 纯POJO切面
- 注入式AspectJ切面(适合Spring各个版本)
前面三中都是Spring基于代理的AOP变体,因此,Spring对AOP的支持局限于方法拦截。如果AOP需求超过了简单的方法拦截范畴,那么应该考虑在ASpectJ里实现切面,利用Spring的DI把Spring BEan注入到ASpectJ切面中
Spring实现方式
实现AOP的技术,主要分为两大类:一是采用动态代理技术,利用截取消息的方式,对该消息进行装饰,以取代原有对象行为的执行;二是采用静态织入的方式,引入特定的语法创建“方面”,从而使得编译器可以在编译期间织入有关“方面”的代码。重点关注动态代理技术Spring的动态代理包括两个部分: - JDK动态代理
JDK动态代理主要涉及到java.lang.reflect包中的两个类:Proxy和InvocationHandler。InvocationHandler是一个接口,通过实现该接口定义横切逻辑,并通过反射机制调用目标类的代码,动态将横切逻辑和业务逻辑编制在一起。 Proxy利用InvocationHandler动态创建一个符合某一接口的实例,生成目标类的代理对象。
- CGLib动态代理
CGLib全称为Code Generation Library,是一个强大的高性能,高质量的代码生成类库,可以在运行期扩展Java类与实现Java接口,CGLib封装了asm,可以再运行期动态生成新的class。和JDK动态代理相比较:JDK创建代理有一个限制,就是只能为接口创建代理实例,而对于没有通过接口定义业务方法的类,则可以通过CGLib创建动态代理。
Spring AOP 框架对 AOP 代理类的处理原则是:- 如果目标对象的实现类实现了接口,Spring AOP 将会采用 JDK 动态代理来生成 AOP 代理类;
- 如果目标对象的实现类没有实现接口,Spring AOP 将会采用 CGLIB 来生成 AOP 代理类——不过这个选择过程对开发者完全透明、开发者也无需关心。
Spring AOP 会动态选择使用 JDK 动态代理、CGLIB 来生成 AOP 代理,如果目标类实现了接口,Spring AOP 则无需 CGLIB 的支持,直接使用 JDK 提供的 Proxy 和 InvocationHandler 来生成 AOP 代理即可。