CGLIB代理方法执行基本流程CGLIB代理的事务CglibAopProxy的getProxy在进行增强的时候会去获取拦截器:CglibAopProxy的getCallbacks这个里面的方法拦截器内部是我们的事务相关的通知器,里面还有事务拦截器:DynamicAdvisedInterceptor的intercept内部跟JdkDynamicAopProxy的invoke很像,都是获取拦截链,其实就是事务拦截器:但是JdkDynamicAopProxy封装了一个ReflectiveMethodInvocation,最后是用反射调用被代理对象的方法的,这个前面讲过,不多说了。而CglibAopP
CGLIB代理方法执行基本流程FastClass方法调用前面讲了,动态创建了FastClass方法索引增强对象,能快速调用方法,内部是用FastClass调用的,我来看这个,注意这里是f2的invoke方法,传入参数是i2,obj是代理的CGLIB增强对象,也就是说,调用了代理的CGLIB增强类的FastClass方法索引增强对象CglibObj$$EnhancerByCGLIB$$ef630afc$$FastClassByCGLIB$$9f694f5b的invoke方法,传入索引13和代理的CGLIB增强对象,以及参数:方法索引增强对象的invoke方法调用其实这个我们debug是看不到的,
CGLIB代理方法执行基本流程拦截器的intercept调用的秘密一般我们在这里面做一些增强,然后调用原来的对象的方法,也就是methodProxy.invokeSuper,但是前面说了,传入参数o是代理对象,他是怎么调用原来的对象呢。:MethodProxy的invokeSuper调用父类方法其实看这个名字就知道是调用原方法,调用父类方法嘛,前面有继承嘛,对吧。但是事实是怎么样呢,我们来看看吧:publicObjectinvokeSuper(Objectobj,Object[]args)throwsThrowable{try{init();//初始化fastClassInfoFastClas
CGLIB代理方法执行基本流程CGLIB动态代理的细节本篇想讲下CGLIB动态代理的一些细节,因为前面的事务讲的是JDK的,我们知道JDK动态代理最终就是用了方法的反射调用,调用被代理对象的方法,而CGLIB可不是那么简单,虽然最终是执行了代理对象的方法,但是不是反射哦,而是用了方法代理MethodProxy和FastClass来做的,这个是什么鬼,我们慢慢道来。来个例子讲比较好理解这个例子虽然只用了拦截器,拦截器怎么用的,过滤器索引怎么对应过滤器的这个就不讲了,百度就好了,我要讲里面的细节,要展示的秘密可是很多的哦,我们来看看。publicclassCglibObj{publicvoidf1
创建事务信息流程图处理提交流程图处理回滚流程图传播机制图简单总结事务的例子和原理基本都分析了,当然也有些没讲清楚,还是需要自己多熟悉,其实原理就是AOP的拦截,每个方法都会创建一个事务状态,事务状态描述了创建的事务是不是新事务,是不是有新连接,是不是新同步,这些属性会影响回滚和提交的逻辑。还有事务传播机制原理基本源码里有,主要是REQUIRES_NEW和NESTED,这两个和其他的搭配可以有不同的效果,具体还是要看你的业务需求的。具体的组合很多,我也试不完,但是知道里面的原理,调试下就可以明白了,比如你知道REQUIRES_NEW是会创建一个新连接的,那么他就跟已经存在的事务隔离了,如果出了异
创建事务信息流程图处理提交流程图处理回滚流程图外层是REQUIRED有异常捕获内层是NESTED事务状态外层:内层用外层的连接,而且不是新事务,不是新同步:异常回滚内层直接进保存点回滚:会重置连接持有器的回滚标记:外层因为有异常捕获,而且连接持有器的回滚标记是false,所以成功提交。外层是REQUIRED有异常内层是NESTED结果是全回滚,因为内层成功了提交就把保存点上方了,其他什么都不做,然后外层遇到异常就直接回滚了。所以这里就是一个好的例子,如果内层业务出了异常,外层捕获后就不会影响外层。比如前面说过的插入数据在外层,内层是打日志,就算打日志出现异常,也只是影响日志,外部的数据插入还是
创建事务信息流程图处理提交流程图处理回滚流程图内外都是REQUIRED,外层捕获异常如果外面这样会如何呢:外层事务会去提交,但是最后还是执行回滚,然后报异常,因为内层已经设置了全局回滚,在这里就会进回滚的逻辑,unexpected=true。回滚完之后报异常,表示说都已经设置全局回滚了,为什么还要进提交:所以最终结果还是都回滚,而且还抛异常了,所以最好不要这么用,这个是在内层是REQUIRES_NEW和NESTED里用就可以,具体后面讲。外层是REQUIRED并捕获异常内层是REQUIRES_NEW先来外层捕获异常。事务状态我们先来看看他们创建的状态吧,首先是外层的:然后内层的,创建了新的连接
创建事务信息流程图处理提交流程图处理回滚流程图外部方法回滚处理前面说了,内部异常后,事务没做什么,只是设置了全局回滚的标记,处理完之后异常还会继续往上抛:然后来看外部的事务怎么处理,先看外部事务状态,此时可见连接持有器的回滚标记已经被内部事务改成true了,因为外部事务创建的时候是一个新事务,所以就可以执行doRollback,内部就是JDBC连接的回滚:清除处理回滚完了之后到了清除方法中,这两个方法是满足条件可以执行的:一个就是把线程私有变量的属性全部清除了:另一个进行数据源和连接资源的解绑,因为马上就要释放连接资源了,然后把连接的属性设置回来,比如自动提交,隔离级别,只读设置等:解绑会把数
创建事务信息流程图处理提交流程图处理回滚流程图准备和线程私有变量同步状态prepareSynchronization前面讲了,新事务创建了,事务状态也创建了,新连接也创建了,那就需要设置一些属性了,为了避免多线程安全问题,避免用锁的情况下,设计了一些线程私有变量来存这些属性:可以看到,只要是新的同步,就会把状态里的一些信息同步到线程私有变量里,比如事务是否激活了,事务隔离级别,事务是否只读等信息,最后还会激活事务同步状态,也就是:内外都是REQUIRED是如何回滚的我们还是举个例子讲:创建事务状态内部异常怎么就全回滚了呢,我们用前面的事务状态来分析下:首先外部创建了一个事务,因为当前没事务,所
创建事务信息流程图处理提交流程图处理回滚流程图开启新连接上篇说了,会给每个方法创建一个事务状态TransactionStatus,实现类是DefaultTransactionStatus,里面有三个属性很重要,一个是当前新创建的事务transaction,一个是是否是新事务标记newTransaction,另外一个是newSynchronization是否是新同步,也就是新的事务可能需要跟线程做同步,把线程私有变量设置成跟当前事务相关的值。如果设置了新事务,那是什么样的呢,其实就在doBegin里,里面会开启新连接:如果是第一次创建事务,或者是要创建一个新事务,比如REQUIRES_NEW的情
创建事务信息流程图处理提交流程图处理回滚流程图spring事务设计思想所有的细节都在图上了,所以我把图都放上来了,方便对照。首先就是事务是用AOP来完成的,也就是一个try,catch,finally,最后再提交://创建事务信息TransactionInfotxInfo=createTransactionIfNecessary(ptm,txAttr,joinpointIdentification);ObjectretVal;try{retVal=invocation.proceedWithInvocation();//调用方法}catch(Throwableex){completeTrans
处理提交流程图传播机制图AbstractPlatformTransactionManager的processCommit处理提交其实这个跟处理回滚很像,先处理保存点,然后处理新事务,如果不是新事务不会真正提交,要等外层是新事务的才提交,最后根据条件执行数据清除,线程的私有资源解绑,重置连接自动提交,隔离级别,是否只读,释放连接,恢复挂起事务等。privatevoidprocessCommit(DefaultTransactionStatusstatus)throwsTransactionException{try{booleanbeforeCompletionInvoked=false;try
处理回滚流程图处理提交流程图传播机制图ConnectionHolder的clear连接持有器的清除继续上一篇的doCleanupAfterCompletion,连接关闭了,最后连接持有器也应该清除状态。@Overridepublicvoidclear(){super.clear();this.transactionActive=false;this.savepointsSupported=null;this.savepointCounter=0;}ResourceHolderSupport的clear清除事务同步状态,回滚状态等。publicvoidclear(){this.synchroni
处理回滚流程图传播机制图AbstractPlatformTransactionManager的cleanupAfterCompletion回滚后处理看上去很像没多少东西,其实里面涉及好多呢,比如如果是新的事务同步状态的话,要把线程的同步状态清除了,如果是新事务的话,进行数据清除,线程的私有资源解绑,重置连接自动提交,隔离级别,是否只读,释放连接等。如果有挂起的事务,还要把这个事务给恢复,其实就是把属性设置回去。privatevoidcleanupAfterCompletion(DefaultTransactionStatusstatus){status.setCompleted();if(st
处理回滚流程图传播机制图AbstractPlatformTransactionManager的processRollback处理回滚unexpectedRollback这个一般是false,除非是设置rollback-only=true,才是true,表示是全局的回滚标记。首先会进行回滚前回调,然后判断是否设置了保存点,比如NESTED会设置,要先回滚到保存点。如果状态是新的事务,那就进行回滚,如果不是新的,就设置一个回滚标记,内部是设置连接持有器回滚标记。然后回滚完成回调,根据事务状态信息,完成后数据清除,和线程的私有资源解绑,重置连接自动提交,隔离级别,是否只读,释放连接,恢复挂起事务等p
创建事务信息流程图处理回滚流程图传播机制图AbstractPlatformTransactionManager的prepareSynchronization同步状态设置各种线程私有变量的状态。protectedvoidprepareSynchronization(DefaultTransactionStatusstatus,TransactionDefinitiondefinition){if(status.isNewSynchronization()){TransactionSynchronizationManager.setActualTransactionActive(status.ha
创建事务信息流程图传播机制图AbstractPlatformTransactionManager的suspend挂起当前事务有些传播机制需要挂起当前事务,比如NOT_SUPPORTED,REQUIRES_NEW。首先会清除所有线程相关的同步状态,如果当前事务存在的话,就进行一些属性的清除,比如清空连接持有器,清空线程私有变量的同步状态,最后把当前事务清除的属性保存到一个SuspendedResourcesHolder里,以便于恢复的时候设置会去。@NullableprotectedfinalSuspendedResourcesHoldersuspend(@NullableObjecttrans
创建事务信息流程图传播机制图AbstractPlatformTransactionManager的getTransaction获取事务事务的创建是跟事务管理器相关的,也就是:@OverridepublicfinalTransactionStatusgetTransaction(@NullableTransactionDefinitiondefinition)throwsTransactionException{TransactionDefinitiondef=(definition!=null?definition:TransactionDefinition.withDefaults());/
传播机制图AOP拦截原理简单的示意图:前面我们实战了7中传播机制,现在我们就来看下原理吧,源码比较复杂,我只选重点的讲。首先前面说了AOP事务的初始化,我们知道事务是基于AOP的拦截的,前面创建了JdkDynamicAopProxy:调用里面的invoke方法,这个在讲AOP的时候也讲过,其实就是获取拦截器链:将目标对象的方法封装成MethodInvocation,然后执行拦截器,里面是个递归执行的方法:最后执行的是拦截器的方法,也就是事务拦截器TransactionInterceptor的invoke:TransactionAspectSupport的invokeWithinTransact
传播机制图NESTED存在事务就会创建保存点,到时候回滚到保存点,不存在就创建一个事务。例子外层无捕获异常外层无捕获异常的情况,就算创建了保存点,回滚了,但是外层事务捕获到异常还是会进行混滚,等于什么都没做了。外层有捕获异常外层有捕获异常的情况,回滚到保存点。REQUIRED和NESTED回滚的区别这个其实能回滚到保存点是因为用了同一个连接,会先设置一个保存点,如果出了异常就会回滚到那个保存点。然后外层业务捕获了异常,就可以提交了。如果外层业务没有捕获异常,那就整个连接回滚了,这个时候保存点就没用了。那有人会说了,如果我都用REQUIRED,内部异常,外部业务捕获是不是跟这个一样的效果呀,其实
传播机制图NEVER不要事务,如果当前存在事务还要报异常。例子直接回滚了,调用accountDao.insertTest1()的时候报异常:org.springframework.transaction.IllegalTransactionStateException:Existingtransactionfoundfortransactionmarkedwithpropagation'never'但是换个方式用,报异常了也可以全成功:结果:NOT_SUPPORTED不要事务,如果当前存在事务就把当前事务挂起。例子结果就是account成功了,user不成功:REQUIRES_NEW如果当前存
先实战后原理事务这个东西一开始理解起来比较复杂,所以我打断先讲下实战例子,有个概念,然后我们带着问题去看源码,期间我也会画图来辅助理解,尽可能把7大传播机制都讲透,这样对事物的使用就得心应手了。7大传播机制其实可以分成3个支持当前事务,3个不支持当前事务,1个嵌套事务。什么是当前事务呢,你可以理解为A方法调用B方法,A有事务,那对于B来说,A的事务就是当前事务。大致的传播机制就是这样:MANDATORY使用当前事务,当前如果没事务就报异常。例子直接抛出异常:org.springframework.transaction.IllegalTransactionStateException:Noex
JDK动态代理前面讲了事务注解的属性获取,如果发现有实例的方法有事务后面就会进行动态代理,其实和前面AOP流程一样的。实战首先事务需要数据库支持,我们就用mysql,然后需要数据源,我们用默认的DriverManagerDataSource,然后我们还用JdbcTemplate,然后还需要一个DataSourceTransactionManager,否则用不了,会报异常。TransactionConfig配置类@Configuration@PropertySource("classpath:db.properties")@EnableTransactionManagemen
初始化流程图AbstractAdvisorAutoProxyCreator的findAdvisorsThatCanApply前面BeanFactoryTransactionAttributeSourceAdvisor已经被实例化了,现在要检测实例化之后的bean是否需要通知器,其实就是检测方法或者类上是否有事务注解。AopUtils的findAdvisorsThatCanApply主要是这里,看是否有事务注解:AopUtils的canApply主要是这个方法,他会获取目标类以及父类的所有的方法,进行一一匹配,查看方法和类上是否有事务注解,有的话就直接返回,说明这个类型是可以进行通知器应用的。p
初始化流程图@EnableTransactionManagement做了什么我们新来看下这个注解吧,可以看到他也有代理方式proxyTargetClass,默认false是JDK动态代理,还有AdviceMode,这个就是后面创建那种类型的Advice,默认是代理,这个后面会有看到,其实这个跟AOP注解的类似,也会import一个类TransactionManagementConfigurationSelector:@Target(ElementType.TYPE)@Retention(RetentionPolicy.RUNTIME)@Documented@Import(Transaction
初始化流程图事务的初始化流程前面讲了事务的注解的作用,需要一些类的辅助,那这些类是什么时候创建,又是做什么的呢,我们来看看。AutoProxyRegistrar创建spring的初始化流程前面有讲过,会用ConfigurationClassPostProcessor进行注解配置类的解析,如果遇到ImportBeanDefinitionRegistrar的话,会进行实例化,然后到后面的加载bean定义的时候调用loadBeanDefinitionsFromRegistrars执行他们的registerBeanDefinitions方法,AutoProxyRegistrar会进行Infrastru
AOP通知方法如何执行先看下整体的AOP基本通知方法的内部执行顺序结构:我们拿JDK动态代理为例,CGLIB也是类似,当调用某个方法的时候,会进行JdkDynamicAopProxy代理的invoke执行:@Override@NullablepublicObjectinvoke(Objectproxy,Methodmethod,Object[]args)throwsThrowable{ObjectoldProxy=null;booleansetProxyContext=false;TargetSourcetargetSource=this.advised.targetSource;Object
JdkDynamicAopProxy的getProxy主要是JdkDynamicAopProxy在代理前还有一些事情要说下:AopProxyUtils的completeProxiedInterfaces这里说白了就是增加一些其他的接口SpringProxy,DecoratingProxy,Advised。staticClass<?>[]completeProxiedInterfaces(AdvisedSupportadvised,booleandecoratingProxy){Class<?>[]specifiedInterfaces=advised.getProxie
postProcessAfterInitialization初始化之后进行代理AOP的真正代理是在实例创建了,初始化之后进行代理,也就是在这个里面由AbstractAutoProxyCreator进的postProcessAfterInitialization方法进行处理:首先查看是否在earlyProxyReferences里存在,也就是已经处理过了,不存在就考虑是否要包装,也就是代理。@OverridepublicObjectpostProcessAfterInitialization(@NullableObjectbean,StringbeanName){if(bean!=null){O
创建代理上篇讲了先获取匹配的通知器,其实也叫做拦截器,准备代理,这篇我们就看看是什么做的。AbstractAutoProxyCreator的createProxy创建代理进行代理工厂的创建,然后判断是否需要设置proxyTargetClass,以便于后面决定是不是要进行JDK动态代理还是CGLIB的动态代理,然后把通知器advisors包装下,加入到代理工厂,获取代理对象。protectedObjectcreateProxy(Class<?>beanClass,@NullableStringbeanName,@NullableObject[]specificInterceptors