什么是 AOP
- AOP是一种编程范式,通过添加切面点,在不改动已有核心逻辑代码的前提下,添加功能或更改代码流程。
AOP用来做什么?
- 我们可以将日志记录,性能统计,安全控制,事务处理,异常处理等代码从业务逻辑代码中划分出来,通过对这些行为的分离,我们希望可以将它们独立到非业务逻辑的方法中,进而在这些行为改变的时候不影响业务逻辑的代码
AOP的两种代理方式
以 AspectJ 为代表的静态代理。
静态代理是指 AOP 框架在编译阶段生成 AOP 代理类,因此也称为编译时增强。ApsectJ 是静态代理的实现之一,也是最为流行的。静态代理由于在编译时就生成了代理类,效率相比动态代理要高一些。AspectJ 可以单独使用,也可以和 Spring 结合使用。
以 Spring AOP 为代表的动态代理。
与静态代理不同,动态代理就是说 AOP 框架不会去修改编译时生成的字节码,而是在运行时在内存中生成一个 AOP 代理对象,这个 AOP 对象包含了目标对象的全部方法,并且在特定的切点做了增强处理,并回调原对象的方法。
Spring AOP 中的动态代理主要有两种方式:JDK 动态代理 和 CGLIB 动态代理。
JDK 代理通过反射来处理被代理的类,并且要求被代理类必须实现一个接口。核心类是
InvocationHandler
接口 和Proxy
类。 而当目标类没有实现接口时,Spring AOP 框架会使用 CGLIB 来动态代理目标类。CGLIB(Code Generation Library),是一个代码生成的类库,可以在运行时动态的生成某个类的子类。
CGLIB 是通过继承的方式做的动态代理,因此如果某个类被标记为
final
,那么它是无法使用 CGLIB 做动态代理的。核心类是MethodInterceptor
接口和Enhancer
类
基本术语
(1)切面(Aspect)
切面是一个横切关注点的模块化,一个切面能够包含同一个类型的不同增强方法,比如说事务处理和日志处理可以理解为两个切面。切面由切入点和通知组成,它既包含了横切逻辑的定义,也包括了切入点的定义。 Spring AOP就是负责实施切面的框架,它将切面所定义的横切逻辑织入到切面所指定的连接点中。
- 使用
@Aspect
注解的类就是切面
(2) 目标对象(Target)
目标对象指将要被增强的对象,即包含主业务逻辑的类对象。或者说是被一个或者多个切面所通知的对象。
(3) 连接点(JoinPoint)
程序执行过程中明确的点,如方法的调用或特定的异常被抛出。连接点由两个信息确定:
- 方法(表示程序执行点,即在哪个目标方法)
- 相对点(表示方位,即目标方法的什么位置,比如调用前,后等)
简单来说,连接点就是被拦截到的程序执行点,因为Spring只支持方法类型的连接点,所以在Spring中连接点就是被拦截到的方法。
- 使用
@Before("pointcut()")
实现
(4) 切入点(PointCut)
切入点是对连接点进行拦截的条件定义。切入点表达式如何和连接点匹配是AOP的核心,Spring缺省使用AspectJ切入点语法。
一般认为,所有的方法都可以认为是连接点,但是我们并不希望在所有的方法上都添加通知,而切入点的作用就是提供一组规则(使用 AspectJ pointcut expression language 来描述) 来匹配连接点,给满足规则的连接点添加通知。
- 使用
@Pointcut("execution(* com.yangl.test.aop.service..*(..))")
实现 - 匹配规则是
com.yangl.test.aop.service
包下的所有类的所有函数。
(5) 通知(Advice)
通知是指拦截到连接点之后要执行的代码,包括了“around”、“before”和“after”等不同类型的通知。Spring AOP框架以拦截器来实现通知模型,并维护一个以连接点为中心的拦截器链。
1 | // @Before说明这是一个前置通知,log函数中是要前置执行的代码,JoinPoint是连接点, |
(6) 织入(Weaving)
织入是将切面和业务逻辑对象连接起来, 并创建通知代理的过程。织入可以在编译时,类加载时和运行时完成。在编译时进行织入就是静态代理,而在运行时进行织入则是动态代理。
(7) 增强器(Adviser)
Advisor是切面的另外一种实现,能够将通知以更为复杂的方式织入到目标对象中,是将通知包装为更复杂切面的装配器。Advisor由切入点和Advice组成。
Advisor这个概念来自于Spring对AOP的支撑,在AspectJ中是没有等价的概念的。Advisor就像是一个小的自包含的切面,这个切面只有一个通知。切面自身通过一个Bean表示,并且必须实现一个默认接口。
1 | //AbstractPointcutAdvisor是默认接口 |