@Transactional原理

作者: lesline | 来源:发表于2019-08-11 19:02 被阅读0次

    Spring源码解析之事务篇

    在讲解Transactional原理前,先看下spring对事务的管理都有哪些。

    事务管理

    spring支持编程式事务管理和声明式事务管理两种方式。

    编程式事务管理
    编程式事务使用TransactionTemplate或者直接使用底层的PlatformTransactionManager。
    对于编程式事务管理,spring推荐使用TransactionTemplate。

    声明式事务
    声明式事务是建立在AOP之上的。其本质是对方法前后进行拦截,然后在目标方法开始之前创建或者加入一个事务,在执行完目标方法之后根据执行情况提交或者回滚事务。声明式事务最大的优点就是不需要通过编程的方式管理事务,这样就不需要在业务逻辑代码中掺杂事务管理的代码,只需在配置文件中做相关的事务规则声明(或通过基于@Transactional注解的方式),便可以将事务规则应用到业务逻辑中。
    显然声明式事务管理要优于编程式事务管理,这正是spring倡导的非侵入式的开发方式。声明式事务管理使业务代码不受污染,一个普通的POJO对象,只要加上注解就可以获得完全的事务支持。和编程式事务相比,声明式事务唯一不足地方是,它的最细粒度只能作用到方法级别,无法做到像编程式事务那样可以作用到代码块级别。但是即便有这样的需求,也存在很多变通的方法,比如,可以将需要进行事务管理的代码块独立为方法等等。
    声明式事务管理也有两种常用的方式,一种是基于tx和aop名字空间的xml配置文件,另一种就是基于@Transactional注解。显然基于注解的方式更简单易用,更清爽。

    Transactional原理

    再分析源码前,现从理论上大概分析下:
    纯JDBC操作数据库的基本步骤:

    1. 获取连接 Connection conn = DriverManager.getConnection()
    2. 开启事务conn.setAutoCommit(true/false);
    3. 执行CRUD
    4. 提交事务/回滚事务 conn.commit() / conn.rollback();
    5. 关闭连接 conn.close();

    spring开启事务

    spring开启事务主要有两种方式

    spring方式

    开启事务注解标记@Transactional
    Spring在jdbc中提供了一个事务管理组件DataSourceTransactionManager

    <!-- 配置事务管理组件 -->
    <bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="dataSource”> 
    </bean>
    <!-- 开启事务注解标记@Transactional -->
    <tx:annotation-driven transaction-manager=“txManager" />  
    

    配置上面的信息后,Spring在初始化包含Transactional注解的类时,会自动生成这些类的代理,并放置再容器中,以便备用。

    spring boot

    spring boot中打开事务的几种方式:
    1、自动装载
    spring-boot-autoconfigure jar中 spring.factories
    org.springframework.boot.autoconfigure.EnableAutoConfiguration=org.springframework.boot.autoconfigure.transaction.TransactionAutoConfiguration
    @TransactionAutoConfiguration->EnableTransactionManagement
    2、手功启动事务管理 @EnableTransactionManagemen
    @EnableTransactionManagemen ->TransactionManagementConfigurationSelector->ProxyTransactionManagementConfiguration->TransactionInterceptor

    具体实现:

    ::Transactional 实现事务管理是通过TransactionInterceptor拦截器工作的。::

    主要关注两点:
    ::Spring 会调用TransactionInterceptor在目标方法执行前后进行拦截::
    ::获取数据库连接是通过当前线程获取的,同一线程获取的连接是同一个::

    生成TransactionInterceptor切面类

    我们以spring开启事务方式为例,
    spring tx的入口就是在TxAdviceBeanDefinitionParser这里将解析tx的配置,生成TransactionInterceptor对象,这个也就是一个普通的切面类,只要符合AOP规则的调用都会进入此切面。

    解析<tx:annotation-driven/>配置

    public class TxNamespaceHandler extends NamespaceHandlerSupport {
        static final String /TRANSACTION_MANAGER_ATTRIBUTE/= "transaction-manager";
        static final String /DEFAULT_TRANSACTION_MANAGER_BEAN_NAME/= "transactionManager";
        @Override
        public void init() {
            registerBeanDefinitionParser("advice", new TxAdviceBeanDefinitionParser());
          //对<tx:annotation-driven/>标签的解析
            registerBeanDefinitionParser("annotation-driven", new AnnotationDrivenBeanDefinitionParser());
            registerBeanDefinitionParser("jta-transaction-manager", new JtaTransactionManagerBeanDefinitionParser());
        }
    }
    

    AnnotationDrivenBeanDefinitionParser类中会创建并注册以下三个类:

    1. TransactionInterceptor:MethodInterceptor-Advice
    2. BeanFactoryTransactionAttributeSourceAdvisor: PointcutAdvisor
    3. AnnotationTransactionAttributeSource: Pointcut
      并且组装了这三个类的关系,关系如下:
        BeanFactoryTransactionAttributeSourceAdvisor advisor = new BeanFactoryTransactionAttributeSourceAdvisor();
        advisor.setTransactionAttributeSource(new AnnotationTransactionAttributeSource());
        advisor.setAdvice(new TransactionInterceptor());
    

    关于PointcutAdvisor与MethodInterceptor的使用方式,参考:spring aop使用 - 简书

    具体实现:
    BeanFactoryTransactionAttributeSourceAdvisor:实现了PointcutAdvisor接口的getPointcut()方法
    TransactionInterceptor:实现了MethodInterceptor
    AnnotationTransactionAttributeSource:通过调用getTransactionAttribute方法判断是否有@Transactional
    TransactionAttributeSourcePointcut :实现MethodMatcher接口的matches方法,用于判断方法是否有@Transactional,内部使用了AnnotationTransactionAttributeSource
    Spring AOP容器为使用@Transactional注解的类创建代理,在执行代理类的目标方法时,会调用Advisor的getAdvice获取MethodInterceptor并执行其invoke方法,BeanFactoryTransactionAttributeSourceAdvisor的getAdvice方法会返回TransactionInterceptor,它实现了MethodInterceptor。
    TransactionInterceptor类中invoke方法为:

    @Override
    @Nullable
    public Object invoke(final MethodInvocation invocation) throws Throwable {
            return invokeWithinTransaction(invocation.getMethod(), targetClass, invocation::proceed);
    }
    

    最终会调用TransactionAspectSupport中的invokeWithinTransaction方法:

    @Nullable
    protected Object invokeWithinTransaction(Method method, @Nullable Class<?> targetClass,
            final InvocationCallback invocation) throws Throwable {
    
        // If the transaction attribute is null, the method is non-transactional.
        TransactionAttributeSource tas = getTransactionAttributeSource();
        final TransactionAttribute txAttr = (tas != null ? tas.getTransactionAttribute(method, targetClass) : null);
        final PlatformTransactionManager tm = determineTransactionManager(txAttr);
        final String joinpointIdentification = methodIdentification(method, targetClass, txAttr);
    
        if (txAttr == null || !(tm instanceof CallbackPreferringPlatformTransactionManager)) {
            // Standard transaction demarcation with getTransaction and commit/rollback calls.
            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 {
                cleanupTransactionInfo(txInfo);
            }
            commitTransactionAfterReturning(txInfo);
            return retVal;
        }
    }
    

    从上可以看出,在invokeWithinTransaction方法中有完整的事务管理。

    获取数据库连接

    接着我们看下如何开创建事务,进入createTransactionIfNecessary方法

    protected TransactionInfo createTransactionIfNecessary(@Nullable PlatformTransactionManager tm,
            @Nullable TransactionAttribute txAttr, final String joinpointIdentification) {
    
        // If no name specified, apply method identification as transaction name.
        if (txAttr != null && txAttr.getName() == null) {
            txAttr = new DelegatingTransactionAttribute(txAttr) {
                @Override
                public String getName() {
                    return joinpointIdentification;
                }
            };
        }
    
        TransactionStatus status = null;
        if (txAttr != null) {
            if (tm != null) {
                status = tm.getTransaction(txAttr);//此方法获取数据库连接
            }
            else {
                if (logger.isDebugEnabled()) {
                    logger.debug("Skipping transactional joinpoint [" + joinpointIdentification +
                            "] because no transaction manager has been configured");
                }
            }
        }
        return prepareTransactionInfo(tm, txAttr, joinpointIdentification, status);
    }
    

    getTransaction方法的实现由DataSourceTransactionManager类提供,
    DataSourceTransactionManager调用父类AbstractPlatformTransactionManager的实现,如下:

    public abstract class AbstractPlatformTransactionManager implements PlatformTransactionManager, Serializable {
    
    @Override
    public final TransactionStatus getTransaction(@Nullable TransactionDefinition definition) throws TransactionException {
        Object transaction = doGetTransaction();
        doBegin(transaction, definition);
        prepareSynchronization(status, definition);
        return status;
        }
    }
    

    doGetTransaction为DataSourceTransactionManager中方法,用于获取数据库连接。

    org.springframework.jdbc.datasource.DataSourceTransactionManager
    @Override
    protected Object doGetTransaction() {
        DataSourceTransactionObject txObject = new DataSourceTransactionObject();
        txObject.setSavepointAllowed(isNestedTransactionAllowed());
        ConnectionHolder conHolder =
                (ConnectionHolder) TransactionSynchronizationManager./getResource/(obtainDataSource());
        txObject.setConnectionHolder(conHolder, false);
        return txObject;
    }
    
    

    TransactionSynchronizationManager中getResource方法:
    resources中存的就是当前线程池与当前线程中的一个连接,以后,这个线程中获取连接时都会从获取同一个连接。
    ::注意:resources是用ThreadLocal保存的,这也就说明了为什么开户新的线程会获取一个新的连接,也有跟之前事务没有关系了::

    public abstract class TransactionSynchronizationManager {
     //resources中存的就是当前线程池与当前线程中的一个连接,以后,这个线程中获取连接时都会从获取同一个连接。
       private static final ThreadLocal<Map<Object, Object>> resources =
             new NamedThreadLocal<Map<Object, Object>>("Transactional resources");
     
       /**
        * Retrieve a resource for the given key that is bound to the current thread.
        * @param key the key to check (usually the resource factory)
        * @return a value bound to the current thread (usually the active
        * resource object), or {@code null} if none
        * @see ResourceTransactionManager#getResourceFactory()
        */
       public static Object getResource(Object key) {
          Object actualKey = TransactionSynchronizationUtils.unwrapResourceIfNecessary(key);
          Object value = doGetResource(actualKey);
          return value;
       }
     
       /**
        * Actually check the value of the resource that is bound for the given key.
        */
       private static Object doGetResource(Object actualKey) {
          Map<Object, Object> map = resources.get();
          if (map == null) {
             return null;
          }
          Object value = map.get(actualKey);
          // Transparently remove ResourceHolder that was marked as void...
          if (value instanceof ResourceHolder && ((ResourceHolder) value).isVoid()) {
             map.remove(actualKey);
             // Remove entire ThreadLocal if empty...
             if (map.isEmpty()) {
                resources.remove();
             }
             value = null;
          }
          return value;
       }
    }
    

    DataSourceUtils

    别外也可通过DataSourceUtils类直接获取连接:

    public abstract class DataSourceUtils {
    
       public static Connection getConnection(DataSource dataSource) throws CannotGetJdbcConnectionException {
          try {
             return doGetConnection(dataSource);
          }
          catch (SQLException ex) {
             throw new CannotGetJdbcConnectionException("Could not get JDBC Connection", ex);
          }
       }
        
       public static Connection doGetConnection(DataSource dataSource) throws SQLException {
           ConnectionHolder conHolder = (ConnectionHolder) TransactionSynchronizationManager.getResource(dataSource);
           if (conHolder != null && (conHolder.hasConnection() || conHolder.isSynchronizedWithTransaction())) {
              conHolder.requested();
              if (!conHolder.hasConnection()) {
                 logger.debug("Fetching resumed JDBC Connection from DataSource");
                 conHolder.setConnection(dataSource.getConnection());
              }
              return conHolder.getConnection();
           }
        
           Connection con = dataSource.getConnection();
           ......
    
           return con;
        }
    }
    

    参考:

    Spring事务实现原理详解 - 爱宝贝丶的个人空间 - 开源中国
    Spring AOP和事务的相关陷阱 - lanhz - 博客园
    Spring事务源码阅读笔记 - 活在夢裡 - 博客园

    相关文章

      网友评论

        本文标题:@Transactional原理

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