源码剖析@Transactional注解

654 阅读8分钟

PS:本人从事JavaWeb行业不足3年的理解,也是人生中的第一篇技术文章,简单说一下自己对于该注解的理解,不足处希望大佬们指点

想必大家对于@Transactional注解都不陌生,我们通常使用的时候不知道他在后面默默付出了些什么,接下来我们一起走进@Transactional

1.@Transactional的作用

@Transactional 可以作用于接口、接口方法、类以及类方法上。当作用于类上时,该类的所有 public 方法将都具有该类型的事务属性,同时,我们也可以在方法级别使用该标注来覆盖类级别的定义。

2.@Transactional注解中的参数

我们戳进@Transactional里面会看到

@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
public @interface Transactional {

    //可选的限定描述符,指定使用的事务管理器
    String value() default "";
    
    //可选的事务传播行为,具体看Propagation中定义的属性
    Propagation propagation() default Propagation.REQUIRED;

    //可选的事务隔离级别
    Isolation isolation() default Isolation.DEFAULT;

    //事务超时时间设置
    int timeout() default TransactionDefinition.TIMEOUT_DEFAULT;

    //读写或只读事务,默认读写
    boolean readOnly() default false;

    //导致事务回滚的异常类数组,
    //注意项:即使自定义异常,也必须继承自Throwable
    Class<? extends Throwable>[] rollbackFor() default {};
    
    //导致事务回滚的异常类名字数组
    //类名数组,必须继承自Throwable
    String[] rollbackForClassName() default {};

    //不会导致事务回滚的异常类数组
    //Class对象数组,必须继承自Throwable
    Class<? extends Throwable>[] noRollbackFor() default {};

    //不会导致事务回滚的异常类名字数组
    //类名数组,必须继承自Throwable
    String[] noRollbackForClassName() default {};

}

顺带提一下事物的4个特性

事物特性参考源处:https://www.jianshu.com/p/bcfd48c4cbf4

1.原子性:对数据进行操作的时候,要么全都执行,要么全都不执行。

2.一致性:和原子性是分不开的,事务执行成功,说明全部都执行了,那么就使数据库从一个一致性状态变到另一个一致性状了。

3.隔离性: 一个事务的执行不能被其他事务干扰

4.持续性: 事务一旦提交成功,那么数据库里的数据就会永久性的改变。

3.@Transactional注解的实现

由上面看到的属性及注释我们进注解的实现源码看一下吧

image.png

3.1 @Transactional核心配置

在上图我们能看到这些,其中核心配置的是AnnotationTransactionAttributeSource(注解事物属性配置)这个类。我们能看到该类继承了抽象类AbstractFallbackTransactionAttributeSource,这个类实现 TransactionAttributeSource接口,重写了getTransactionAttribute方法,我们来看看这个方法实现了什么,附上官方源码及注释

/**
 * Determine the transaction attribute for this method invocation.
 * <p>Defaults to the class's transaction attribute if no method attribute is found.
 * @param method the method for the current invocation (never {@code null})
 * @param targetClass the target class for this invocation (may be {@code null})
 * @return TransactionAttribute for this method, or {@code null} if the method
 * is not transactional
 */
public TransactionAttribute getTransactionAttribute(Method method, Class<?> targetClass) {
   // First, see if we have a cached value.
   Object cacheKey = getCacheKey(method, targetClass);
   Object cached = this.attributeCache.get(cacheKey);
   if (cached != null) {
      // Value will either be canonical value indicating there is no transaction attribute,
      // or an actual transaction attribute.
      if (cached == NULL_TRANSACTION_ATTRIBUTE) {
         return null;
      }
      else {
         return (TransactionAttribute) cached;
      }
   }
   else {
      // We need to work it out.
      TransactionAttribute txAtt = computeTransactionAttribute(method, targetClass);
      // Put it in the cache.
      if (txAtt == null) {
         this.attributeCache.put(cacheKey, NULL_TRANSACTION_ATTRIBUTE);
      }
      else {
         if (logger.isDebugEnabled()) {
            Class<?> classToLog = (targetClass != null ? targetClass : method.getDeclaringClass());
            logger.debug("Adding transactional method '" + classToLog.getSimpleName() + "." +
                  method.getName() + "' with attribute: " + txAtt);
         }
         this.attributeCache.put(cacheKey, txAtt);
      }
      return txAtt;
   }
}

可以看到官方的注释已经解释的很清楚,确定此方法调用的事务属性。如果没有找到方法属性,则默认为类的事务属性。该方法返回的是一个接口,该接口继承了TransactionDefinition,这个类里面定义了事物的传播级别,我们一起来看一下

3.2 Spring中事物的传播级别

/**
 * Interface that defines Spring-compliant transaction properties.
 * Based on the propagation behavior definitions analogous to EJB CMT attributes.
 *
 * <p>Note that isolation level and timeout settings will not get applied unless
 * an actual new transaction gets started. As only {@link #PROPAGATION_REQUIRED},
 * {@link #PROPAGATION_REQUIRES_NEW} and {@link #PROPAGATION_NESTED} can cause
 * that, it usually doesn't make sense to specify those settings in other cases.
 * Furthermore, be aware that not all transaction managers will support those
 * advanced features and thus might throw corresponding exceptions when given
 * non-default values.
 *
 * <p>The {@link #isReadOnly() read-only flag} applies to any transaction context,
 * whether backed by an actual resource transaction or operating non-transactionally
 * at the resource level. In the latter case, the flag will only apply to managed
 * resources within the application, such as a Hibernate {@code Session}.
 *
 * @author Juergen Hoeller
 * @since 08.05.2003
 * @see PlatformTransactionManager#getTransaction(TransactionDefinition)
 * @see org.springframework.transaction.support.DefaultTransactionDefinition
 * @see org.springframework.transaction.interceptor.TransactionAttribute
 */
public interface TransactionDefinition {

   /**
    *默认的spring事务传播级别,使用该级别的特点是,如果上下文中已经存在事务,那么就加入到事务中执行,
    *如果当前上下文中不存在事务,则新建事务执行。所以这个级别通常能满足处理大多数的业务场景。
    */
   int PROPAGATION_REQUIRED = 0;

   /**
    *从字面意思就知道,supports,支持,该传播级别的特点是,如果上下文存在事务,则支持事务加入事务,
    *如果没有事务,则使用非事务的方式执行。所以说,并非所有的包在transactionTemplate.execute中的代码都会有事务支持。
    *这个通常是用来处理那些并非原子性的非核心业务逻辑操作。应用场景较少。
    */
   int PROPAGATION_SUPPORTS = 1;

   /**
    *该级别的事务要求上下文中必须要存在事务,否则就会抛出异常!
    *配置该方式的传播级别是有效的控制上下文调用代码遗漏添加事务控制的保证手段。
    *比如一段代码不能单独被调用执行,但是一旦被调用,就必须有事务包含的情况,就可以使用这个传播级别。
    */
   int PROPAGATION_MANDATORY = 2;

   /**
    *从字面即可知道,new,每次都要一个新事务,该传播级别的特点是,每次都会新建一个事务,
    *并且同时将上下文中的事务挂起,执行当前新建事务完成以后,上下文事务恢复再执行。
    */
   int PROPAGATION_REQUIRES_NEW = 3;

   /**
    *这个也可以从字面得知,not supported ,不支持,当前级别的特点就是上下文中存在事务,
    *则挂起事务,执行当前逻辑,结束后恢复上下文的事务。
    */
   int PROPAGATION_NOT_SUPPORTED = 4;

   /**
    *该事务更严格,上面一个事务传播级别只是不支持而已,有事务就挂起,
    *而PROPAGATION_NEVER传播级别要求上下文中不能存在事务,一旦有事务,就抛出runtime异常,强制停止执行!
    *这个级别上辈子跟事务有仇。
    */
   int PROPAGATION_NEVER = 5;

   /**
    *字面也可知道,nested,嵌套级别事务。
    *该传播级别特征是,如果上下文中存在事务,则嵌套事务执行,如果不存在事务,则新建事务。
    */
   int PROPAGATION_NESTED = 6;

   /**
    *默认隔离级别 
    */
   int ISOLATION_DEFAULT = -1;

   int ISOLATION_READ_UNCOMMITTED = Connection.TRANSACTION_READ_UNCOMMITTED;

   int ISOLATION_READ_COMMITTED = Connection.TRANSACTION_READ_COMMITTED;

   int ISOLATION_REPEATABLE_READ = Connection.TRANSACTION_REPEATABLE_READ;

   int ISOLATION_SERIALIZABLE = Connection.TRANSACTION_SERIALIZABLE;

   int TIMEOUT_DEFAULT = -1;

   int getPropagationBehavior();

   int getIsolationLevel();

   int getTimeout();

   boolean isReadOnly();

   String getName();

}

以上解释出处(大概解释都借鉴出来了,感兴趣的同学可以看一下):blog.csdn.net/edward0830l…

3.3 @Transactional的事物拦截器

我们都知道事物利用了AOP思想,上面戳注解的时候我们看到一个类:TransactionProxyFactoryBean(事物代理工厂Bean),话不多说,我们看源码

@SuppressWarnings("serial")
public class TransactionProxyFactoryBean extends AbstractSingletonProxyFactoryBean
      implements BeanFactoryAware {

   private final TransactionInterceptor transactionInterceptor = new TransactionInterceptor();

   private Pointcut pointcut;


   /**
    * Set the transaction manager. This will perform actual
    * transaction management: This class is just a way of invoking it.
    * @see TransactionInterceptor#setTransactionManager
    */
   public void setTransactionManager(PlatformTransactionManager transactionManager) {
      this.transactionInterceptor.setTransactionManager(transactionManager);
   }

   /**
    * Set properties with method names as keys and transaction attribute
    * descriptors (parsed via TransactionAttributeEditor) as values:
    * e.g. key = "myMethod", value = "PROPAGATION_REQUIRED,readOnly".
    * <p>Note: Method names are always applied to the target class,
    * no matter if defined in an interface or the class itself.
    * <p>Internally, a NameMatchTransactionAttributeSource will be
    * created from the given properties.
    * @see #setTransactionAttributeSource
    * @see TransactionInterceptor#setTransactionAttributes
    * @see TransactionAttributeEditor
    * @see NameMatchTransactionAttributeSource
    */
   public void setTransactionAttributes(Properties transactionAttributes) {
      this.transactionInterceptor.setTransactionAttributes(transactionAttributes);
   }

   /**
    * Set the transaction attribute source which is used to find transaction
    * attributes. If specifying a String property value, a PropertyEditor
    * will create a MethodMapTransactionAttributeSource from the value.
    * @see #setTransactionAttributes
    * @see TransactionInterceptor#setTransactionAttributeSource
    * @see TransactionAttributeSourceEditor
    * @see MethodMapTransactionAttributeSource
    * @see NameMatchTransactionAttributeSource
    * @see org.springframework.transaction.annotation.AnnotationTransactionAttributeSource
    */
   public void setTransactionAttributeSource(TransactionAttributeSource transactionAttributeSource) {
      this.transactionInterceptor.setTransactionAttributeSource(transactionAttributeSource);
   }

   /**
    * Set a pointcut, i.e a bean that can cause conditional invocation
    * of the TransactionInterceptor depending on method and attributes passed.
    * Note: Additional interceptors are always invoked.
    * @see #setPreInterceptors
    * @see #setPostInterceptors
    */
   public void setPointcut(Pointcut pointcut) {
      this.pointcut = pointcut;
   }

   /**
    * This callback is optional: If running in a BeanFactory and no transaction
    * manager has been set explicitly, a single matching bean of type
    * {@link PlatformTransactionManager} will be fetched from the BeanFactory.
    * @see org.springframework.beans.factory.BeanFactory#getBean(Class)
    * @see org.springframework.transaction.PlatformTransactionManager
    */
   public void setBeanFactory(BeanFactory beanFactory) {
      this.transactionInterceptor.setBeanFactory(beanFactory);
   }


   /**
    * Creates an advisor for this FactoryBean's TransactionInterceptor.
    */
   @Override
   protected Object createMainInterceptor() {
      this.transactionInterceptor.afterPropertiesSet();
      if (this.pointcut != null) {
         return new DefaultPointcutAdvisor(this.pointcut, this.transactionInterceptor);
      }
      else {
         // Rely on default pointcut.         return new TransactionAttributeSourceAdvisor(this.transactionInterceptor);
      }
   }
}

根据以上代码我们能看到该类继承了单例模式的代理工厂抽象类AbstractSingletonProxyFactoryBean,实现了BeanFactoryAware接口的createMainInterceptor方法,这个方法会创建出一个通知者对象用于通知事物。看到这里会发现具体的实现到底在哪啊?不要急我们慢慢看!

我们戳注解的时候会发现一个接口TransactionCallback,顺着进去我们可以看到

/**
 * Callback interface for transactional code. Used with {@link TransactionTemplate}'s
 * {@code execute} method, often as anonymous class within a method implementation.
 *
 * <p>Typically used to assemble various calls to transaction-unaware data access
 * services into a higher-level service method with transaction demarcation. As an
 * alternative, consider the use of declarative transaction demarcation (e.g. through
 * Spring's {@link org.springframework.transaction.annotation.Transactional} annotation).
 *
 * @author Juergen Hoeller
 * @since 17.03.2003
 * @see TransactionTemplate
 * @see CallbackPreferringPlatformTransactionManager
 */
public interface TransactionCallback<T> {

   /**
    * Gets called by {@link TransactionTemplate#execute} within a transactional context.
    * Does not need to care about transactions itself, although it can retrieve
    * and influence the status of the current transaction via the given status
    * object, e.g. setting rollback-only.
    *
    * <p>Allows for returning a result object created within the transaction, i.e.
    * a domain object or a collection of domain objects. A RuntimeException thrown
    * by the callback is treated as application exception that enforces a rollback.
    * Any such exception will be propagated to the caller of the template, unless
    * there is a problem rolling back, in which case a TransactionException will be
    * thrown.
    *
    * @param status associated transaction status
    * @return a result object, or {@code null}
    * @see TransactionTemplate#execute
    * @see CallbackPreferringPlatformTransactionManager#execute
    */
   T doInTransaction(TransactionStatus status);

}

接口上访的注释有这么一行话 Callback interface for transactional code. Used with {@link TransactionTemplate}'s {@code execute} method, often as anonymous class within a method implementation. ,可以看到注释中说事务代码的回调接口在TransactionTemplate的execute方法,所以我们就进这个TransactionTemplate类里面去看一看。

上面我就不列出来了,感兴趣的同学可以自己去看看,我们直接来看execute方法。

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
         rollbackOnException(status, ex);
         throw ex;
      }
      catch (Error err) {
         // Transactional code threw error -> rollback
         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;
   }
}

以上代码我们看到一直都是transactionManager这位兄弟在做事,直接把所有事都干完了。

从中能完美的体会到AOP思想的使用,以代理的方式去把你的业务代码进行增强扩展。