AOP学习
题外话
- org.springframework.aop.advisor接口 具有一个getAdvice方法,PointcutAdvisor继承了它增加了一个getPointcut方法;
所以要构建一个PointcutAdvisor,就需要具备有advice和pointCut
而一个pointCut具备了getClassFilter和getMethodMatcher方法,匹配类和方法 - 而Interceptor extends Advice,MethodInterceptor extends Interceptor
- MethodInterceptor 有一个invoke(MethodInvocation invocation)
- AspectJMethodBeforeAdvice,AspectJAfterReturningAdvice这两个不是interceptor仅仅是advice
- 所以advice有可能是一个interceptor,也有可能不是,如果不是interceptor,又要转成它,就要有适配器AdvisorAdapter能getInterceptor
-
事务
.事务registerAutoProxyCreator的是InfrastructureAdvisorAutoProxyCreator,跟切面类AOP同样的继承线路
所以你看事务中BeanFactoryTransactionAttributeSourceAdvisor是一个PointcutAdvisor,就需要有pointcut和advice,- pointcut是用来匹配方法MethodMatcher和类ClassFilter,所以就有了TransactionAttributeSourcePointcut里面有TransactionAttributeSourceClassFilter,TransactionAttributeSource(分注解匹配和名字匹配两种)就用来MethodMatcher的
- advice可以是一个MethodInterceptor,这边用TransactionInterceptor进行触发invoke进入事务的处理
-
@EnableCaching
缓存这边也是注入InfrastructureAdvisorAutoProxyCreator.
然后跟事务很相似,BeanFactoryCacheOperationSourceAdvisor是一个PointcutAdvisor- pointcut是CacheOperationSourcePointcut,引入CacheOperationSourceClassFilter,而matches就通过CacheOperationSource实现
- advice就通过CacheInterceptor实现了
-
@Validated
参数验证器走的跟事务不是一个继承体系,它走的是AbstractAdvisingBeanPostProcessor;
这个类有一个advisor变量,凡是继承这个类,都可以提供一个advisor出来,
然后在IOC的postProcessAfterInitialization阶段对bean进行判断,
如果当前存在有advisor,那就判断advisor是否匹配beanClass,用的还是AOP那套AopUtils.canApply,解析beanClass的类所有方法进行使用pointcut进行匹配;
能够匹配上就可以为当前者beanClass创建代理,使用advisor;
SpringAop到了proxyFactory这一步都是一样的,在这之前,切面类那套是先收集advisors,然后转统一的advisor,因为切面类收集的advisor中的advice并不都是一个interceptor,所以有了转换advisor一层处理,还有advisorAdapter,
而AbstractAdvisingBeanPostProcessor,则是直接只创建了一个advisor,然后此时大家后续都是对这个走proxyFactory创建- MethodValidationPostProcessor在afterPropertiesSet中初始化了DefaultPointcutAdvisor,pointCut用的是AnnotationMatchingPointcut,那classFiter用的AnnotationClassFilter,methodMatcher是全部匹配MethodMatcher.TRUE,处理@Validated注解的
- advice就是MethodValidationInterceptor了
AOP概括:
初始化时机:
springAOP是通过注解EnableAspectJAutoProxy生效的,这个注解会注入一个AnnotationAwareAspectJAutoProxyCreator beanDefination,从继承结构发现它是一个后置处理器且实现了order,那么会早以一般的后置处理器先实例化,在IOC refresh的registerBeanPostProcessors 步骤触发实例化。
在doCreatebean之前会触发InstantiationAwareBeanPostProcessor#postProcessBeforeInstantiation后置处理介入是否产生代理,但是不是所有的bean都会进行创建proxy,比如如果在advisedBeans存在的则不会创建proxy,基础的组件isInfrastructureClass(Advice 、PointCut 、Advisor 、AopInfrastructureBean)或者AspectJPointcutAdvisor类型的切面类不创建代理,所以这边会首次进行收集advisor进行判断是否是切面类;那如何收集呢?
这advisor包括原生的advisor和切面类的advisor的解析:
原生的advisor从beanfactory里面直接获取后,进行初始化;
切面类的advisor进行解析切面类,进行获取beanfactory里面所有的beanNames,然后先进行beanname的合法校验过滤,这边可以进行优化,提高效率,然后开始解析bean是否有aspect注解,有的话则开始构建元数据,因为aspect注解里可以配置single,perthis,pertarget
如果是单例会委托给BeanFactoryAspectInstanceFactory去解析切面类,怎么解析呢,
先获取切面类里面非pointcut的方法,然后进行排序(按照5种通知类型排序)
然后检测方法上有哪些通知类型注解,没有则return,有则进行解析封装为advisor,,同时里面会构建相应五种advice
解析完方法还会解析declareparent属性的注解,也是封装advisor,单例会进行缓存这些advisors增强器,
而非单例的解析是不会缓存advisors增强器,每次都是通过PrototypeAspectInstanceFactory解析切面类,不过两者都会缓存解析的factory
缓存了advisors后,在进行初始化之后AbstractAutoProxyCreator#postProcessAfterInitialization会触发代理的创建,
还有AbstractAdvisingBeanPostProcessor#postProcessAfterInitialization(这个是另外的继承体系,MethodValidationPostProcessor,AsyncAnnotationBeanPostProcessor)
如果之前在postProcessBeforeInstantiation有收集过advisor,直接从缓存中获取,判断是否有必要进行创建代理;
需要创建代理的时候,从缓存的advisor中与当前的beanClass进行匹配得出候选的advisor,候选步骤:
advisor匹配分两种,一种引介增强IntroductionAdvisor,一种常规增强PointcutAdvisor;
IntroductionAdvisor的匹配直接通过classFilter进行matches
PointcutAdvisor的匹配则是先通过classFilter匹配,如果通过则继续用pointCut的MethodMatcher进行匹配,这边的matcher分两种,
一种是introductionAwareMethodMatcher,切入点表达式的匹配,
另种则是通过常规注解,比如spring cache,注解的事务匹配,
获取到beanClass所有方法,循环它们,只要匹配到一个方法立即返回;
获取到所有候选的advisor后,如果有AspectJPointcutAdvisor,则会添加一个ExposeInvocationInterceptor;
然后进行所有advisor的排序,排序一种是写注解order,一种注解本身里面有order属性,都是从低到高的排序;同个切面类增强顺序是一致的,然后同个切面类多个相同的增强则是通过字母顺序排的
有了匹配的advisor,开始创建代理,代理类型的选择:
先判断是否是强制cglib(isProxyTargetClass=true),如果没有(isProxyTargetClass=false),则继续判断当前bean的beanDefination里面是否有代理属性preserveTargetClass=true,如果还是么有,则开始判断是否配置了回调接口(InitializingBean,DisposableBean,Closeable,AutoCloseable,Aware),存在则设置proxyTarget=true;
如果都是正常的接口,则不设置ProxyTargetClass,否则设置为true;
封装advisor
先获取通用的commonInterceptors,添加到之前缓存好的advisor里面
然后对这些全部的advisor进行迭代,把这些advisor进行封装为DefaultPointcutAdvisor,因为这里面有包括了本来就是advisor,还有MethodInterceptor类型,MethodBeforeAdvice,AfterReturningAdvice,ThrowsAdvice,这三种advice需要进行适配,然后转为DefaultPointcutAdvisor;
创建具体代理模式:
如果isProxyTargetClass=false,则创建JdkDynamicAopProxy,否则,判断targetClass如果是接口,则也为JdkDynamicAopProxy,然后获取对应的proxy;
jdk通过Proxy.newProxyInstance代理接口生成proxy;
如果是cglib,则进行创建回调的DynamicAdvisedInterceptor,设置到Enhancer里面
JDK是通过JdkDynamicAopProxy#invoke方法进行触发的,
为当前的method,进行匹配advisor;这边也是分IntroductionAdvisor,PointcutAdvisor进行匹配的,引介是通过getClassFilter,PointcutAdvisor则通过MethodMatcher,匹配到advisor后,如果advisor中的advice是MethodInterceptor类型不用转换,如果是比如AfterReturningAdvice,MethodBeforeAdvice,ThrowsAdvice,需要经过适配器获取对应的getInterceptor,组装成链
然后通过索引下标-1开始迭代这个链
时机触发:
事务生效有两种,一种是通过注解enableTransaction开启,一种是通过切点表达式配置
注解的方式开启会注入一个InfrastructureAdvisorAutoProxyCreator(如果已经存在了AnnotationAwareAspectJAutoProxyCreator,则不会注入InfrastructureAdvisorAutoProxyCreator),这里面只是多一个判断isEligibleAdvisorBean类型实现方法,判断role注解,
同时还导入一个ProxyTransactionManagementConfiguration,配置则是注入了BeanFactoryTransactionAttributeSourceAdvisor(这个就是原生的advisor),advisor由advice和pointcut构成的,所以事务中的advice就是TransactionInterceptor充当,而注解方式的pointcut就是TransactionAttributeSourcePointcut来充当,methodMatch通过AnnotationTransactionAttributeSource充当,而attributeSource又是委托给 SpringTransactionAnnotationParser进行解析。
所以配置又会注入了attributesource和interceptor,interceptor依赖tm和attributesource
attributesource就是defination,定义事务的基础属性,隔离级别,时间,事务管理器,传播行为,异常等等
所有切点配置引入方式按照注解方式一样,也是构造interceptor即advice,
然后依赖tm和atrributesource, attributesource就可以配置正则表达式匹配相应需要事务支持的方法
执行过程
:
获取attributeSource,Tm :
事务入口通过interceptor进入的,然后通过attributesource获取attribute,这样具备了事务的基本属性,隔离级别,时间传播行为等等
然后从beanfactory里面获取tm
创建新事务:
如果tm存在,则先从tm里面获取到ConnectionHolder,它里面有个transactionActive判断当前线程事务是否存在的
如果不存在事务,则根据传播行为是否开启新事务:
开始判断传播行为不能是PROPAGATION_MANDATORY,这是要求必须有事务,否则抛出异常
PROPAGATION_REQUIRED,PROPAGATION_REQUIRES_NEW,PROPAGATION_NESTED这三个都需要开启新事务
先获取数据库连接,然后关闭自动提交,设置连接的隔离级别,超时时间。就可以激活事务状态transactionActive=true
将链接绑定到当前线程中;
如果是已经存在事务了:
根据隔离级别PROPAGATION_NOT_SUPPORTED,PROPAGATION_REQUIRES_NEW,这两个是先挂起事务
PROPAGATION_NEVER这个是抛出异常,表示当前不能有事务存在
PROPAGATION_SUPPORTS or PROPAGATION_REQUIRED. 这两个则是沿用当前事务,但是要保持两个事务隔离级别和只读事务一致
PROPAGATION_NESTED,如果支持保存点,则创建保存点,否则开启新事务
执行业务逻辑invocation.proceedWithInvocation();
如果抛异常,则根据异常类型是否匹配进行回滚,如果没匹配则进行提交,在提交里面还会判断是否需要回滚
最后进行commit;
-
术语
- joinpoint 连接点
目标对象的所属类中,定义的所有方法 - Pointcut:切入点
那些被拦截 / 被增强的连接点,从连接点中进行选择某些点即选某些方法作为切入点 - Advice:通知
增强的逻辑,对切入点进行增强的逻辑
通知还有分类型:
Before 前置通知, After 后置通知 ,AfterReturning 返回通知, AfterThrowing 异常通知
Around 环绕通知 - Aspect:切面
Aspect 切面 = PointCut 切入点 + Advice 通知 - Weaving:织入
将 Advice 通知应用到 Target 目标对象,进而生成 Proxy 代理对象的过程 - Introduction:引介
- 在不修改原有类的代码的前提下,在运行期为原始类动态添加新的属性 / 方法
- joinpoint 连接点
-
关键图
-
AOP底层原理
- @EnableAspectJAutoProxy 注解有啥作用
这个是启动AOP功能的注解,从enable开头可以看出,里面肯定有导入类AspectJAutoProxyRegistrar, 这个类是处理BeanDefinitionsd的注入,里面注入了一个AnnotationAwareAspectJAutoProxyCreator 的BeanDefinitions ,然后注入后,接着去判断这个注解里面的属性,然后放入到这个BeanDefinitions的propertyValues里面- AnnotationAwareAspectJAutoProxyCreator 类初始化时机
这个是BeanPostProcessor,看继承图,它还实现排序接口Ordered,那意味它优先普通的后置处理器先初始化,在IOC中的refresh-registerBeanPostProcessors 方法里面先初始化实现PriorityOrdered的BeanPostProcessor,再初始化实现Ordered的BeanPostProcessor,其中通过beanFactory.getBean进行初始化的
- AnnotationAwareAspectJAutoProxyCreator 类初始化时机
- 增强器的构建
- AbstractAutoProxyCreator #postProcessBeforeInstantiation (InstantiationAwareBeanPostProcessor)做了啥事情
- 触发时机
这个方法是在doGetBean-createBean-InstantiationAwareBeanPostProcessor#postProcessBeforeInstantiation - doCreateBean,在对象实例化之前,AOP在这边进行触发 - 做了哪些事情
1.标记过的beanclass不能被增强
:如果当前bean被增强过,则不会再次增强:通过判断是否存在advisedBeans集合中
2哪些类或者接口不能被增强
:如果么有被增强过,则判断是否是基础类型Advice,Pointcut,Advisor,AopInfrastructureBean这些接口实现类,这些是基础类型InfrastructureClass,不能被增强;同时如果是切面类且!compiledByAjc,也是不能被增强的
3.哪些beanName需要被跳过
:接着判断是否需要被跳过的bean(shouldSkip), shouldSkip方法(这个方法被AspectJAwareAdvisorAutoProxyCreator类重写了)会处理如下事情
- 通过findCandidateAdvisors(这个被AnnotationAwareAspectJAutoProxyCreator 重写了)找到所有候选的增强器,循环他们,如果当前的增强器是AspectJPointcutAdvisor类型且当前beanname与这个增强器的切面名称一样,则跳过不进行增强
- 如果上述循环后没找到,则调用父类判断当前beanname是否是ORIGINAL结尾的,是则不进行增强
- 触发时机
- 如果存在getCustomTargetSource,则会进行创建代理对象,结束IOC的流程
如何找到候选的增强器
findCandidateAdvisors (AnnotationAwareAspectJAutoProxyCreator重写)逻辑是啥
增强器advisor,就是pointcut+方法体,这些东西就是存在切面类中,所以肯定会去解析所有的切面类,解析过的就不会再解析了
- AbstractAutoProxyCreator #postProcessBeforeInstantiation (InstantiationAwareBeanPostProcessor)做了啥事情
- 原生的AdvisorBeans收集:先获取spring原生的advisors, 调用父类的逻辑super.findCandidateAdvisors()
父类的增强器构建委托给了BeanFactoryAdvisorRetrievalHelperAdapter类,这个原生在事务的AOP里面ProxyTransactionManagementConfiguration配置类有注入一个BeanFactoryTransactionAttributeSourceAdvisor,在这边就会获取到它- 找出beanFactory中实现了Advisor接口的beanName数组,要是发现没有则直接返回;这边缓存了这个cachedAdvisorBeanNames,下次如果有值,则直接使用了,不用重新获取
- 如果找到了advisorNames,则依次循环他们,同时先判断是否是合法的beanname(isEligibleBean#AbstractAdvisorAutoProxyCreator,这个可以进行覆写), 然后判断是否是当前正在创建的bean,最后则通过beanFactory.getBean(name, Advisor.class)初始化这个advisor
- 切面类的advisor收集:原生的advisor初始化后,则接着收集切面类中的advisor,那肯定要先找出切面类
- 委托BeanFactoryAspectJAdvisorsBuilderAdapter进行构建收集,这个类除了使用到beanfactory,还会使用到一个ReflectiveAspectJAdvisorFactory(这里面有5种那个通知类型,你懂得)
- 收集所有的beanNames:获取beanfactory中所有的beanname,是所有,然后判断isEligibleBean(这个被BeanFactoryAspectJAdvisorsBuilderAdapter重写了,底层还是调用了AnnotationAwareAspectJAutoProxyCreator类里面的isEligibleAspectBean)是否合格的bean,以目前测试来看,是都符合的,他里面可以设置过滤模式includePatterns(正则表达式),进行结合判断是否是个合格的bean,不符合继续循环
- 判断是否有对应的classType:合格后,则通过beanFactory再获取这个bean的类型,这边都么有进行任何初始化,如果么有获取到类型,则continue
- 切面类判断:通过判断当前beanType(上面从beanfactory里面获取的,根据beanName)使用了注解Aspect,以及不是被ajc编译过(就是属性不包含ajc$,被AspectJ 编译过的,是含有相应的痕迹,它们是不能被spring aop利用的),那就是切面类了
- 构建AspectMetadata:根据beanType和beanName构建出AspectMetadata,这里面做很多事情
- 再次判断是否是切面类
- 根据注解@Aspect里面的属性value,进行构造PerClauseKind,如果么有值默认是SINGLETON,有值的话,则根据值,构建比如PERTHIS,PERTARGET等等;
- 如果是SINGLETON,perClausePointcut为TruePointcut,否则为AspectJExpressionPointcut或者ComposablePointcut
- 分单例非单例处理增强器
- 单例bean的切面
构建BeanFactoryAspectInstanceFactory(这个里面又会构建一个aspectMetadata),传入给ReflectiveAspectJAdvisorFactory,通过它去收集advisor,收集后,如果是单例,则放入advisorsCache,否则不缓存,只缓存aspectFactoryCache,就是BeanFactoryAspectInstanceFactory,进行获取增强器getAdvisors,那如何获取呢?
1. 备选advisor方法构建:根据aspectMetadata进行获取到当前的class,然后通过反射获取类中所有方法,过滤出不含包注解Pointcut方法,然后排序,排序的时候,如果方法上面有通知类型的注解,则按照这个顺序的Around.class, Before.class, After.class, AfterReturning.class, AfterThrowing.class),如果有相同的多个通知类型,继续按照方法名称排序,如果么有通知类型的方法,则也是按照方法名称排序
,作为备选的advisorMethod
2. pointCut构建:构建AspectJExpressionPointcut,先看方法上是否有那6种注解,before,after等等,有则可以构建,没有则continue,
3. AdvisorImpl封装,通知类型初始化:有了切入点,有了方法体,那就可以构建增强器了InstantiationModelAwarePointcutAdvisorImpl;同时这边还初始化了具体通知类型,就是那5种注解,比如AspectJAroundAdvice,AspectJMethodBeforeAdvice,AspectJAfterAdvice,AspectJAfterReturningAdvice,AspectJAfterThrowingAdvice等等,以及方法参数的绑定
4. 属性注解@DeclareParents增强器器收集:方法的增强器构建结束后,会开始处理切面类中属性的增强器收集,构建DeclareParentsAdvisor,这个目前没接触,后面在看看 ? ? 在这个类处理ReflectiveAspectJAdvisorFactory
5. 缓存advisors:增强器收集后,如果当前bean是单例,则将增强器classAdvisors放入advisorsCache,下次直接从缓存获取而不再重新解析;否则缓存BeanFactoryAspectInstanceFactory进入aspectFactoryCache - 非单例
非单例构建的是PrototypeAspectInstanceFactory,只是不会缓存收集好的classAdvisors,下次进来要重新解析当前切面类收集增强器,但是不会在重新构建MetadataAwareAspectInstanceFactory,走缓存获取,所以同样会缓存PrototypeAspectInstanceFactory进入aspectFactoryCache
- 单例bean的切面
-
Bean如何被AOP代理
-
AbstractAutoProxyCreator #postProcessAfterInitialization 初始化时机
- 上面说过AnnotationAwareAspectJAutoProxyCreator 是个后置处理器,在doCreateBean的最后阶段即初始化阶段,createBeanInstance—applyMergedBeanDefinitionPostProcessors—populateBean—initializeBean(这里面的最后一个阶段applyBeanPostProcessorsAfterInitialization)进行了AOP的逻辑处理,调用AbstractAutoProxyCreator#wrapIfNecessary
-
wrapIfNecessary 做了啥
- 判断是否要被增强的bean
如果targetSourcedBeans存在,则不进行处理;
如果在上面阶段被标记为跳过的bean,不进行处理(放在了advisedBeans Map 里面为false );
同时这边也会进行基础类型的判断isInfrastructureClass和是否跳过shouldSkip(这里面有advisors的收集,收集只是为了判断advisors的切面类名getAspectName是否与当前的beanName相同,相同则跳过
),因为有可能上个阶段不是必须经历的,那在这边要有相同的处理 - 为当前的bean进行匹配增强器
- 获取增强器:获取收集好的候选增强器candidateAdvisors
- 匹配当前class的增强器:为当前的beanClass进行应用合适的增强器findAdvisorsThatCanApply
- 引介增强器匹配:循环增强器列表,先匹配引介增强IntroductionAdvisor(前面有收集引介增强的增强器,这边才有的判断),如果是,则调用它的ClassFilter进行matches当前的class
- 常规方法增强器匹配:接着判断PointcutAdvisor,之前我们构建的是InstantiationModelAwarePointcutAdvisorImpl,然后pointCut是AspectJExpressionPointcut,
1. 所以获取pointCut的getClassFilter进行判断是否matches当前的class
2. 过了1,则继续获取MethodMatcher,进行判断是匹配任何方法的TrueMethodMatcher
3. 如果不是proxyClass,这边会进行判断className是否包含了$$,如果包含了则获取他的父类,不包含直接返回class,如果当前的class有接口,则获取他的接口类
4. 然后针对收集到的class进行反射所有的方法,然后调用pointcut的matches进行匹配,至于如何匹配,太复杂了,看不懂,留着???
- 添加默认DefaultPointcutAdvisor以及排序advisors:收集完匹配的增强器eligibleAdvisors后, 如果增强器列表中有一个增强器是isAspectJAdvice类型,则就在列表首位置增加一个DefaultPointcutAdvisor,传入一个ExposeInvocationInterceptor(类里面有个threadLocal,用于保存当前的MethodInvocation,便于调用过程其他线程可以获取)进行构造,然后sortAdvisors下增强器列表,一个切面类之间是根据advice通知类型排序,如果多个切面类则是通过@order先确定切面类的顺序;在这说明这个类要被增强,放入到advisedBeans标记下为true
- 开始创建代理createProxy
- 代理模式判断:ProxyFactory构建,判断是否需要强制使用CGlib,如果之前EnableAspectJAutoProxy里面有设置proxyTargetClass为true,就不用进入判断使用哪种代理模式,
所以可以看出是配置优先于接口代理
,否则进入判断,如果当前的bean的beanDefination有这个preserveTargetClass属性为true,则代理;否则再进行接口的判断,接口实现需要排除配置接口的判断,如InitializingBean,DisposableBean,Closeable,AutoCloseable,Aware以及内部语言接口名称groovy.lang.GroovyObject等等,这些接口不能算,如果排除了接口还有正常接口,收集它们进入interfaces,同时不设置setProxyTargetClass为True,否则设置它为true,这个如果是true创建代理会直接创建cglibProxy - 构建advisor: 先获取commonInterceptors,可以进行设置,如果有的话,则加入到前面已经收集好的advisor
- 封装为真的Advisor,放入ProxyFactory: 通过DefaultAdvisorAdapterRegistry将所有的advisor转换为advisor,因为前面收集好的advsior有很多种类型,如果是真的Advisor类型(比如事务的BeanFactoryTransactionAttributeSourceAdvisor)直接返回继续下一个,MethodInterceptor类型则封装为DefaultPointcutAdvisor,如果是MethodBeforeAdvice,AfterReturningAdvice,ThrowsAdvice则也封装为DefaultPointcutAdvisor,所有全部都封装为真的advisor类型,放入ProxyFactory
- 创建具体代理aopProxy: 通过DefaultAopProxyFactory进行创建代理,config.isOptimize() || config.isProxyTargetClass() || hasNoUserSuppliedProxyInterfaces(config),满足这些的前提下(不满足是JDK代理),此时如果
targetClass是个接口或者是个代理类则创建JdkDynamicAopProxy
,否则使用bjenesisCglibAopProxy - 获取代理getProxy:
- JdkDynamicAopProxy
- 获取完整的代理接口集合:除了上面收集的interfaces,这边判断是否已经收集了SpringProxy,Advised,DecoratingProxy接口,如果没有,则进行添加
- 找出接口中有定义了hashcode和equal方法,findDefinedEqualsAndHashCodeMethods,进行标记,等会在执行代理的时候会进行判断是否是调用了这个两个方法
- 实例化:Proxy.newProxyInstance(classLoader, proxiedInterfaces, this),传入了InvocationHandler,jdkproxy本身是实现了InvocationHandler接口,最终是cons.newInstance(new Object[]{h}),通过InvocationHandler作为参数
- CglibAopProxy
- 如果getTargetClass的beanName包含了$$,则需要获取他的superClass,用于enhancer的设置父类
- createEnhancer
- 设置接口,这步与jdkproxy一样
- 组装回调数组callbacks,这个数组就是执行实际方法时会进行拦截的,比如equal,hascode这边有单独的Interceptor,就不会执行DynamicAdvisedInterceptor;设置setCallbackTypes回调类型
- 实例化,先enhancer.createClass(),然后实例化,最后设置callbacks
- JdkDynamicAopProxy
- 代理模式判断:ProxyFactory构建,判断是否需要强制使用CGlib,如果之前EnableAspectJAutoProxy里面有设置proxyTargetClass为true,就不用进入判断使用哪种代理模式,
-
-
Bean被AOP代理后如何执行
- CglibProxy 不代理private方法VisibilityPredicate#evaluate
- cglib的代理是由DynamicAdvisedInterceptor进行进入的,这个是在获取代理的时候设置的callBack放入的
- 先判断exposeProxy的设置,如果设置了为true, 则将会将当前代理对象放入aop的上下文即AopContext.setCurrentProxy(proxy)
- 获取增强器链getInterceptorsAndDynamicInterceptionAdvice
- 先从缓存methodCache获取当前method的缓存,有缓存,直接返回之前构造好的增强器链
- 没有缓存,先创建DefaultAdvisorAdapterRegistry,这个适配器有默认的三种MethodBeforeAdviceAdapter,AfterReturningAdviceAdapter,ThrowsAdviceAdapter,然后获取之前收集好的advisors,循环它们,里面分PointcutAdvisor,IntroductionAdvisor以及其他进行判断
- PointcutAdvisor
- 通过advisor的getPointcut,获取他的getClassFilter进行匹配当前的actualClass,看下切入点表达式是否匹配
- 如果class符合,则获取pointCut的getMethodMatcher进行匹配当前的方法,这边有分IntroductionAwareMethodMatcher引介匹配
- 方法匹配上后,获取MethodInterceptor[],通过DefaultAdvisorAdapterRegistry获取当前的advisor的advice,然后判断这个advice是真的MethodInterceptor子类还是具体的通知,如果是具体的通知,则通过适配器获取对应的AdviceInterceptor,比如AfterReturningAdviceInterceptor,MethodBeforeAdviceInterceptor,ThrowsAdviceInterceptor;如果直接是MethodInterceptor,则直接添加
- 如果是动态的匹配器MethodMatcher.isRuntime(),则继续封装为InterceptorAndDynamicMethodMatcher
- IntroductionAdvisor
- 也是先获取IntroductionAdvisor的classFilter匹配当前的actualClass
- 然后就开始直接获取advisor的getInterceptors了
- 前两种都不是
- 直接执行 registry.getInterceptors(advisor);
- PointcutAdvisor
- 如果没有获取到增强器链且方法是public ,则直接反射methodProxy.invoke(target, argsToUse)获取到值
- 如果4不满足,则开始创建CglibMethodInvocation,继承了ReflectiveMethodInvocation,这里面会初始化methodProxy,前提是(Modifier.isPublic(method.getModifiers()) &&
method.getDeclaringClass() != Object.class && !AopUtils.isEqualsMethod(method) &&
!AopUtils.isHashCodeMethod(method) && !AopUtils.isToStringMethod(method);然后调用CglibMethodInvocation的proceed - 执行proceed,调用是super的即ReflectiveMethodInvocation的proceed方法,这个方法里面利用一个下标初始值为-1控制获取增强器链来执行相应的通知方法,如果获取到的interceptorOrInterceptionAdvice是InterceptorAndDynamicMethodMatcher则会进行走它的逻辑,否则就走具体的interceptorOrInterceptionAdvice的invoke方法逻辑
- 首先先调用ExposeInvocationInterceptor进行保存当前代理对象上下文
- 然后要是有环绕通知,则先调用AspectJAroundAdvice#invoke,这里面先处理参数的绑定argBinding,里面有个argumentBindings,不知道什么阶段设置的
??
;环绕通知会传入一个MethodInvocationProceedingJoinPoint参数,然后调用在业务方法里面继续调用joinpoint的process方法,这里面会进行ReflectiveMethodInvocation的clone复制一份,然后才调用process,又回去了继续调用链的获取 - 接着就到MethodBeforeAdviceInterceptor#invoke,然后先调用AspectJMethodBeforeAdvice#before,后面执行逻辑就相同了处理参数绑定然后通过反射调用业务的before方法;调用后再继续调用MethodInvocation.proceed方法
- 接着就到AspectJAfterAdvice#invoke,这个方法是先调用MethodInvocation#proceed方法直接回到了增强器链那边去调用下一个增强器链afterReturning了,
然后在finally里面才发起业务逻辑@after注解通知方法的调用,所以@after不管如何都会执行的而且是最后执行
- 到了AfterReturningAdviceInterceptor#invoke方法,也先是调用了MethodInvocation#proceed方法,也是直接到增强链那边进行下一个 增强链的判断;然后执行完才会调用业务逻辑@afterReturn注解通知方法的调用
- 最后到了AspectJAfterThrowingAdvice#invoke,也是直接调用MethodInvocation#proceed方法,回到增强器链,如果执行过程有异常,判断异常类型是否符合@AfterThrowing方法定义,如果符合则调用@AfterThrowing业务逻辑方法,最终还是会抛出异常;到这边就全部 调用链结束了,
- 然后发起真正的业务方法调用,调用结束后,此时倒退到AfterReturningAdviceInterceptor中,执行切面类中@afterReturn注解通知方法的调用;然后再继续回到AspectJAfterAdvice中,执行它的finally方法即@after注解通知方法调用;接着在回到了环绕通知里面,进行环绕通知里面的剩余的业务,最后退回ExposeInvocationInterceptor到方法,回到DynamicAdvisedInterceptor的intercept方法里面
- CglibProxy 不代理private方法VisibilityPredicate#evaluate
-
jdkProxy
- jdkproxy先判断equal,hashcode这个两个方法则不走增强链,如果是method.getDeclaringClass()是DecoratingProxy,Advised这两个类,也不走增强链
- 剩下的就跟cglib一样的逻辑了,先判断advised.exposeProxy,然后获取getInterceptorsAndDynamicInterceptionAdvice增强器链,如果为空直接反射调用,不为空构造ReflectiveMethodInvocation进行proceed调用
- @EnableAspectJAutoProxy 注解有啥作用
@Import(AspectJAutoProxyRegistrar.class)
public @interface EnableAspectJAutoProxy {
boolean proxyTargetClass() default false;
boolean exposeProxy() default false;
}
-------------
AspectJAutoProxyRegistrar 代码:
@Override
public void registerBeanDefinitions(
AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
AopConfigUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(registry);
AnnotationAttributes enableAspectJAutoProxy =
AnnotationConfigUtils.attributesFor(importingClassMetadata, EnableAspectJAutoProxy.class);
if (enableAspectJAutoProxy != null) {
if (enableAspectJAutoProxy.getBoolean("proxyTargetClass")) {
AopConfigUtils.forceAutoProxyCreatorToUseClassProxying(registry);
}
if (enableAspectJAutoProxy.getBoolean("exposeProxy")) {
AopConfigUtils.forceAutoProxyCreatorToExposeProxy(registry);
}
}
}
-
事务传播行为
- REQUIRED 你没有,我开启新事务;你有了,我加入。
- REQUIRES_NEW 你没有,我开启新事务;你有挂起,我开启新的,结束后释放你的
- SUPPORTS 你有事务我就加入,没有就没有
- NOT_SUPPORTED 你有事务,把你挂起
- MANDATORY 你必须有事务,没事务我不干活,抛出异常
- NEVER 你必须没有事务,如果你有,则我抛出异常
- NESTED 没事务,开启新事务,有事务,则判断是否支持保存点,支持则记录保存点,然后子事务出现异常回滚部分即上个保存点(基于同个数据源,如果是分布式,则不适合),不支持保存点会开启新事务
-
事务核心关键类
-
TransactionDefinition
事务明细定义,比如事务隔离级别,事务超时时间,读写事务,事务传播行为
- TransactionAttribute 定义了事务捕捉异常
- TransactionTemplate 是个definition,它的构建是需要PlatformTransactionManager
-
TransactionStatus 事务状态
-
PlatformTransactionManager
-
-
事务原理
- 注解 @EnableTransactionManagement
proxyTargetClass 看了上面的AOP,这个就很熟悉
mode有两个值PROXY 代表的确实是运行期增强,但 ASPECTJ 代表的是类加载期增强
order 事务通知的执行顺序,在advisor会进行设置
-
AutoProxyRegistrar 这个一看就是注入一个BeanDefinition,这个里面主要做了解析注解中的mode和proxyTargetClass属性,然后注入InfrastructureAdvisorAutoProxyCreator,上面的AOP注入的是AnnotationAwareAspectJAutoProxyCreator,如果存在了这个就不会注入事务的InfrastructureAdvisorAutoProxyCreator。
-
InfrastructureAdvisorAutoProxyCreator它是用来筛选基础的adivsor,看下面的isEligibleAdvisorBean方法
-
导入一个ProxyTransactionManagementConfiguration,这里面注入所有bean都是ROLE_INFRASTRUCTURE
- 注入TransactionAttributeSource接口实现类AnnotationTransactionAttributeSource,里面有个通过method和targetClass获取TransactionAttribute即TransactionDefination方法;同时这个实现类,里面会添加三个,其中有个解析类SpringTransactionAnnotationParser会去解析
@Transactional
- 注入TransactionInterceptor的方法拦截器bean,同时依赖了transactionAttributeSource
- 注入BeanFactoryTransactionAttributeSourceAdvisor 增强器依赖transactionAttributeSource和TransactionInterceptor,advisor里面需要有advice和pointcut(pointcut又是需要通过attributeSource去解决的,找到有注解的方法啊);同时这边会设置order,从注解里面的order属性获取值,设置的到advisor里面;而AOP里面的advisor从切面类上的@order注解进行设置,是通过BeanFactoryAspectInstanceFactory里面的getOrder进行处理的
TransactionInterceptor就是一个advice;
pointcut在advisor内部声明为TransactionAttributeSourcePointcut;
切入点的判断是否有写注解Transactional,那肯定是交给了TransactionAttributeSource处理
- 注入TransactionAttributeSource接口实现类AnnotationTransactionAttributeSource,里面有个通过method和targetClass获取TransactionAttribute即TransactionDefination方法;同时这个实现类,里面会添加三个,其中有个解析类SpringTransactionAnnotationParser会去解析
-
- 注解 @EnableTransactionManagement
-
事务Interceptor 执行
- TransactionInterceptor#invoke
ProxyTransactionManagementConfiguration类里面有注入了一个TransactionInterceptor类同时它依赖transactionAttributeSource,既然是interceptor那么在调用业务方法之前就会触发到invoke方法,开始做如下事情:-
AttributeSource获取:先获取到TransactionAttributeSource,这个在configuaration时候就注入了,直接获取
-
TransactionAttribute封装,它是definition:通过TransactionAttributeSource获取当前方法上的事务定义TransactionAttribute 类似advice,
先判断方法是public;默认是public方法才具有事务语义
通过annotationParsers解析器去解析当前的method,这边使用SpringTransactionAnnotationParser获取到这个方法上面的注解Transactional,
然后将注解里面的属性封装TransactionAttribute(RuleBasedTransactionAttribute类)对象,然后就return,
如果方法上面没有,则继续解析类上是否有,然后有放入缓存,没有也放入缓存null值,方法优先级高于类
这里面有缓存机制,同时这个方法在wrapIfNecessary方法执行为当前bean找到候选的advisor会触发,因为判断是否是候选,要获取当前类中所有方法,然后判断方法是否匹配,事务就是通过TransactionAttributeSourcePointcut中的methodMathcher匹配,匹配的时候通过TransactionAttributeSource去实现了,所以这边是一开始就缓存了所有类的方法的attributieSource了,执行interceptor只要获取缓存里面即可 -
txAttr辅助获取tm,因为注解里面有属性可以当下指定tm:获取TransactionManager,里面有很多种获取,其中一种就是从beanFactory.getBean(TransactionManager.class)获取,这就是configuration里面定义的,还有一种通过@Transaction注解里面的value,去beanfactory里面找
-
如果是ReactiveTransactionManager 响应式的,这边处理它的逻辑然后直接返回了
-
封装tm,txAttr,txStatus,构建TransactionInfo:如果非CallbackPreferringPlatformTransactionManager,开始构建TransactionInfo,这对象包含了tm,txAttr,txStatus,所以这边还少了atrributeStatus
5.1 TransactionStatus封装
5.1.1. 先获取transaction:tm.getTransaction(txAttr)
tm.doGetTransaction:直接构造DataSourceTransactionObject对象作为Transaction,以dataSource对象作为key,从当前线程中获取一个Map,然后通过key去获取ConnectionHolder,然后设置setConnectionHolder;这边根据当前的tm进行获取,这边要设置是否支持保存点
5.1.2. 判断是否已经存在的事务: 当前的DataSourceTransactionObject是否有connetionHolder存在以及它是否被激活transactionActive,则走已经存在的事务逻辑处理1. 如果是PROPAGATION_NOT_SUPPORTED,不支持事务,如果存在事务则挂起,怎么挂起呢, 就是在tm里面把DataSourceTransactionObject的setConnectionHolder为null, 同时将Datasource去除绑定即在threadlocal里面去掉(如果恢复挂起,就是把这Datasource再绑定回去), 然后TransactionSynchronizationManager原先设置的事务名称,只读事务属性,事务隔离级别, 激活事务状态通通封装到对象SuspendedResourcesHolder里面, 然后将TransactionSynchronizationManager全部设置为初始状态当然要先保存这个source 2. 如果是PROPAGATION_REQUIRES_NEW,先挂起事务,然后开启新事务,如果出异常则恢复被挂起的事务 3. PROPAGATION_NESTED, 如果支持保存点,则先构建prepareTransactionStatus,然后创建保存点, 因为是嵌套事务,所有不是新事务isNewSynchronization(),那么所有属性都是用原先的, 比如隔离级别,时间等等
5.1.3 如果是新事务:
5.1.3.1. 然后根据事务txAtrr的传播行为PROPAGATION_REQUIRED,PROPAGATION_REQUIRES_NEW,PROPAGATION_NESTED这三种为一种类型处理,他们都是没事务则开启一个
新事务startTransaction
先构造DefaultTransactionStatus,属性赋值是否是个新事务以及newSynchronization,readOnly,刚开始都是新的
----------------------- 接着开始doBegin
设置Transaction某些属性-----如果没有connection先获取connection(判断条件!txObject.hasConnectionHolder() ||
txObject.getConnectionHolder().isSynchronizedWithTransaction()),此时获取到连接就具备了ConnectionHolder;通过DataSource获取连接,这边有个选择数据源切入点
然后synchronizedWithTransaction设置为true
设置conn的隔离级别和只读事务
设置conn的setAutoCommit,如果是true,则设置为false,
设置是否执行只读事务的SQL,两个因素决定:Tm里面的enforceReadOnly以及TransactionDefinition的readOnly,
然后激活事务transactionActive
设置超时时间到connection
如果是新连接,则绑定datasource和connectionHolder为map对象到当前线程threadLocal,保存的是一个map,key是datasource对象 ,value是connetionHolder
----------------------- doBegin结束,
接着将setActualTransactionActive,setCurrentTransactionIsolationLevel,setCurrentTransactionReadOnly,setCurrentTransactionName设置到当前线程中,最后initSynchronization----isSynchronizationActive=true
startTransaction结束,返回DefaultTransactionStatus;
5.1.3.2. 如果是其他行为,无需开启事务的,则直接构建DefaultTransactionStatus 返回,只是它的transaction是null5.1.4. 最后构造TransactionInfo对象传入tm, txAttr,然后设置status,最后将info设置到当前线程中threadlocal
-
然后执行invocation的proceed方法,回到了增强器链中执行下个chain
-
异常回滚:如果执行业务方法抛出异常
通过transactionAttribute判断异常是否在设置范围内,
在异常设置范围内,通过DefaultTransactionStatus判断是否有保存点,则回滚到保存点,
如果是新事务,则通过status获取getTransaction,然后transaction再获取connection进行rollback
如果是参与其他事务中,当前if (status.isLocalRollbackOnly() || isGlobalRollbackOnParticipationFailure()),则设置rollbackOnly=true(ResourceHolder里面的)
如果异常不在范围,则继续进行提交事务 -
提交事务
如果TransactionStatus是complete,则无法提交;
如果defStatus.isLocalRollbackOnly()被标记为回滚,则执行回滚,且unexpectedRollback=FALSE
如果!shouldCommitOnGlobalRollbackOnly() && defStatus.isGlobalRollbackOnly()(这是ResourceHolder里面的),则执行回滚,且unexpectedRollback=TRUE
否则开始提交(判断status.isNewTransaction()才可以提交意味新事务才行,嵌套和默认传播级别都是原用上一个事务的),tm里面有个prepareForCommit可以进行覆盖自定义逻辑,triggerBeforeCommit,triggerBeforeCompletion,triggerAfterCompletion,triggerAfterCommit,可以TransactionSynchronizationManager.getSynchronizations()进行放入自定义业务逻辑;
通过DefaultTransactionStatus获取getTransaction,然后再获取connection进行commit -
完成后,开始进行清除上面保存的资源,重置连接,释放连接,最后如果之前有挂起事务,则需要释放
-
- TransactionInterceptor#invoke
-
事务回滚异常情况(针对DataSourceTransactionManager)
-
Transaction rolled back because it has been marked as rollback-only
场景现况:一个PersonDao类中的一个方法调用了另外一个StudentDao里面的方法,然后StudentDao方法抛出了异常被PersonDao方法捕获了。开始前的想法:personDao方法能正常的插入数据,StudentDao操作的数据进行回滚
结果:两个dao都没有插入数据成功,且抛出了未曾想到的异常
分析:-
两个方法的事务传播行为都是默认即Propagation.REQUIRED,
-
当一个
具有事务的方法
如果抛出异常之后且符合transactionAttribute定义的类型,要进行TransactionManager.rollback操作,可以定义unexpectedRollback是否直接抛出异常
分三种情况操作:
①如果有Savepoint进行rollbackToHeldSavepoint。
②如果是新事务,则直接进行con.rollback()
③如果是参与了上一级的事务中(发生事务嵌套了
),
先判断rollbackOnly(默认false) 或者globalRollbackOnParticipationFailure(默认true) 这两个参数只要有一个true,
则进行标记connection的rollbackOnly=true,否则不做任何标记处理。
最后如果failEarlyOnGlobalRollbackOnly(默认false)这个为false,会直接修改unexpectedRollback=false保证不会抛出异常 -
所以StudentDao这边没设置任何参数且是参与上一级事务,所以标记rollbackOnly=true
-
当PersonDao进行提交操作的时候,
1.先判断当前事务中的TransactionStatus.rollbackOnly=true,那进行unexpectedRollback=false的TransactionManager.rollback三种情况操作
2.如果connection为rollbackOnly=true的状态,同时,是同时shouldCommitOnGlobalRollbackOnly=false,那么会进行unexpectedRollback=true的TransactionManager.rollback三种情况操作
所以PersonDao是新事务,因为它是最外一层同时传播行为是Propagation.REQUIRED;所以它先进行了con.rollback(),然后根据unexpectedRollback直接抛出了异常
-
-
解决方案
1.如果调用第三方法失败就失败,不能影响自己的事务提交,那么从commit方法可以看出,直接修改当前线程中的transactionStatus的rollbackOnly=true,那么就会进入不抛出异常(unexpected=false)的processRollback三种情况进行操作;
所以在捕获异常后,增加TransactionAspectSupport.currentTransactionStatus().setRollbackOnly(); 就可以改变rollbackOnly
-
-
Spring事务失效情况
- 需要进行事务支持的的类没有被spring管理;
spring 的事务是通过动态代理,所以前提要被spring进行管理,将interceptor进行织入到类中,这样从容器获取bean执行方法的时候才会触发 - final 方法无法触发事务执行,因为spring是使用动态代理,cglib方式是无法对final方法进行代理
- 方法内部调用无法触发事务;因为方法内部直接调用,获取的对象不是通过spring进行管理的对象,所以没有interceptor的织入
- 多线程调用会触发事务,但是不会回滚,所以这边也归入
spring事务的connection是通过TransactionSynchronizationManager进行操作threadLocal进行绑定的,所以方法内部如果使用多线程操作DB,获取到的connection不是同个,无法进行回滚
- 需要进行事务支持的的类没有被spring管理;
spring cache的套路与注解的AOP很相似,看下面图
在上面的AOP依赖的是AbstractAutoProxyCreator,而Validated注解生效的继承的是AbstractAdvisingBeanPostProcessor,这里面是有一个advisor变量,需要为其赋值,然后在postProcessAfterInitialization进行判断当前beanClass是否满足advisor的匹配,如果匹配,则为其创建proxy
-
主角是这个MethodValidationPostProcessor类,为啥,因为他里面有个注解Validated,
-
那这个后置处理器在什么时候被注入进来呢,在springWebMvc的自动装配的时候WebMvcAutoConfiguration这个类,会通过@AutoConfigureAfter先导入一个ValidationAutoConfiguration,他里面就会@Bean了一个MethodValidationPostProcessor后置处理器
-
MethodValidationPostProcessor 作用
- 这个类实现了InitializingBean接口,在初始化的时候,把增强器advisor给初始化了,增强器的是需要切入点pointcut和advice即interceptor, 所以它的pointcut是AnnotationMatchingPointcut,这里面有关键的classFilter(
他肯定是判断类上是否有注解
),methodMatcher他直接是MethodMatcher.TRUE,所以不用反射类中方法进行逐一判断,advice就是MethodValidationInterceptor - 怎么为bean进行匹配增强器呢: 因为它继承了AbstractAdvisingBeanPostProcessor,它实现了BeanPostProcessor,所以在postProcessAfterInitialization阶段先判断是bean是否是合法bean即通过AopUtils.canApply进行判断advisor是否匹配当前bean。其实就是判断类上是否有Validated注解,然后再根据methodMatcher,因为它的是MethodMatcher.TRUE,所以类上有注解即可
- 创建代理:当前的bean能够被advisor匹配后,就开始创建proxyFactory,然后添加这个advisor,进行创建具体的代理模式
- 这个类实现了InitializingBean接口,在初始化的时候,把增强器advisor给初始化了,增强器的是需要切入点pointcut和advice即interceptor, 所以它的pointcut是AnnotationMatchingPointcut,这里面有关键的classFilter(
async是使用AOP的AbstractAdvisingBeanPostProcessor这个继承体系与@Validated一样
通过EnableAsync注解@Bean一个AsyncAnnotationBeanPostProcessor,
不过这个postProcessor在setBeanFactory的时候初始化了advisor为AsyncAnnotationAdvisor,
赋值给了AbstractAdvisingBeanPostProcessor类中的advisor变量
- AsyncAnnotationAdvisor的poinut是AnnotationMatchingPointcut,AnnotationClassFilter作为classFilter
- advice 就是AnnotationAsyncExecutionInterceptor