前言相信小伙伴们一定都用过@Transactional注解,那么@Transactional背后的秘密你知道多少呢?Spring是如何开启事务的?它如何提交和关闭交易?1调试开始时的猜测在阅读源码之前,你应该已经知道MySQL是如何启动一个事务的。因此,我们可以得出一个猜测:跟着源码一起看,Spring的@Transactional注解是如何执行事务逻辑的?2Spring事务执行流程这里启动事务使用SpringBoot+MySQL+Druidcom.alibabadruid-spring-boot-starter1.2.6创建bean时,会为UserService生成一个基于AOP的代理对象;AbstractAutowireCapableBeanFactory#initializeBean...wrapIfNecessaryAbstractAutoProxyCreator#createProxyCglibAopProxy#getProxy生成代理对象,开始执行userService.updateUserInfo();这里的userService是代理对象;它将被CglibAopProxy.DynamicAdvisedInterceptor#intercept方法拦截;事务处理AbstractPlatformTransactionManager#getTransaction会在这里调用AbstractPlatformTransactionManager#startTransaction方法启动事务。看到doBegin这个词是不是突然觉得眼熟?跟进DataSourceTransactionManager#doBegin方法,注意此时在spring-jdbc-5.3.8.jar包下。因为使用了druid的连接池,这个Connection是durid的连接池。DruidPooledConnection#setAutoCommit(false)关闭自动提交;这里是druid的逻辑,执行它然后去com.alibaba.druid.filter.FilterChainImpl#connection_setAutoCommit。ConnectionImpl#setAutoCommit,这个在mysql-connector-java-8.0.25.jar包下。这句话是关键点SETautocommit=0。SETautocommit=0开始事务!总结一下流程:执行SQL后,启动事务后,会通过回调执行方法内部逻辑。因为这里使用的是Mybatis,所以还是会被代理。MapperProxy#调用;DruidPooledPreparedStatement#execute;ClientPreparedStatement#execute;执行过程比较简单:在TransactionAspectSupport#invokeWithinTransaction的最后一行提交事务,commitTransactionAfterReturning(txInfo);是提交事务。AbstractPlatformTransactionManager#commit抽象事务管理器,提交事务DataSourceTransactionManager#doCommit数据源数据管理器,这里提交事务必须是调用连接池的方法,所以会在DruidPooledConnection中执行DruidPooledConnectioncommit最终会执行到mysql-connector-.25.jar包下的java-8.0ConnectionImpl#commit调用commit提交事务。在这里提交异常回滚异常TransactionAspectSupport#invokeWithinTransaction将被捕获。AbstractPlatformTransactionManager#rollback在这里执行回滚,执行DataSourceTransactionManager#doRollback最后执行ConnectionImpl#rollback()到mysql-connector-java-8.0.25.jar的ConnectionImpl#rollbackNoChecks执行回滚语句rollbackrestoresautocommitcleanupTransactionInfo(txInfo);这个方法中之前设置的autocommit会被恢复。3Java原生开启事务如果觉得这个有点绕,那我们可以看看没有Spring的简单版。/***@authorliuzhihang*@date2021/6/1816:51*/publicclassMainTest{publicstaticvoidmain(String[]args)throwsException{DruidDataSourcedataSource=newDruidDataSource();dataSource.setDriverClassName("com.mysql.cj.jdbc.Driver");dataSource.setUrl("jdbc:mysql://localhost:3306/demo");dataSource.setUsername("root");dataSource.setPassword("root");Connectionconnection=dataSource.getConnection();try{//关闭自动提交connection.setAutoCommit(false);connection.prepareStatement("updateuser_infosetuser_name='liuzhihang'whereuser_id='1001';").executeUpdate();connection.prepareStatement("updateuser_addresssetaddress='anhui'whereuser_id='1001';").executeUpdate();//提交事务connection.commit();}catch(Exceptione){//回滚connection.rollback();}finally{//开启自动提交connection.setAutoCommit(true);}}}后看了Java原生提交事务的方式,感觉简单明了。Spring@Transactional只是创建了一个AOP代理,通过代理调用原生的开启关闭事务,同时也执行SQL部分,而Mybatis也执行代理提交SQL。4总结最后,合并图表总结流程。至此,交易执行流程分析完毕。但是还有一个问题?为什么使用setautocommit=0来启动事务,而不是使用begin或starttransaction来启动事务呢?