事务管理对于企业应用来说是至关重要的,当出现异常情况时,它可以保证数据的一致性。spring支持编程式事务管理和声明式事务管理两种方式。
首先我们来看看spring框架的事物抽象。Spring的事务策略由TransactionManager接口定义,PlatformTransactionManager接口和ReactiveTransactionManager接口继承了TransactionManager接口。我们的程序大多用的都是PlatformTransactionManager接口。
Spring 5.0之后引入了reactive web框架webflux,与webflux平级的就是webmvc,webflux是一个完全的响应式并且非阻塞的web框架,因此spring 5.2之后Spring还为响应式web框架提供了事务管理抽象,即ReactiveTransactionManager接口。我们下面主要讲的是PlatformTransactionManager事务管理抽象。
下面是PlatformTransactionManager接口的源码:
public interface PlatformTransactionManager extends TransactionManager { TransactionStatus getTransaction(@Nullable TransactionDefinition var1) throws TransactionException; void commit(TransactionStatus var1) throws TransactionException; void rollback(TransactionStatus var1) throws TransactionException; }
声明式事务一般使用@Transactional注解,并且使用@EnableTransactionManagement开启事务管理就足够了,但接下来我们讲的是其背后的工作原理。
声明式事务是建立在AOP之上的,首先我们的应用程序会通过XML的方式或者注解的方式提供元数据,AOP与事务元数据结合产生一个代理。当执行目标方
法时拦截器TransactionInterceptor会对目标方法进行拦截,然后在目标方法的前后调用代理。其本质是在目标方法开始之前创建或者加入一个事务,在执行完目标
方法之后根据执行情况提交或者回滚事务。
拦截器TransactionInterceptor通过检查方法返回类型来检测是哪种事务管理风格。如果返回的是响应式类型(例如Publisher)的方法则符合响应式事务管理的条
件,其他返回类型包括void则使用PlatformTransactionManager。
@Transactional注解是基于注解的声明式事务,当然基于XMl配置的也可以,但因为是在Spring Boot应用中使用,所以一律使用基于注解的声明式事务。
@Transactional可以作用于接口定义上、接口方法上、类定义上和类的公共方法上,如果作用于私有方法或者包可见的方法上,虽然不会引发错误,但是并不会激
活事务的一些行为。另外,将@Transactional作用于类上要比作用于接口上要更好,下面我来说明下原因。Spring AOP框架中有两种模式,分别是基于代理和
基于AspectJ,而基于代理又分为两种,一种是基于接口的,一种是基于类的,如果是基于类的代理或是基于AspectJ,则将@Transactional作用于接口上不会起到
任何作用。
上面说到了事务是基于AOP的,那么如何让事务支持多种模式呢?@EnableTransactionManagement注解提供了相关的支持,@EnableTransactionManagement注解的源码如下:
public @interface EnableTransactionManagement { boolean proxyTargetClass() default false; AdviceMode mode() default AdviceMode.PROXY; int order() default 2147483647; }
可以看到,事务管理默认模式为基于代理,即mode=AdviceMode.PROXY,且默认的代理方式为基于接口的,因为proxyTargetClass=false。
下面来看一下@Transactional注解的属性有哪些:
属性 | 类型 | 描述 |
---|---|---|
value | String | 指定事务管理器 |
propagation | enum: Propagation | 事务传播行为 |
isolation | enum: Isolation | 事务隔离级别 |
timeout | Int(单位为秒) | 事务超时时间 |
readOnly | boolean | 是否只读 |
rollbackFor | Class[] | 引起回滚的异常类数组 |
rollbackForClassName | String[] | 引起回滚的异常类名称数组 |
noRollbackFor | Class[] | 不会引起回滚的异常类数组 |
noRollbackForClassName | String[] | 不会引起回滚的异常类名称数组 |
无论您在 Spring 中选择声明式还是编程式事务管理,定义正确的TransactionManager实现都是绝对必要的,TransactionManager的实现通常与它们工作的环境有关:JDBC、JTA、Hibernate 等等。
如果是纯 JDBC,则使用DataSourceTransactionManager,如下代码所示。
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close"> <property name="driverClassName" value="${jdbc.driverClassName}" /> <property name="url" value="${jdbc.url}" /> <property name="username" value="${jdbc.username}" /> <property name="password" value="${jdbc.password}" /> </bean> <bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref="dataSource"/> </bean>
如果是Hibernate,则使用HibernateTransactionManager。(Hibernate相较于纯JDBC多了一层sessionFactory)
<bean id="sessionFactory" class="org.springframework.orm.hibernate5.LocalSessionFactoryBean"> <property name="dataSource" ref="dataSource"/> <property name="mappingResources"> <list> <value>org/springframework/samples/petclinic/hibernate/petclinic.hbm.xml</value> </list> </property> <property name="hibernateProperties"> <value> hibernate.dialect=${hibernate.dialect} </value> </property> </bean> <bean id="txManager" class="org.springframework.orm.hibernate5.HibernateTransactionManager"> <property name="sessionFactory" ref="sessionFactory"/> </bean>
如下图所示,由于test_1和test_2这两个DB在同一个数据源下面,因此是本地事务,spring事务是直接就可以支持的。
@Service @Transactional public class MultiDBServiceImpl implements IMultiDBService { @Autowired private JdbcTemplate jdbcTemplate; @Override public void save() { String sql1 = "insert into test_2.t_class(Fname,Fnum) values(\"303班\",30);"; String sql2 = "insert into test_1.t_student(Fname,Fage,Fclass) values(\"曹操\",30,3);"; jdbcTemplate.execute(sql1); jdbcTemplate.execute(sql2); // 回滚 throw new RuntimeException(); } }
CREATE TABLE t_student ( Fid int(10) UNSIGNED NOT NULL AUTO_INCREMENT, Fname varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL, Fage int(255) NULL DEFAULT NULL, Fclass int(255) NULL DEFAULT NULL, PRIMARY KEY (Fid) USING BTREE ) ENGINE = InnoDB AUTO_INCREMENT = 4 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = Dynamic; CREATE TABLE t_class ( Fid int(10) UNSIGNED NOT NULL AUTO_INCREMENT, Fname varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL, Fnum int(11) NULL DEFAULT NULL, PRIMARY KEY (Fid) USING BTREE ) ENGINE = InnoDB AUTO_INCREMENT = 4 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = Dynamic;
如果没打@Transactional注解,则直接进入目标方法,否则,进入到cglib生成的代理类中,如下图所示。
可以看到,cglib生成的代理为是com.bobo.springbootdemo.service.impl.MultiDBServiceImpl E n h a n c e r B y S p r i n g C G L I B EnhancerBySpringCGLIB EnhancerBySpringCGLIB2afb0e3b类型,
且实现了3个接口(其中Advised接口颇为重要),继承自目标类即com.bobo.springbootdemo.service.impl.MultiDBServiceImpl。
AOP代理对象的生成是在Bean初始化方法中,有一些BeanPostProcessor对象,其中有一个叫AnnotationAwareAspectJAutoProxyCreator,是专门用于创建代理对象的,AnnotationAwareAspectJAutoProxyCreator是AbstractAutoProxyCreator的子类,AbstractAutoProxyCreator是BeanPostProcessor的子类,AbstractAutoProxyCreator实现了postProcessAfterInitialization方法,如下所示。
@Override public Object postProcessAfterInitialization(@Nullable Object bean, String beanName) { if (bean != null) { Object cacheKey = getCacheKey(bean.getClass(), beanName); if (this.earlyProxyReferences.remove(cacheKey) != bean) { return wrapIfNecessary(bean, beanName, cacheKey); } } return bean; }
wrapIfNecessary方法核心代码如下图所示。
这里分两步叙述:
getAdvicesAndAdvisorsForBean方法点进去,会找到如下方法。
protected List<Advisor> findEligibleAdvisors(Class<?> beanClass, String beanName) { // 找到候选的Advisors List<Advisor> candidateAdvisors = findCandidateAdvisors(); // 从候选Advisors中找到符合条件的Advisors,判断是否符合条件前面已经介绍过了,说白了就是有事务注解 List<Advisor> eligibleAdvisors = findAdvisorsThatCanApply(candidateAdvisors, beanClass, beanName); extendAdvisors(eligibleAdvisors); if (!eligibleAdvisors.isEmpty()) { eligibleAdvisors = sortAdvisors(eligibleAdvisors); } return eligibleAdvisors; }
下面介绍如何找候选的Advisors。
通过一路跟踪,最终是走到了BeanFactoryAdvisorRetrievalHelper类的findAdvisorBeans方法。
候选的Advisors的寻找方法实际上就等价于从Bean工厂中找到所有Advisor类型的bean。
如上图所示,会从BeanFactory中找到一个beanName为org.springframework.transaction.config.internalTransactionAdvisor、类型为Advisor的bean BeanFactoryTransactionAttributeSourceAdvisor,该bean通过依赖注入了Advice类型的advice属性,TransactionInterceptor是Advice的子类。
通过全局搜索可以得知,TransactionInterceptor和BeanFactoryTransactionAttributeSourceAdvisor这两个bean的配置在ProxyTransactionManagementConfiguration类里面,如下图所示。
BeanFactoryTransactionAttributeSourceAdvisor间接继承自PointcutAdvisor,实现了getPointcut方法,如下图所示。
TransactionAttributeSource只是个接口,那么它的实现是什么,如何找到切入点的?
TransactionAttributeSource实现前面的图中已经给出了,是AnnotationTransactionAttributeSource,我们来看看这个类是怎么找到事务方法的。
这个类中有两个重要的方法,源码如下。
@Override @Nullable protected TransactionAttribute findTransactionAttribute(Class<?> clazz) { return determineTransactionAttribute(clazz); } @Override @Nullable protected TransactionAttribute findTransactionAttribute(Method method) { return determineTransactionAttribute(method); }
可以看到参数可以是Class类型也可以是Method类型,这说明@Transactional注解既可以作用于类上也可以作用于方法上。
点进findTransactionAttribute方法,如下所示。
@Nullable protected TransactionAttribute determineTransactionAttribute(AnnotatedElement element) { for (TransactionAnnotationParser parser : this.annotationParsers) { TransactionAttribute attr = parser.parseTransactionAnnotation(element); if (attr != null) { return attr; } } return null; }
TransactionAnnotationParser的实现有:Ejb3TransactionAnnotationParser、JtaTransactionAnnotationParser和SpringTransactionAnnotationParser,由于这是Spring事务,因此点进去SpringTransactionAnnotationParser看,源码如下。
@Override @Nullable public TransactionAttribute parseTransactionAnnotation(AnnotatedElement element) { AnnotationAttributes attributes = AnnotatedElementUtils.findMergedAnnotationAttributes( element, Transactional.class, false, false); if (attributes != null) { return parseTransactionAnnotation(attributes); } else { return null; } }
可以看到,其实就是看有没有Transactional注解,如果有,则进入parseTransactionAnnotation方法,parseTransactionAnnotation是用来解析Transactional注解的各种属性的。
下面通过debug验证一下。
通过debug,可以看到将Transactional注解打在MultiDBServiceImpl上之后,确实走到了findTransactionAttribute(Class<?> clazz)方法,如下图所示。
AbstractAutoProxyCreator.createProxy方法如下图所示。
proxyFactory.getProxy方法如下所示。
public Object getProxy(@Nullable ClassLoader classLoader) { return createAopProxy().getProxy(classLoader); } // createAopProxy方法 protected final synchronized AopProxy createAopProxy() { // AopProxyFactory接口目前只有一种实现:DefaultAopProxyFactory return getAopProxyFactory().createAopProxy(this); } // DefaultAopProxyFactory的createAopProxy方法 public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException { if (config.isOptimize() || config.isProxyTargetClass() || hasNoUserSuppliedProxyInterfaces(config)) { Class<?> targetClass = config.getTargetClass(); if (targetClass.isInterface() || Proxy.isProxyClass(targetClass)) { return new JdkDynamicAopProxy(config); } // 这里的config实际上就ProxyFactory对象,ProxyFactory是ProxyConfig的子类 return new ObjenesisCglibAopProxy(config); } else { return new JdkDynamicAopProxy(config); } }
ObjenesisCglibAopProxy是CglibAopProxy的子类,CglibAopProxy.getProxy方法如下图所示。
getProxy方法主要代码如下所示。
Enhancer enhancer = createEnhancer(); if (classLoader != null) { enhancer.setClassLoader(classLoader); if (classLoader instanceof SmartClassLoader && ((SmartClassLoader) classLoader).isClassReloadable(proxySuperClass)) { enhancer.setUseCache(false); } } enhancer.setSuperclass(proxySuperClass); enhancer.setInterfaces(AopProxyUtils.completeProxiedInterfaces(this.advised)); enhancer.setNamingPolicy(SpringNamingPolicy.INSTANCE); enhancer.setStrategy(new ClassLoaderAwareGeneratorStrategy(classLoader)); // 获取callbacks,其中就包括CglibAopProxy的内部类DynamicAdvisedInterceptor Callback[] callbacks = getCallbacks(rootClass); Class<?>[] types = new Class<?>[callbacks.length]; for (int x = 0; x < types.length; x++) { types[x] = callbacks[x].getClass(); } // fixedInterceptorMap only populated at this point, after getCallbacks call above enhancer.setCallbackFilter(new ProxyCallbackFilter( this.advised.getConfigurationOnlyCopy(), this.fixedInterceptorMap, this.fixedInterceptorOffset)); enhancer.setCallbackTypes(types); // 创建代理类的Class对象和实例 return createProxyClassAndInstance(enhancer, callbacks);
点进去之后,发现被CglibAopProxy拦截,如下图所示。
当用@Autowired注入IMultiDBService Bean并且调用它的save时,会经过如下过程:
后面就是TransactionInterceptor的逻辑了,本教程暂不介绍。