前言
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
注解注释的方法时解析成的AdviceAfterReturningAdvice
:在切面方法执行后(这里的执行后指不向外抛异常, 否则的话就应该是AspectJAfterThrowingAdvice
这种 Advice) 主要实现有: 1.AspectJAfterAdvice
:解析 AspectJ 中的@After
注解来生成的 Advice(PS: 在java中的实现其实就是在 finally 方法中调用以下对应要执行的方法) 2.AspectJAfterReturningAdvice
:解析 AspectJ 中的@AfterReturning
属性来生成的 Advice(PS: 若切面方法抛出异常, 则这里的方法就将不执行) 3.AspectJAfterThrowingAdvice
:解析 AspectJ 中的@AfterThrowing
属性来生成的 Advice(PS: 若切面方法抛出异常, 则这里的方法就执行)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);
}
}
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
- AfterReturningAdviceInterceptor:这个类其实就是将 AfterReturningAdvice 包裹成 MethodInterceptor 的适配类, 而做对应适配工作的就是 AfterReturningAdviceAdapter
- MethodBeforeAdviceInterceptor:这个类其实就是将 MethodBeforeAdvice 包裹成 MethodInterceptor 的适配类, 而做对应适配工作的就是 MethodBeforeAdviceAdapter
- ThrowsAdviceInterceptor:这个类其实就是将 ThrowsAdvice 包裹成 MethodInterceptor 的适配类, 而做对应适配工作的就是 ThrowsAdviceAdapter
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
4、Advised
Advised: 已经被建议的对。
代码语言:javascript复制// 这个 Advised 接口的实现着主要是代理生成的对象与AdvisedSupport (Advised的支持器)
public interface Advised extends TargetClassAware {
// 这个 frozen 决定是否 AdvisedSupport 里面配置的信息是否改变
boolean isFrozen();
// 是否代理指定的类, 而不是一些 Interface
boolean isProxyTargetClass();
// 返回代理的接口
Class<?>[] getProxiedInterfaces();
// 判断这个接口是否是被代理的接口
boolean isInterfaceProxied(Class<?> intf);
// 设置代理的目标对象
void setTargetSource(TargetSource targetSource);
// 获取代理的对象
TargetSource getTargetSource();
// 判断是否需要将 代理的对象暴露到 ThreadLocal中, 而获取对应的代理对象则通过 AopContext 获取
void setExposeProxy(boolean exposeProxy);
// 返回是否应该暴露 代理对象
boolean isExposeProxy();
// 设置 Advisor 是否已经在前面过滤过是否匹配 Pointcut (极少用到)
void setPreFiltered(boolean preFiltered);
// 获取 Advisor 是否已经在前面过滤过是否匹配 Pointcut (极少用到)
boolean isPreFiltered();
// 获取所有的 Advisor
Advisor[] getAdvisors();
// 增加 Advisor 到链表的最后
void addAdvisor(Advisor advisor) throws AopConfigException;
// 在指定位置增加 Advisor
void addAdvisor(int pos, Advisor advisor) throws AopConfigException;
// 删除指定的 Advisor
boolean removeAdvisor(Advisor advisor);
// 删除指定位置的 Advisor
void removeAdvisor(int index) throws AopConfigException;
// 返回 Advisor 所在位置de index
int indexOf(Advisor advisor);
// 将指定的两个 Advisor 进行替换
boolean replaceAdvisor(Advisor a, Advisor b) throws AopConfigException;
// 增加 Advice <- 这个Advice将会包裹成 DefaultPointcutAdvisor
void addAdvice(Advice advice) throws AopConfigException;
// 在指定 index 增加 Advice <- 这个Advice将会包裹成 DefaultPointcutAdvisor
void addAdvice(int pos, Advice advice) throws AopConfigException;
// 删除给定的 Advice
boolean removeAdvice(Advice advice);
// 获取 Advice 的索引位置
int indexOf(Advice advice);
// 将 ProxyConfig 通过 String 形式返回
String toProxyConfigString();
}
它的主要实现,就是面向我们创建代理的,非常实用:
ProxyFactory
: 这个类通过构造函数中的 proxyInterface/interceptor/targetSource 来创建代理对象(这个类是编程式 AOP 中最常用的对象)ProxyFactoryBean
: 这个类是基于 FactoryBean 的 Proxy创建形式, 其通过代理的 Interface, targetSource 与指定的 interceptorNames 来创建对应的AopProxy, 最后生成对应的代理对象AspectJProxyFactory
: 将一个被@Aspect
注解标示的类丢入其中, 变创建了对应的代理对象 (这个类现在已经很少用了。 但是@Aspect
方式常用哦)
5、TargetSource
TargetSource:其实是动态代理作用的对象
- HotSwappableTargetSource: 进行线程安全的热切换到对另外一个对象实施动态代理操作
- AbstractPoolingTargetSource: 每次进行生成动态代理对象时都返回一个新的对象(比如内部实现类CommonsPool2TargetSource就是例子,但它依赖于common-pool2包)
ThreadLocalTargetSource
: 为每个进行请求的线程维护一个对象的 TargetSourceSingletonTargetSource
: 最普遍最基本的单例 TargetSource, 在 Spring 中生成动态代理对象, 一般都是用这个 TargetSource
6、AdvisorChainFactory
这个接口主要定义了从 Advised中获取 Advisor 并判断其是否与 对应的 Method 相匹配, 最终返回的是MethodInterceptor
。(其中对 Advisor 转化成 MethodInterceptor 的工作都是交由 DefaultAdvisorAdapterRegistry 来完成, ) 它的实现类为:DefaultAdvisorChainFactory
,提供的唯一方法是:
// 获取匹配 targetClass 与 method 的所有切面的通知
@Override
public List<Object> getInterceptorsAndDynamicInterceptionAdvice(
Advised config, Method method, Class<?> targetClass) {
// This is somewhat tricky... We have to process introductions first,
// but we need to preserve order in the ultimate list.
List<Object> interceptorList = new ArrayList<Object>(config.getAdvisors().length); // PS: 这里 config.getAdvisors 获取的是 advisors 是数组
Class<?> actualClass = (targetClass != null ? targetClass : method.getDeclaringClass());
boolean hasIntroductions = hasMatchingIntroductions(config, actualClass); // 判断是有 IntroductionAdvisor 匹配到
// 下面这个适配器将通知 [Advice] 包装成拦截器 [MethodInterceptor]; 而 DefaultAdvisorAdapterRegistry则是适配器的默认实现
AdvisorAdapterRegistry registry = GlobalAdvisorAdapterRegistry.getInstance();
for (Advisor advisor : config.getAdvisors()) { // 获取所有的 Advisor
if (advisor instanceof PointcutAdvisor) { // advisor 是 PointcutAdvisor 的子类
// Add it conditionally.
PointcutAdvisor pointcutAdvisor = (PointcutAdvisor) advisor;
// 判断此切面 [advisor] 是否匹配 targetClass (PS: 这里是类级别的匹配)
if (config.isPreFiltered() || pointcutAdvisor.getPointcut().getClassFilter().matches(actualClass)) {
/** 通过对适配器将通知 [Advice] 包装成 MethodInterceptor, 这里为什么是个数组? 因为一个通知类
* 可能同时实现了前置通知[MethodBeforeAdvice], 后置通知[AfterReturingAdvice], 异常通知接口[ThrowsAdvice]
* 环绕通知 [MethodInterceptor], 这里会将每个通知统一包装成 MethodInterceptor
*/
MethodInterceptor[] interceptors = registry.getInterceptors(advisor);
MethodMatcher mm = pointcutAdvisor.getPointcut().getMethodMatcher();
// 是否匹配 targetClass 类的 method 方法 (PS: 这里是方法级别的匹配)
if (MethodMatchers.matches(mm, method, actualClass, hasIntroductions)) {
if (mm.isRuntime()) { // 看了对应的所有实现类, 只有 ControlFlowPointcut 与 AspectJExpressionPointcut 有可能 返回 true
// Creating a new object instance in the getInterceptors() method
// isn't a problem as we normally cache created chains.
// 如果需要在运行时动态拦截方法的执行则创建一个简单的对象封装相关的数据, 它将延时
// 到方法执行的时候验证要不要执行此通知
for (MethodInterceptor interceptor : interceptors) {
interceptorList.add(new InterceptorAndDynamicMethodMatcher(interceptor, mm)); // 里面装的是 Advise 与 MethodMatcher
}
}
else {
interceptorList.addAll(Arrays.asList(interceptors));
}
}
}
}
else if (advisor instanceof IntroductionAdvisor) { // 这里是 IntroductionAdvisor
// 如果是引入切面的话则判断它是否适用于目标类, Spring 中默认的引入切面实现是 DefaultIntroductionAdvisor 类
// 默认的引入通知是 DelegatingIntroductionInterceptor 它实现了 MethodInterceptor 接口s
IntroductionAdvisor ia = (IntroductionAdvisor) advisor;
if (config.isPreFiltered() || ia.getClassFilter().matches(actualClass)) {
Interceptor[] interceptors = registry.getInterceptors(advisor);
interceptorList.addAll(Arrays.asList(interceptors));
}
}
else {
Interceptor[] interceptors = registry.getInterceptors(advisor);
interceptorList.addAll(Arrays.asList(interceptors));
}
}
return interceptorList;
}
7、DefaultAdvisorAdapterRegistry
这里类里面存在着 MethodBeforeAdviceAdapter, AfterReturningAdviceAdapter, ThrowsAdviceAdapter
这三个类是将 Advice适配成MethodInterceptor
的适配类; 而其本身具有两个重要的功能:
1、将 Advice/MethodInterceptor 包裹成 DefaultPointcutAdvisor 2、通过上面的三个适配类将 Advisor 中的 Advice 适配成对应的MethodInterceptor
(PS: 在代理对象执行时, 执行的都是MethodInterceptor, 当然在进行配置时既可以配置 Advice, MethodInterceptor, Advisor均可)
8、AopProxyFactory
这个接口中定义了根据 AdvisedSupport 中配置的信息来生成合适的AopProxy (主要分为 基于Java 动态代理的 JdkDynamicAopProxy 与基于 Cglib 的ObjenesisCglibAopProxy)
9、MethodInvocation
MethodInvocation
是进行 invoke 对应方法 / MethodInterceptor
的类。
其主要分成用于 Proxy 的 ReflectiveMethodInvocation, 与用于 Cglib 的 CglibMethodInvocation
(其实就是就是递归的调用 MethodInterceptor, 当没有 MethodInterceptor可以调用时)
10、AbstractAutoProxyCreator
这个类是声明式 Aop 编程中非常重要的一个角色:自动代理创建器 解读如下:
- AbstractAutoProxyCreator继承了 ProxyProcessorSupport, 所以它具有了ProxyConfig中动态代理应该具有的配置属性
- AbstractAutoProxyCreator 实现了
SmartInstantiationAwareBeanPostProcessor
(包括实例化的前后置函数, 初始化的前后置函数) 并进行了实现 - 实现了 创建代理类的主方法 createProxy 方法
- 定义了抽象方法 getAdvicesAndAdvisorsForBean**(获取 Bean对应的 Advisor)**
AbstractAutoProxyCreator 中等于是构建了创建 Aop 对象的主逻辑**(模版)**,而其子类 AbstractAdvisorAutoProxyCreator 实现了getAdvicesAndAdvisorsForBean 方法, 并且通过工具类 BeanFactoryAdvisorRetrievalHelper(PS: 它的方法findAdvisorBeans中实现类获取容器中所有 Advisor 的方法) 来获取其对应的 Advisor。它的主要子类如下:
AspectJAwareAdvisorAutoProxyCreator
: 通过解析 aop 命名空间的配置信息时生成的 AdvisorAutoProxyCreator, 主要通过ConfigBeanDefinitionParser.parse() -> ConfigBeanDefinitionParser.configureAutoProxyCreator() -> AopNamespaceUtils.registerAspectJAutoProxyCreatorIfNecessary() -> AopNamespaceUtils.registerAspectJAutoProxyCreatorIfNecessary(); 与之对应的 Pointcut 是AspectJExpressionPointcut, Advisor 是 AspectJPointcutAdvisor, Advice 则是 AspectJAfterAdvice, AspectJAfterReturningAdvice, AspectJAfterThrowingAdvice, AspectJAroundAdviceAnnotationAwareAspectJAutoProxyCreator
: 这是基于 @AspectJ注解生成的 切面类的一个 AbstractAutoProxyCreator, 解析额工作交给了 AspectJAutoProxyBeanDefinitionParser, 步骤如下AspectJAutoProxyBeanDefinitionParser.parse() -> AopNamespaceUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary() -> AopConfigUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary()DefaultAdvisorAutoProxyCreator
: 这个类也是 AbstractAutoProxyCreator的子类, 它帅选作用的类时主要是根据其中的 advisorBeanNamePrefix(类名前缀)配置进行判断BeanNameAutoProxyCreator
: 通过类的名字来判断是否作用(正则匹配)
上面这几个类主要是声明式 Aop 编程的主要功能类
总结
本篇主要介绍了 Spring-Aop 中的主要构建组件, 并进行相应的说明, 单纯的讲 aop 这个概念其实不复杂,,但在Spring这个产品中,就显得很复杂了。主要体现在如下方面:
- 涉及的组件多 -> Pointcut, Advice, MethodInterceptor, Advised, Advisor, TargetSource, AopProxyFactory, MethodInvocation, AbstractAutoProxyCreator, 这也造成掌握 Spring-aop 的难度
- 实现aop需要多个基础功能的支持(AspectJ中的注解及注解解析, Cglib, java中的 Proxy, Asm字节码(主要式解析方法中的参数名))
- 结合了 BeanPostProcessor 在Bean的实例化/初始化阶段就进行生成对应的代理类(比如 AspectJAwareAdvisorAutoProxyCreator与AnnotationAwareAspectJAutoProxyCreator)
总体而言,Spring AOP是众多技术的一个综合,若要要彻底了解和运用自如,相关的周边技术能力必不可少。因此我平时书说的,基础不牢,地动山摇
,在这里的学习过程中会有所体会~~~