前言
Spring AOP作为整个Spring体系最最重要的分支之一,若干技术都是基于它的(比如事务、异步、缓存等)
前面通过好几篇文章,已经把Spring AOP内部的的运行流程等基本都叙述了一遍,但是通过小伙伴们反馈,还是晕头转向的:类太多了,且很多概念感觉很模糊,不知道咋用的。
因此本文就站在一个更高的角度,对Spring AOP部分用到的一些组件,进行概念上的、宏观上的进行一个概述和分析总结。
Spring AOP概述
AOP(Aspect-Oriented Programming)面向切面编程,它是在OOP的基础上诞生的一种编程模型。我们可以通熟易懂的理解为:在程序中具有公共特性的某些类/某些方法上进行拦截, 在方法执行的前面/后面/执行结果返回后 增加执行一些方法。
在Spring中,实现的底层原理是通过动态代理 / CGLIB来做的
Spring AOP组件总结
1、Pointcut
这个类位于 org.springframework.aop 包中,它的作用就是定义切面的匹配点。(简单的说就是我去切哪些类、哪些方法…) 在 Spring Aop 中匹配的点主要是 class 与 method 这两个方面, 分别为ClassFilter 与 MethodFilter
// 由 ClassFilter 与 MethodMatcher 组成的 pointcut public interface Pointcut { // 类过滤器, 可以知道哪些类需要拦截 ClassFilter getClassFilter(); // 方法匹配器, 可以知道哪些方法需要拦截 MethodMatcher getMethodMatcher(); // 匹配所有对象的 Pointcut Pointcut TRUE = TruePointcut.INSTANCE; }
在 Spring 中主要有以下几个类,介绍如下:
- NameMatchMethodPointcut:通过刚发名进行精确匹配的。 (PS: 其中 ClassFilter = ClassFilter.TRUE)
- ControlFlowPointcut:根据在当前线程的堆栈信息中的方法名来决定是否切入某个方法(效率较低)
- ComposablePointcut:组合模式的 Pointcut, 主要分成两种: 1.组合中所有都匹配算成功 2. 组合中都不匹配才算成功
- JdkRegexpMethodPointcut:通过 正则表达式来匹配方法(PS: ClassFilter.TRUE)
- AspectJExpressionPointcut:通过 AspectJ 包中的组件进行方法的匹配(切点表达式)
- TransactionAttributeSourcePointcut:通过 TransactionAttributeSource 在 类的方法上提取事务注解的属性 @Transactional 来判断是否匹配, 提取到则说明匹配, 提取不到则说明匹配不成功
- AnnotationJCacheOperationSource:支持JSR107的cache相关注解的支持
上述中的 TransactionAttributeSourcePointcut 其实就是Spring注解式事务的 Pointcut。通过匹配方法上 @Transactional 标签来确定方法是否匹配;(事务篇会分析它的源码)
2、Advice
Advice: 建议忠告, 劝告, 通知。它其实最开始是 aopalliance 包中的一个空接口, 接口的存在主要是为了标示对应类为 Advice;
在Spring Aop 中 Advice 其实表示的是在 Pointcut 点上应该执行的方法。而这些方法可以在目标方法之前、之后、包裹、抛出异常等等任何地方执行。
Advice: 其主要分成两类:普通advice 与Interceptor/MethodInterceptor:
普通Advice :
- MethodBeforeAdvice:在目标方法之前执行,主要实现有:
1. AspectJMethodBeoreAdvice:这是通过解析被 org.aspectj.lang.annotation.Before 注解注释的方法时解析成的Advice
2.AfterReturningAdvice:在切面方法执行后(这里的执行后指不向外抛异常, 否则的话就应该是 AspectJAfterThrowingAdvice 这种 Advice) 主要实现有:
1. AspectJAfterAdvice:解析 AspectJ 中的 @After 注解来生成的 Advice(PS: 在java中的实现其实就是在 finally 方法中调用以下对应要执行的方法)
2. AspectJAfterReturningAdvice:解析 AspectJ 中的@AfterReturning 属性来生成的 Advice(PS: 若切面方法抛出异常, 则这里的方法就将不执行)
3. AspectJAfterThrowingAdvice:解析 AspectJ 中的 @AfterThrowing 属性来生成的 Advice(PS: 若切面方法抛出异常, 则这里的方法就执行)
3.AspectJAroundAdvice:将执行类 MethodInvocation(MethodInvocation其实就是Pointcut) 进行包裹起来, 并控制其执行的 Advice (其中 Jdk中中 Proxy 使用ReflectiveMethodInvocation, 而 Cglib 则使用 CglibMethodInvocation)
注意,注意,注意:在Proxy中最终执行的其实都是MethodInterceptor,因此这些Advice最终都是交给 AdvisorAdapter -> 将 advice 适配成 MethodInterceptor
MethodInterceptor:
- ExposeInvocationInterceptor:将当前 MethodInvocation 放到当前线程对应的 ThreadLoadMap里面的, 这是一个默认的 Interceptor, 在AspectJAwareAdvisorAutoProxy获取何时的 Advisor 时会调用自己的 extendAdvisors 方法, 从而将 ExposeInvocationInterceptor 方法执行链表的第一位。它的实现非常简单:
private ExposeInvocationInterceptor() { } @Override public Object invoke(MethodInvocation mi) throws Throwable { MethodInvocation oldInvocation = invocation.get(); invocation.set(mi); try { return mi.proceed(); } finally { invocation.set(oldInvocation); } }
2.SimpleTraceInterceptor:Spring内置了很多日志跟踪的拦截器,父类AbstractTraceInterceptor有多个日志实现:
CustomizableTraceInterceptor:对方法调用前后拦截一下
public class Main { public static void main(String[] args) { ProxyFactory factory = new ProxyFactory(new Person()); factory.setProxyTargetClass(true); // 强制私用CGLIB 以保证我们的Person方法也能正常调用 // 记录日志的切面 CustomizableTraceInterceptoradvice = new CustomizableTraceInterceptor(); // 切点+通知(注意:此处放的是复合切面) Advisor advisor = new DefaultPointcutAdvisor(advice); factory.addAdvisor(advisor); Person p = (Person) factory.getProxy(); p.run(); p.say(); } } 输出: 17:28:56.054 [main] TRACE o.s.a.i.CustomizableTraceInterceptor - Entering method 'run' of class [com.fsx.bean.Person] 我在run... 17:28:56.078 [main] TRACE o.s.a.i.CustomizableTraceInterceptor - Exiting method 'run' of class [com.fsx.bean.Person] 17:28:56.079 [main] TRACE o.s.a.i.CustomizableTraceInterceptor - Entering method 'say' of class [com.fsx.bean.Person] 我在say... 17:28:56.079 [main] TRACE o.s.a.i.CustomizableTraceInterceptor - Exiting method 'say' of class [com.fsx.bean.Person]
SimpleTraceInterceptor:正常效果同上,异常也是同样的输出,没CustomizableTraceInterceptor强大
DebugInterceptor:SimpleTraceInterceptor的子类。有个计数器,记录被拦截的次数,且可以这样获取出来advice.getCount()
PerformanceMonitorInterceptor:记录每个方法运行的时长,还是比较有用的.
我在run... 17:37:10.853 [main] TRACE o.s.a.i.PerformanceMonitorInterceptor - StopWatch 'com.fsx.bean.Person.run': running time (millis) = 29 我在say... 17:37:10.854 [main] TRACE o.s.a.i.PerformanceMonitorInterceptor - StopWatch 'com.fsx.bean.Person.say': running time (millis) = 0
3.AfterReturningAdviceInterceptor:这个类其实就是将 AfterReturningAdvice 包裹成 MethodInterceptor 的适配类, 而做对应适配工作的就是 AfterReturningAdviceAdapter
4.MethodBeforeAdviceInterceptor:这个类其实就是将 MethodBeforeAdvice 包裹成 MethodInterceptor 的适配类, 而做对应适配工作的就是 MethodBeforeAdviceAdapter
5.ThrowsAdviceInterceptor:这个类其实就是将 ThrowsAdvice 包裹成 MethodInterceptor 的适配类, 而做对应适配工作的就是 ThrowsAdviceAdapter
6.TransactionInterceptor:这个类就是大名鼎鼎的注解式事务的工具类, 这个类通过获取注解在方法上的 @Transactional 注解的信息来决定是否开启事务的 MethodInterceptor (PS: 在注解式事务编程中将详细叙述)
1、无论通过aop命名空间/AspectJ注解注释的方法, 其最终都将解析成对应的 Advice
2、所有解析的 Advice 最终都将适配成 MethodInterceptor, 并在 JdkDynamicAopProxy/CglibAopProxy中进行统一调用
3、Advisor
Advisor 其实它就是 Pointcut 与 Advice 的组合, Advice 是执行的方法, 而要知道方法何时执行, 则 Advice 必需与 Pointcut 组合在一起, 这就诞生了 Advisor 这个类
打个比方:
Advice表示建议
Pointcut表示建议的地点
Advisor表示建议者(它知道去哪建议,且知道是什么建议)
- PointcutAdvisor: 我们在 Spring 中常用的 Advisor, 包含一个 Pointcut 与一个 advice
- AspectJPointcutAdvisor: 这个是 Spring 解析 aop 命名空间时生成的 Advisor(与之对应的 Advice 是 AspectJMethodBeforeAdvice, AspectJAfterAdvice, AspectJAfterReturningAdvice, AspectJAfterThrowingAdvice, AspectJAroundAdvice, Pointcut 则是AspectJExpressionPointcut), 对于这个类的解析是在 ConfigBeanDefinitionParser
- InstantiationModelAwarePointcutAdvisorImpl: 这个Advisor是在Spring解析被 @AspectJ注解注释的类时生成的 Advisor, 而这个 Advisor中的 Pointcut与Advice都是由 ReflectiveAspectJAdvisorFactory 来解析生成的(与之对应的 Advice 是 AspectJMethodBeforeAdvice, AspectJAfterAdvice, AspectJAfterReturningAdvice, AspectJAfterThrowingAdvice, AspectJAroundAdvice, Pointcut 则是AspectJExpressionPointcut), 解析的步骤是: AnnotationAwareAspectJAutoProxyCreator.findCandidateAdvisors() -> BeanFactoryAspectJAdvisorsBuilder.buildAspectJAdvisors() -> ReflectiveAspectJAdvisorFactory.getAdvisors() -> ReflectiveAspectJAdvisorFactory.getAdvisor() 最终生成了 InstantiationModelAwarePointcutAdvisorImpl (当然包括里面的 Pointcut 与 advice 也都是由 ReflectiveAspectJAdvisorFactory 解析生成的)
- TransactionAttributeSourceAdvisor: 一个基于 MethodInterceptor(其实是 TransactionInterceptor)与 TransactionAttributeSourcePointcut 的Advisor, 而这个类最常与 TransactionProxyFactoryBean使用
- DefaultPointcutAdvisor: 最常用的 Advisor, 在使用编程式aop时, 很多时候会将 Advice / MethodInterceptor 转换成 DefaultPointcutAdvisor
- NameMatchMethodPointcutAdvisor: 这个是在使用 NameMatchPointcutAdvisor时创建的 Advisor, 主要是通过 方法名来匹配是否执行 Advice
- RegexpMethodPointcutAdvisor: 基于正则表达式来匹配 Pointcut 的 Advisor, 其中的 Pointcut 默认是 JdkRegexpMethodPointcut
- Spring 中解析 aop:advisor 时生成的 Advisor, 见 ConfigBeanDefinitionParser.parseAdvisor
- BeanFactoryTransactionAttributeSourceAdvisor: 在注解式事务编程时, 主要是由 BeanFactoryTransactionAttributeSourceAdvisor, AnnotationTransactionAttributeSource, TransactionInterceptor 组合起来进行事务的操作(PS: AnnotationTransactionAttributeSource 主要是解析方法上的 @Transactional注解, TransactionInterceptor 是个 MethodInterceptor, 是正真操作事务的地方, 而BeanFactoryTransactionAttributeSourceAdvisor 其实起着组合它们的作用); <- 与之相似的还有 BeanFactoryCacheOperationSourceAdvisor