事务
.事务registerAutoProxyCreator的是InfrastructureAdvisorAutoProxyCreator,跟切面类AOP同样的继承线路
所以你看事务中BeanFactoryTransactionAttributeSourceAdvisor是一个PointcutAdvisor,就需要有pointcut和advice,
@EnableCaching
缓存这边也是注入InfrastructureAdvisorAutoProxyCreator.
然后跟事务很相似,BeanFactoryCacheOperationSourceAdvisor是一个PointcutAdvisor
@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创建
初始化时机:
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;
标记过的beanclass不能被增强
:如果当前bean被增强过,则不会再次增强:通过判断是否存在advisedBeans集合中哪些类或者接口不能被增强
:如果么有被增强过,则判断是否是基础类型Advice,Pointcut,Advisor,AopInfrastructureBean这些接口实现类,这些是基础类型InfrastructureClass,不能被增强;同时如果是切面类且!compiledByAjc,也是不能被增强的哪些beanName需要被跳过
:接着判断是否需要被跳过的bean(shouldSkip), shouldSkip方法(这个方法被AspectJAwareAdvisorAutoProxyCreator类重写了)会处理如下事情如何找到候选的增强器
findCandidateAdvisors (AnnotationAwareAspectJAutoProxyCreator重写)逻辑是啥 这个顺序的Around.class, Before.class, After.class, AfterReturning.class, AfterThrowing.class),如果有相同的多个通知类型,继续按照方法名称排序,如果么有通知类型的方法,则也是按照方法名称排序
,作为备选的advisorMethod:方法的增强器构建结束后,会开始处理切面类中属性的增强器收集,构建DeclareParentsAdvisor,这个目前没接触,后面在看看 ? ? 在这个类处理ReflectiveAspectJAdvisorFactory
AbstractAutoProxyCreator #postProcessAfterInitialization 初始化时机
wrapIfNecessary 做了啥
收集只是为了判断advisors的切面类名getAspectName是否与当前的beanName相同,相同则跳过
),因为有可能上个阶段不是必须经历的,那在这边要有相同的处理至于如何匹配,太复杂了,看不懂,留着???
所以可以看出是配置优先于接口代理
,否则进入判断,如果当前的bean的beanDefination有这个preserveTargetClass属性为true,则代理;否则再进行接口的判断,接口实现需要排除配置接口的判断,如InitializingBean,DisposableBean,Closeable,AutoCloseable,Aware以及内部语言接口名称groovy.lang.GroovyObject等等,这些接口不能算,如果排除了接口还有正常接口,收集它们进入interfaces,同时不设置setProxyTargetClass为True,否则设置它为true,这个如果是true创建代理会直接创建cglibProxytargetClass是个接口或者是个代理类则创建JdkDynamicAopProxy
,否则使用bjenesisCglibAopProxy??
;环绕通知会传入一个MethodInvocationProceedingJoinPoint参数,然后调用在业务方法里面继续调用joinpoint的process方法,这里面会进行ReflectiveMethodInvocation的clone复制一份,然后才调用process,又回去了继续调用链的获取然后在finally里面才发起业务逻辑@after注解通知方法的调用,所以@after不管如何都会执行的而且是最后执行
jdkProxy
@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);
}
}
}
AutoProxyRegistrar 这个一看就是注入一个BeanDefinition,这个里面主要做了解析注解中的mode和proxyTargetClass属性,然后注入InfrastructureAdvisorAutoProxyCreator,上面的AOP注入的是AnnotationAwareAspectJAutoProxyCreator,如果存在了这个就不会注入事务的InfrastructureAdvisorAutoProxyCreator。
InfrastructureAdvisorAutoProxyCreator它是用来筛选基础的adivsor,看下面的isEligibleAdvisorBean方法
导入一个ProxyTransactionManagementConfiguration,这里面注入所有bean都是ROLE_INFRASTRUCTURE
@Transactional
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是null
5.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
完成后,开始进行清除上面保存的资源,重置连接,释放连接,最后如果之前有挂起事务,则需要释放
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 cache的套路与注解的AOP很相似,看下面图
在上面的AOP依赖的是AbstractAutoProxyCreator,而Validated注解生效的继承的是AbstractAdvisingBeanPostProcessor,这里面是有一个advisor变量,需要为其赋值,然后在postProcessAfterInitialization进行判断当前beanClass是否满足advisor的匹配,如果匹配,则为其创建proxy
主角是这个MethodValidationPostProcessor类,为啥,因为他里面有个注解Validated,
那这个后置处理器在什么时候被注入进来呢,在springWebMvc的自动装配的时候WebMvcAutoConfiguration这个类,会通过@AutoConfigureAfter先导入一个ValidationAutoConfiguration,他里面就会@Bean了一个MethodValidationPostProcessor后置处理器
MethodValidationPostProcessor 作用
他肯定是判断类上是否有注解
),methodMatcher他直接是MethodMatcher.TRUE,所以不用反射类中方法进行逐一判断,advice就是MethodValidationInterceptorasync是使用AOP的AbstractAdvisingBeanPostProcessor这个继承体系与@Validated一样
通过EnableAsync注解@Bean一个AsyncAnnotationBeanPostProcessor,
不过这个postProcessor在setBeanFactory的时候初始化了advisor为AsyncAnnotationAdvisor,
赋值给了AbstractAdvisingBeanPostProcessor类中的advisor变量