美文网首页
Spring 4.3 源码分析之 编程/声明式事务

Spring 4.3 源码分析之 编程/声明式事务

作者: 爱吃鱼的KK | 来源:发表于2018-02-14 21:04 被阅读136次
    1. Spring Tx 编程

    整个Spring事务可以通过编程与声明两种方式进行操作:

    1. AbstractPlatformTransactionManager: 通过其子类来自己控制事务开始, 提交, 回滚等操作
    2. TransactionTemplate: 通过传入一个回调函数来控制程序的执行, 其内部还是通过 AbstractPlatformTransactionManager的子类
    3. TransactionProxyFactoryBean: 通过在配置文件中配置 transactionAttributes 来决定对哪些方法进行事务的作用, 此时的 TransactionAttributes获取器是 NameMatchTransactionAttributeSource <-- 这站方式使用得比较少了
    4. 通过 Tx namespace 的方式来声明事务
    5. 通过在方法上标注 @Transactional 注解的方式来实现声明式事务
    
    2. Spring Tx 编程式事务 AbstractPlatformTransactionManager

    AbstractPlatformTransactionManager 是事务操作的基础类, 它具有 begin(事务开启), suspend(挂起事务, 比如 PROPAGATION_REQUIRES_NEW 级别), resume (将挂起的事务重用, 往往在上个事务提交会回滚后), commit(事务的提交), rollback(事务的回滚, 默认我们遇到 RuntimeException | Error 时回滚), 当然这些方法都是模版方法, 都是留给对应ORM的子类去实现的, 比如我们最常用的 DataSourceTransactionManager, 下面的 Demo 就是基于 DataSourceTransactionManager :

    private static final String CREATE_TABLE_SQL = "create table test" +
            "(id int(10) AUTO_INCREMENT PRIMARY KEY, " +
            "name varchar(100))";
    private static final String DROP_TABLE_SQL = "drop table test";
    private static final String INSERT_SQL = "insert into test(name) values(?)";
    private static final String COUNT_SQL = "select count(*) from test";
    
    public static void main(String[] args) {
        // 定义连接池
        DataSource dataSource = getDataSource();
        // 先建 PlatformTransactionManager
        DataSourceTransactionManager transactionManager = new DataSourceTransactionManager();
        transactionManager.setDataSource(dataSource);
        // 定义 TransactionDefinition
        DefaultTransactionDefinition definition = new DefaultTransactionDefinition();
        // 获取 TransactionStatus
        TransactionStatus transactionStatus = transactionManager.getTransaction(definition);
        // 从连接池中获取 <- 这里其实是将 DataSource <--> Connection 放到 ThreadLocal 里面
        Connection con = DataSourceUtils.getConnection(dataSource);
        try {
            // 执行 SQL
            con.prepareStatement(CREATE_TABLE_SQL).execute();
            PreparedStatement pstmt = con.prepareStatement(INSERT_SQL);
            pstmt.setString(1, "test");
            pstmt.execute();
            ResultSet resultSet = con.prepareStatement(COUNT_SQL).executeQuery();
            // 打印查询结果
            while(resultSet.next()){ System.out.println(resultSet.getString(1));}
            con.prepareStatement(DROP_TABLE_SQL).execute();
            transactionManager.commit(transactionStatus);
        } catch (Exception e) { // 遇到异常就直接回滚
            transactionManager.rollback(transactionStatus);
        } 
    }
    
    // 设置数据库连接池
    public static DataSource getDataSource(){
        org.apache.commons.dbcp.BasicDataSource basicDataSource = new org.apache.commons.dbcp.BasicDataSource();
        basicDataSource.setDriverClassName("com.mysql.jdbc.Driver");
        basicDataSource.setUrl("jdbc:mysql://localhost:3306/tuomatuo?useUnicode=true&characterEncoding=UTF8");
        basicDataSource.setUsername("root");
        basicDataSource.setPassword("123456");
        return basicDataSource;
    }
    

    在上面的代码中出现了一个新的角色 DataSourceUtils, 这个类主要是通过 TransactionSynchronizationManager 对 ThreadLocal 中存储的 DataSource 的操作; 以上代码主要是如下几步:

    1. 新建数据库连接 DataSource
    2. 新建 DataSourceTransactionManager, 将 DataSource 赋值到其中
    3. 构建默认的事务配置器 TransactionDefinition
    4. 通过 DataSourceTransactionManager 获取事务状态器 TransactionStatus (此时已经将 Connection 等事务配置信息存储到 ThreadLocal 中)
    5. 通过 DataSourceUtils.getConnection(dataSource) 获取存储在 ThreadLocal 中的 Connection, 接着就是直接对 数据库的操作
    6. 直接成功提交事务, 失败的话就会直接回滚事务
    

    PS: 在运行 Demo 时数据库的连接替换成本地环境的参数

    3. Spring Tx 编程式事务 TransactionTemplate

    TransactionTemplate其实只是在 AbstractPlatformTransactionManager上面做了一个简单的封装, 通过传递一个回调函数 TransactionCallback 来执行数据库的操作, 其他部分的操作与直接使用 AbstractPlatformTransactionManager 是一样的

    <!-- 设置目标 -->
    <bean id="testBeanTarget" class="org.springframework.tests.sample.beans.TestBean">
        <property name="name"><value>dependency</value></property>
    </bean>
    
    <!-- 配置 dataSource -->
    <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource">
        <property name="driverClassName" value="${jdbc.driverClassName}"/>
        <property name="url" value="${jdbc.url}"/>
        <property name="username" value="${jdbc.username}"/>
        <property name="password" value="${jdbc.password}"/>
        <property name="validationQuery">
            <value>select 1 from dual</value>
        </property>
    </bean>
    
    <!-- 配置 DataSourceTransactionManager -->
    <bean class="org.springframework.jdbc.datasource.DataSourceTransactionManager" id="transactionManager">
        <property name="dataSource" ref="dataSource"></property>
    </bean>
    
    <!-- 配置 TransactionProxyFactoryBean -->
    <bean id="transactionProxyFactoryBean" class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean">
        <!-- 代理的目标类 -->
        <property name="target"><ref local="testBeanTarget"/></property>
        <!-- 配置 transactionManager -->
        <property name="transactionManager"><ref local="transactionManager"/></property>
        <!-- 设置 transactionAttributes, 这里使用 NameMatchTransactionAttributeSource 来获取 transactionAttribute 信息 -->
        <!-- 在父类 TransactionAspectSupport.invokeWithinTransaction 中会用到 -->
        <property name="transactionAttributes">
            <props>
                <prop key="s*">PROPAGATION_MANDATORY</prop>
                <prop key="setAg*">  PROPAGATION_REQUIRED,readOnly</prop>
                <prop key="set*">PROPAGATION_SUPPORTS</prop>
            </props>
        </property>
    </bean>
    

    从上面的 Demo 中发现, 其实整个配置信息不复杂, 缺点是 TransactionProxyFactoryBean 是针对单个 Target, 若项目中出现非常多需要事务控制的方法, 则需要配置非常多的 TransactionProxyFactoryBean, 所以出现了基于 Tx NameSpace 与 基于注解 @Transactional 的事务

    4. Spring Tx 基于 Tx Namespace 的事务配置

    基于 Tx nameSpace 的事务配置, 其实就是在 BeanFactory 中注入 TransactionInterceptor, NameMatchTransactionAttributeSource , 其中 TransactionInterceptor 是个 MethodInterceptor, 通过对目标对象代理, 而拦截器 TransactionInterceptor 中就是完成事务的操作, 先看一个对应的事务配置信息:

    <!-- 配置拦截器 -->
    <tx:advice id="txAdvice"> <!-- TxAdviceBeanDefinitionParser 解析标签成 org.springframework.transaction.interceptor.TransactionInterceptor -->
        <tx:attributes>       <!-- TxAdviceBeanDefinitionParser 解析标签成 NameMatchTransactionAttributeSource -> String, TransactionAttribute -->
            <tx:method name="get*" read-only="true"/>
            <tx:method name="set*"/>
            <tx:method name="exceptional"/>
        </tx:attributes>
    </tx:advice>
    
    <!-- 配置 AspectJAwareAdvisorAutoProxyCreator 与 DefaultBeanFactoryPointcutAdvisor -->
    <aop:config> <!-- 解析成 DefaultBeanFactoryPointcutAdvisor 与 AspectJExpressionPointcut <- 对应的表达式是 "execution (* com.lami.mhao.service.*.add*(..))" -->
        <aop:advisor pointcut="execution (* com.lami.mhao.service.*.add*(..))" advice-ref="txAdvice"/>
    </aop:config>
    

    从上面的配置我们可以看到, 原来 事务命名空间这种配置其实就是在 BeanFactory 中注入 MethodInterceptor <--TransactionInterceptor, 在 TransactionInterceptor 中通过 PlatformTransactionManager 来进行事务操作, 在 TransactionInterceptor 中的操作如下:

    protected Object invokeWithinTransaction(Method method, Class<?> targetClass, final InvocationCallback invocation)
            throws Throwable {
        // If the transaction attribute is null, the method is non-transactional.
        // 这里读取事务的属性和设置, 通过 TransactionAttributeSource 对象取得
        final TransactionAttribute txAttr = getTransactionAttributeSource().getTransactionAttribute(method, targetClass);
        // 获取 beanFactory 中的 transactionManager
        final PlatformTransactionManager tm = determineTransactionManager(txAttr);
        // 构造方法唯一标识(类, 方法, 如 service.UserServiceImpl.save)
        final String joinpointIdentification = methodIdentification(method, targetClass, txAttr);
    
        /**
         * 这里区分不同类型的 PlatformTransactionManager 因为它们的调用方式不同
         * 对 CallbackPreferringPlatformTransactionManager 来说, 需要回调函数来
         * 实现事务的创建和提交
         * 对于非 CallbackPreferringPlatformTransactionManager 来说, 不需要通过
         * 回调函数来实现事务的创建和提交
         * 像 DataSourceTransactionManager 就不是 CallbackPreferringPlatformTransactionManager
         * 不需要通过回调的方式来使用
         */
        if (txAttr == null || !(tm instanceof CallbackPreferringPlatformTransactionManager)) {
            // Standard transaction demarcation with getTransaction and commit/rollback calls.
            // 这里创建事务, 同时把创建事务过程中得到的信息放到 TransactionInfo 中去 (创建事务的起点)
            TransactionInfo txInfo = createTransactionIfNecessary(tm, txAttr, joinpointIdentification);
            Object retVal = null;
            try {
                // This is an around advice: Invoke the next interceptor in the chain.
                // This will normally result in a target object being invoked.
                // 这里的调用使用处理沿着拦截器链进行, 使最后目标对象的方法得到调用
                retVal = invocation.proceedWithInvocation();
            }
            catch (Throwable ex) {
                // target invocation exception
                // 如果在事务处理方法调用中出现异常, 事务处理如何进行需要根据具体的情况考虑回滚或者提交
                completeTransactionAfterThrowing(txInfo, ex);
                throw ex;
            }
            finally {
                // 这里把与线程绑定的 TransactionInfo 设置为 oldTransactionInfo
                cleanupTransactionInfo(txInfo);
            }
            // 这里通过事务处理器来对事务进行提交
            commitTransactionAfterReturning(txInfo);
            return retVal;
        }
    }
    

    其中非常重要的 TransactionAttribute 是从 NameMatchTransactionAttributeSource(这个类也是通过解析 xml 时注入的) 中获取的, 主流程如下:

    // 对调用的方法进行判断, 判断它是否是事务方法, 如果是事务方法, 那么取出相应的事务配置属性
    @Override
    public TransactionAttribute getTransactionAttribute(Method method, Class<?> targetClass) {
        // 判断当前目标调用的方法与配置的事务方法是否直接匹配
        if (!ClassUtils.isUserLevelMethod(method)) {
            return null;
        }
    
        // Look for direct name match.
        String methodName = method.getName();
        // 通过方法名从 nameMap 中获取 attr
        TransactionAttribute attr = this.nameMap.get(methodName);
        // 如果不能直接匹配, 就通过 PatternMatchUtils 的 simpleMatch 方法来进行匹配判断
        if (attr == null) {
            // Look for most specific name match.
            String bestNameMatch = null;
            for (String mappedName : this.nameMap.keySet()) {
                if (isMatch(methodName, mappedName) &&          // 方法名称匹配 <- 正则匹配
                        (bestNameMatch == null || bestNameMatch.length() <= mappedName.length())) {
                    attr = this.nameMap.get(mappedName);        // 匹配成功, 则直接获取 TransactionAttribute <- 这里的 TransactionAttribute 是在解析 xml 时注入 BeanFactory 的
                    bestNameMatch = mappedName;
                }
            }
        }
    
        return attr;                                            // 返回通过 methodName 获取的 attr
    }
    

    PS: 对应的 Tx Namespace 解析器是 TxAdviceBeanDefinitionParser <- 代码量不多

    5. Spring Tx 基于注解 @Transactional 的事务

    在使用这种形式的事务时, 只需要在配置文件中加上 <tx:annotation-driven />, 接着就可以在方法上加上 @Transactional 就能通过对某个方法内的 SQL 操作进行事务操作, 其中主要的参与者是:

    1. AbstractAdvisorAutoProxyCreator: 自动Advisor收集, 自动代理创建器
    2. AnnotationTransactionAttributeSource: 通过获取&解析方法上@Transactional 来获取 TransactionAttribute 的 TransactionAttributeSource
    3. TransactionInterceptor: 一个 MethodInterceptor, 正真的事务操作其实就是在这个类中, 本质上也是通过 PlatformTransactionManager 来操作的
    4. BeanFactoryTransactionAttributeSourceAdvisor: 一个基于TransactionAttributeSourcePointcut 的 Advisor, 这种 Pointcut 是通过 AnnotationTransactionAttributeSource, SpringTransactionAnnotationParser 解析方法上的 @Transactional 注解获取 TransactionAttribute 来确定是否匹配成功的 Pointcut
    

    <tx:annotation-driven /> 的解析器则是: org.springframework.transaction.config.AnnotationDrivenBeanDefinitionParser, 主要流程如下:

    public static void configureAutoProxyCreator(Element element, ParserContext parserContext) {
        // 若 BeanFactory 中没有注入 自动代理创建器, 则将注入 InfrastructureAdvisorAutoProxyCreator
        AopNamespaceUtils.registerAutoProxyCreatorIfNecessary(parserContext, element);
    
        String txAdvisorBeanName = TransactionManagementConfigUtils.TRANSACTION_ADVISOR_BEAN_NAME;
        // 没有注入过 internalTransactionAdvisor
        if (!parserContext.getRegistry().containsBeanDefinition(txAdvisorBeanName)) {
            Object eleSource = parserContext.extractSource(element);
    
            // Create the TransactionAttributeSource definition.
            RootBeanDefinition sourceDef = new RootBeanDefinition(      // 注入 AnnotationTransactionAttributeSource (TransactionAttribute 提取器, 其主要是通过注释在方法上的 @Transactional 获取 TransactionAttribute)
                    "org.springframework.transaction.annotation.AnnotationTransactionAttributeSource");
            sourceDef.setSource(eleSource);
            sourceDef.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);      // 这里标志 AnnotationTransactionAttributeSource 是 IOC 的基础类 <- 针对基础类不会进行 AOP 等操作
            String sourceName = parserContext.getReaderContext().registerWithGeneratedName(sourceDef);
    
            // Create the TransactionInterceptor definition.            // 注入 TransactionInterceptor, 正真的事务操作就是在这里拦截器里面 <- 主要还是通过 PlatformTransactionManager
            RootBeanDefinition interceptorDef = new RootBeanDefinition(TransactionInterceptor.class);
            interceptorDef.setSource(eleSource);
            interceptorDef.setRole(BeanDefinition.ROLE_INFRASTRUCTURE); // 标志这是个基础类 <- 基础类不会被 AOP 等特性操作
            registerTransactionManager(element, interceptorDef);        // 设置 transactionManager 的 beanName
            interceptorDef.getPropertyValues().add("transactionAttributeSource", new RuntimeBeanReference(sourceName));
            // 注册 TransactionInterceptor, 并使用 Spring 中的定义规则生成 beanName
            String interceptorName = parserContext.getReaderContext().registerWithGeneratedName(interceptorDef);
    
            // 注入 BeanFactoryTransactionAttributeSourceAdvisor, 这个类中的 Pointcut 是 TransactionAttributeSourcePointcut, 而
            // 只要 TransactionAttributeSource 通 Method, Class 中获取 TransactionAttribute 就表示这个方法将被事务操作
            // Create the TransactionAttributeSourceAdvisor definition.
            RootBeanDefinition advisorDef = new RootBeanDefinition(BeanFactoryTransactionAttributeSourceAdvisor.class);
            advisorDef.setSource(eleSource);
            advisorDef.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);     // 标志这是个基础类 <- 基础类不会被 AOP 等特性操作
            // 将 sourceName 的 bean 注入 advisorDef 的 transactionAttributeSource 属性中
            advisorDef.getPropertyValues().add("transactionAttributeSource", new RuntimeBeanReference(sourceName));
            advisorDef.getPropertyValues().add("adviceBeanName", interceptorName);
            // 如果配置了 order 属性, 则加入到 bean 中
            if (element.hasAttribute("order")) {
                advisorDef.getPropertyValues().add("order", element.getAttribute("order"));
            }
            // 注入 BeanFactoryTransactionAttributeSourceAdvisor
            parserContext.getRegistry().registerBeanDefinition(txAdvisorBeanName, advisorDef);
            // 创建 CompositeComponentDefinition <- 其中包含上面的需要注入的 BeanDefinition <-- 组合模式
            CompositeComponentDefinition compositeDef = new CompositeComponentDefinition(element.getTagName(), eleSource);
            compositeDef.addNestedComponent(new BeanComponentDefinition(sourceDef, sourceName));
            compositeDef.addNestedComponent(new BeanComponentDefinition(interceptorDef, interceptorName));
            compositeDef.addNestedComponent(new BeanComponentDefinition(advisorDef, txAdvisorBeanName));
            parserContext.registerComponent(compositeDef);
        }
    }
    
    6. 总结

    在 Spring 中整个事务的操作是围绕 PlatformTransactionManager 来进行操作的, PlatformTransactionManager 中封装了 begin(开启事务), suspend(将先前的事务挂起来 传播级别可能就是PROPAGATION_REQUIRES_NEW), resume(重用先前的事务属性), commit(提交事务), 这些方法就是模版方法, 在 AbstractPlatformTransactionManager中有着整个操作的逻辑, 对应的 ORM 只需要实现对应模版方法就可以(比如 我们最常用的 DataSourceTransactionManager) ; 而需要理解我们最常用的, 基于 @Transactional 的事务, 需要先理解以下 Aop 这样就是非常好理解, 对应的 Pointcut 其实就是通过在方法上获取 @Transactional 的信息来决定!

    7. 参考:

    Spring Aop核心源码分析
    Spring技术内幕
    Spring 揭秘
    Spring 源码深度分析
    开涛 Spring 杂谈
    伤神 Spring 源码分析
    Spring源码情操陶冶

    相关文章

      网友评论

          本文标题:Spring 4.3 源码分析之 编程/声明式事务

          本文链接:https://www.haomeiwen.com/subject/glwstftx.html