Spring事务实现之编程式事务


编程式事务(TranscationTemplate)

废话不多说,直接看配置文件。

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context"
    xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx"
    xsi:schemaLocation="http://www.springframework.org/schema/beans 
           http://www.springframework.org/schema/beans/spring-beans-3.1.xsd
           http://www.springframework.org/schema/context
           http://www.springframework.org/schema/context/spring-context-3.1.xsd
           http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.1.xsd
           http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.1.xsd">
    <context:component-scan base-package="com.wangcc.transcation.template" />
  <!-- 引入配置文件 -->  
    <bean id="propertyConfigurer"  
        class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">  
        <property name="location" value="classpath:jdbc.properties" />  
        <property name="ignoreUnresolvablePlaceholders" value="true" /> 
         </bean>  
         <!-- 配置数据源 -->
    <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource"
        init-method="init" destroy-method="close">
        <property name="driverClassName" value="${driver}" />  
        <property name="url" value="${url}" />  
        <property name="username" value="${username}" />  
        <property name="password" value="${password}" />  
        <!-- 初始化连接大小 -->  
        <property name="initialSize" value="${initialSize}"></property>  
        <!-- 连接池最大数量 -->  
        <property name="maxActive" value="${maxActive}"></property>  
        <!-- 连接池最大空闲 -->  
        <property name="maxIdle" value="${maxIdle}"></property>  
        <!-- 连接池最小空闲 -->  
        <property name="minIdle" value="${minIdle}"></property>  
        <!-- 获取连接最大等待时间 -->  
        <property name="maxWait" value="${maxWait}"></property>  
        <!-- 
        Druid内置提供一个StatFilter,用于统计监控信息。

        StatFilter的别名是stat,这个别名映射配置信息保存在:

        druid-xxx.jar!/META-INF/druid-filter.properties。
        在spring中使用别名配置方式如下
         -->
             <!-- 配置监控统计拦截的filters,去掉后监控界面sql无法统计 -->  

        <property name="filters" value="stat" />

        <property name="timeBetweenEvictionRunsMillis" value="3000" />
        <property name="minEvictableIdleTimeMillis" value="300000" />
        <property name="validationQuery" value="SELECT 'x'" />
        <property name="testWhileIdle" value="true" />
        <property name="testOnBorrow" value="false" />
        <property name="testOnReturn" value="false" />
        <property name="poolPreparedStatements" value="true" />
        <property name="maxPoolPreparedStatementPerConnectionSize"
            value="20" />
    </bean>
    <!-- mybatis配置 -->
 <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">  
        <property name="dataSource" ref="dataSource" />  
        <!-- 自动扫描mapping.xml文件 -->  
        <property name="mapperLocations" value="classpath*:mapper/*.xml"></property>
        <property name="configLocation" value="classpath:mybatis-config.xml"></property>
        <!-- com.ulic.gpolicyutils -->
        <property name="typeAliasesPackage" value="com.wangcc.transcation.template.entity"></property>
    </bean>  
        <!-- DAO接口所在包名,Spring会自动查找其下的类 -->  

      <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">  
        <property name="basePackage" value="com.wangcc.transcation.template.dao" />  
        <property name="sqlSessionFactoryBeanName" value="sqlSessionFactory"></property>  
    </bean>  
      <!-- (事务管理)transaction manager, use JtaTransactionManager for global tx -->  
    <bean id="transactionManager"  
        class="org.springframework.jdbc.datasource.DataSourceTransactionManager">  
        <property name="dataSource" ref="dataSource" />  
    </bean>  
    <bean id="transactionTemplate" class="org.springframework.transaction.support.TransactionTemplate">
        <property name="transactionManager" ref="transactionManager"/>
          <!--ISOLATION_DEFAULT 表示由使用的数据库决定  -->  
        <property name="isolationLevelName" value="ISOLATION_DEFAULT"/>  
        <property name="propagationBehaviorName" value="PROPAGATION_REQUIRED"/> 
    </bean>
</beans>

在进行事务管理之前的配置可以忽略不看,就是进行了数据源以及Spring与Mybatis结合的一些配置。

我们在配置TransactionManager的时候注入了dataSource数据源属性。

然后在配置TransactionTemplate的时候又将TransactionManager注入其中,并且配置了隔离级别与事务传播级别。注意,这两个关键的参数是在TransactionTemplate中指定的。

那我们很明显的可以知道TransactionTemplate这个类就是我们实现编程式事务关键的核心类。

我们来看下我们是怎么使用调用这个类的。

@Service
public class AccountService {
    @Autowired
    private AccountDao accountDao;
    @Autowired
    private TransactionTemplate template;

    public void transfer(String inUserName, String outUserName, Double money) {
        template.execute(new TransactionCallbackWithoutResult() {
            @Override
            protected void doInTransactionWithoutResult(TransactionStatus transactionStatus) {
                accountDao.reduceMoney(outUserName, money);
                int i = 1 / 0;
                accountDao.addMoney(inUserName, money);
            }
        });
    }
}

非常简单,当我们想要对一个方法使用编程式的事务的时候,我们只需要调用TransactionTemplate的execute方法即可实现编程式的事务,非常的简单,但是你也可以发现,耦合度非常非常的高,这也是为什么我们之后大多会使用切面来声明事务的原因。

要实现编程式的事务,只需要调用execute方法,然后使用一个匿名类来构造execute方法的入参,入参类型是TransactionCallback,接着将我们具体的方法内容放置在该类型中的doInTransaction方法中。但是你看上述的代码,并不是实现了doInTransaction方法呀,别着急TransactionCallbackWithoutResult是TransactionCallback的一个子类,doInTransactionWithoutResult是该类重写的doInTransaction调用的一个方法。

编程式的事务实现起来非常简单,但是很显然耦合度过高,已不推荐使用,也应该是实现Spring事务的唯一一种没有使用到代理模式思想的实现方式。而其他的所谓的三种实现Spring事务的方法,核心都是动态代理,当然你也可以说是拦截器,但是归根节点还是动态代理。

编程式事务源码解析

看完了如何实现编程式事务,我们就来简单分析下编程式事务实现的原理把。

显然我们分析的核心肯定是TransactionTemplate类,我们先看这个类的声明

public class TransactionTemplate extends DefaultTransactionDefinition
        implements TransactionOperations, InitializingBean {

我们发现他继承了DefaultTransactionDefinition这个类

实现了InitializingBean接口,这个接口已经很熟悉了,会在创建bean之后执行他的afterPropertiesSet方法。

还实现了TransactionOperations接口,正是这个接口,定义了我们刚才多次提到的execute方法。

  • DefaultTransactionDefinition

我们先来看看他的父类DefaultTransactionDefinition

public class DefaultTransactionDefinition implements TransactionDefinition, Serializable {
    public static final String PREFIX_PROPAGATION = "PROPAGATION_";

    /** Prefix for the isolation constants defined in TransactionDefinition */
    public static final String PREFIX_ISOLATION = "ISOLATION_";

    /** Prefix for transaction timeout values in description strings */
    public static final String PREFIX_TIMEOUT = "timeout_";

    /** Marker for read-only transactions in description strings */
    public static final String READ_ONLY_MARKER = "readOnly";


    /** Constants instance for TransactionDefinition */
    static final Constants constants = new Constants(TransactionDefinition.class);

    private int propagationBehavior = PROPAGATION_REQUIRED;

    private int isolationLevel = ISOLATION_DEFAULT;

    private int timeout = TIMEOUT_DEFAULT;

    private boolean readOnly = false;

    private String name;
    //....
    }

我们发现他给出了默认的isolationLevel(事务隔离级别),propagationBehavior(事务传播属性),timeout(超时时间),readOnly(是否只读)等等。

而所有的事务隔离级别,事务传播属性值的定义都在DefaultTransactionDefinition实现的接口TransactionDefinition中。

我们在DefaultTransactionDefinition中看到有isolationLevel和propagationBehavior这两个Fields。所以这个时候我们就明白了在配置文件中指定的事务隔离级别和事务传播属性就是DefaultTransactionDefinition中的这两个属性。

关于父类DefaultTransactionDefinition的介绍就到这里,主要就是给出了默认的事务隔离级别和事务传播属性,并且暴露出属性和给出值让用户可以自定义事务隔离级别和事务传播属性等。

  • InitializingBean

    TransactionTemplate实现了InitializingBean接口,那我们就有必要看看实现这个接口的目的是什么。

    @Override
    public void afterPropertiesSet() {
        if (this.transactionManager == null) {
            throw new IllegalArgumentException("Property 'transactionManager' is required");
        }
    }

    方法很简单,目的也一目了然,就是在创建这个Bean之后,一定要向其中注入了transactionManager属性,否则就要报错,他必须有transactionManager属性,因为真正去实现事务的类还是在transactionManager中。

  • TransactionOperations

    这个接口定义了execute方法。

    也是我们在程序中直接调用的方法,我们需要重点关注。

    @Override
    public <T> T execute(TransactionCallback<T> action) throws TransactionException {
        if (this.transactionManager instanceof CallbackPreferringPlatformTransactionManager) {
            return ((CallbackPreferringPlatformTransactionManager) this.transactionManager).execute(this, action);
        }
        else {
            //得到事务
            TransactionStatus status = this.transactionManager.getTransaction(this);
            T result;
            try {
                //开启事务,调用回调方法
                result = action.doInTransaction(status); //即我们具体业务逻辑实现的地方
            }
            catch (RuntimeException ex) {
                // Transactional code threw application exception -> rollback
                //如果是RuntimeException异常,回顾
                rollbackOnException(status, ex);
                throw ex;
            }
            catch (Error err) {
                // Transactional code threw error -> rollback
                //如果是Error 虚拟机产生的异常,回滚
                rollbackOnException(status, err);
                throw err;
            }
            catch (Exception ex) {
                // Transactional code threw unexpected exception -> rollback
                rollbackOnException(status, ex);
                throw new UndeclaredThrowableException(ex, "TransactionCallback threw undeclared checked exception");
            }
            //提交事务
            this.transactionManager.commit(status);
            return result;
        }
    }

展开阅读全文

没有更多推荐了,返回首页