Spring 事务源码(4)—BeanFactoryTransactionAttributeSourceAdvisor注解事务通知器源码解析

  基于最新Spring 5.x,详细介绍了Spring 事务源码,包括BeanFactoryTransactionAttributeSourceAdvisor注解事务通知器源码解析。

  此前,我们学习了< tx:annotation-driven/>标签@EnableTransactionManagement注解的解析源码,他们的都会向IoC容器注入一些bena定义,这些bean将会在后续Spring事务的处理中发挥不同的作用, 现在我们来学习这些bean,首先我们学习BeanFactoryTransactionAttributeSourceAdvisor

Spring 事务源码 系列文章

Spring 5.x 源码汇总

Spring 事务源码(1)—<tx:advice/>事务标签源码解析

Spring 事务源码(2)—<tx:annotation-driven/>事务标签源码解析

Spring 事务源码(3)—@EnableTransactionManagement事务注解源码解析

Spring 事务源码(4)—BeanFactoryTransactionAttributeSourceAdvisor注解事务通知器源码解析

Spring 事务源码(5)—TransactionInterceptor事务拦截器与事务的AOP增强实现

Spring 事务源码(6)—createTransactionIfNecessary处理事务属性并尝试创建事务【两万字】

Spring 事务源码(7)—事务的completeTransactionAfterThrowing回滚、commitTransactionAfterReturning提交以及事务源码总结【一万字】

1 BeanFactoryTransactionAttributeSourceAdvisor的概述

  无论是<tx:annotation-driven/>标签还是@EnableTransactionManagement注解,都会向容器注入一个BeanFactoryTransactionAttributeSourceAdvisor的bean定义
  它的uml类图如下:
在这里插入图片描述
  它是一个Advisor类型的通知器实例,内部的transactionAttributeSource为AnnotationTransactionAttributeSource,advice为TransactionInterceptor。
  它的pointcut是一个TransactionAttributeSourcePointcut的匿名内部类实例,其内部的getTransactionAttributeSource保存了设置的AnnotationTransactionAttributeSource

public class BeanFactoryTransactionAttributeSourceAdvisor extends 
AbstractBeanFactoryPointcutAdvisor {

    /**
     * 事务属性源
     */
    @Nullable
    private TransactionAttributeSource transactionAttributeSource;

    /**
     * 事务切入点,用于判断某个bean/方法是否符合Spring事务切入规则
     */
    private final TransactionAttributeSourcePointcut pointcut = new TransactionAttributeSourcePointcut() {
        @Override
        @Nullable
        protected TransactionAttributeSource getTransactionAttributeSource() {
            return transactionAttributeSource;
        }
    };


    /**
     * 设置用于查找TransactionAttribute的TransactionAttributeSource。
     *
     * @see TransactionInterceptor#setTransactionAttributeSource
     */
    public void setTransactionAttributeSource(TransactionAttributeSource transactionAttributeSource) {
        this.transactionAttributeSource = transactionAttributeSource;
    }

    /**
     * 为这个切入点设置要使用的ClassFilter类筛选器。默认是ClassFilter.TRUE,即匹配所有类
     */
    public void setClassFilter(ClassFilter classFilter) {
        this.pointcut.setClassFilter(classFilter);
    }

    /**
     * 获取切入点
     */
    @Override
    public Pointcut getPointcut() {
        return this.pointcut;
    }

}

  在普通bean被实例化之后将通过AutoProxyCreator的postProcessAfterInitialization方法来判断当前bean是否可以被代理,其中就自然会通过BeanFactoryTransactionAttributeSourceAdvisor进行匹配,在匹配的时候它实际上就是通过解析对应的类、方法上的Spring事务注解(比如@Transactional)来进行匹配的,这就是解析@Transactional注解的入口。

2 TransactionAttributeSourcePointcut事务属性源匹配器

  TransactionAttributeSourcePointcut作为事务通知器的切入点,通知器就是靠此对象来判断某个bean是否可以被此事务通知器增强,简单的说就是判断当前bean是否可以应用这一个事务。
在这里插入图片描述
  在AutoProxyCreator的postProcessAfterInitialization方法中(内部的canApply方法,源码我们在此前的通用Spring AOP的AspectJAwareAdvisorAutoProxyCreator部分已经讲过了),Pointcut的通用匹配逻辑就是:

  1. 每一个实例化之后的bean都会先通过ClassFilter.matches方法进行匹配,在TransactionAttributeSourcePointcut的构造器中调用了setClassFilter方法,将ClassFilter配置为一个TransactionAttributeSourceClassFilter。
  2. 如果ClassFilter匹配通过,那么会获取bean的全部方法并且通过MethodMatcher.matches对方法一一进行匹配。最终,如果至少有一个方法能够被增强,那么表示当前bean实例就能被当然通知器,也就可以创建代理对象。

  下面我们来看看TransactionAttributeSourcePointcut的具体逻辑!

2.1 ClassFilter.matches类型匹配

  很简单,两个判断逻辑:

  1. 如果当前bean的类型属于TransactionalProxy或者PersistenceExceptionTranslator,这些类型要么表示当前bean属于已经进行了手动事务代理,要么表示当前bean属于事务管理的基础bean,这些类的实例的方法不应该进行Spring事务的代理。
  2. 如果不是上面那些类型的bean实例,就通过设置的TransactionAttributeSource来判断。如果TransactionAttributeSource为null,或者isCandidateClass方法返回true,那么表示当前bean的class允许继续匹配方法,否则表示不会继续匹配,即当前bean实例不会进行事务代理。
abstract class TransactionAttributeSourcePointcut extends 
StaticMethodMatcherPointcut implements Serializable {

    /**
     * 设置ClassFilter为一个TransactionAttributeSourceClassFilter实例
     */
    protected TransactionAttributeSourcePointcut() {
        setClassFilter(new TransactionAttributeSourceClassFilter());
    }

    /**
     * 获取底层的TransactionAttributeSource(可以为null)。由子类实现。
     * <p>
     * 在BeanFactoryTransactionAttributeSourceAdvisor中的TransactionAttributeSourcePointcut就是一个匿名内部类实现
     * 它的getTransactionAttributeSource会返回配置的AnnotationTransactionAttributeSource事务属性源
     */
    @Nullable
    protected abstract TransactionAttributeSource getTransactionAttributeSource();


    /**
     * 实际工作委托给TransactionAttributeSource的类过滤器。
     * TransactionAttributeSource的isCandidateClass方法用于过滤那些方法不值继续得搜索的类。
     */
    private class TransactionAttributeSourceClassFilter implements ClassFilter {

        /**
         * 匹配类的方法
         *
         * @param clazz bean的class
         * @return true 成功 false 失败
         */
        @Override
        public boolean matches(Class<?> clazz) {
            //如果当前bean的类型属于TransactionalProxy或者TransactionalProxy或者PersistenceExceptionTranslator
            //那么返回false,这些类的实例的方法不应该进行Spring 事务的代理
            if (TransactionalProxy.class.isAssignableFrom(clazz) ||
                    TransactionManager.class.isAssignableFrom(clazz) ||
                    PersistenceExceptionTranslator.class.isAssignableFrom(clazz)) {
                return false;
            }
            //如果不是上面那些类型的bean实例,就通过设置的TransactionAttributeSource来判断
            //如果TransactionAttributeSource不为null并且isCandidateClass方法返回true,
            //那么表示当前bean的class允许继续匹配方法,否则表示不会继续匹配,即当前bean实例不会进行事务代理
            TransactionAttributeSource tas = getTransactionAttributeSource();
            return (tas == null || tas.isCandidateClass(clazz));
        }
    }

}

2.1.1 AnnotationTransactionAttributeSource

  在BeanFactoryTransactionAttributeSourceAdvisor中的TransactionAttributeSourcePointcut就是一个匿名内部类实现,它的getTransactionAttributeSource会返回配置的AnnotationTransactionAttributeSource事务属性源。
  AnnotationTransactionAttributeSource是专门用于获取基于注解的Spring声明式事务管理的事务属性的属性源。并且除了Spring的org.springframework.transaction.annotation.Transactional注解之外,还支持JTA 1.2的javax.transaction.Transactional事务注解以及EJB3的javax.ejb.TransactionAttribute事务注解。
  在创建时,将会调用它的默认无参构造器。将会为对应的事务注解配置对应的注解解析器,并且默认情况下,仅支持对类中公共的方法进行事务代理。
  也就是说,如果采用Spring AOP的基于注解的声明式事务控制,那么仅支持public方法,这一点,相比于普通的Spring AOP的代理范围更窄,这是我们一定要注意的!如果需要非public方法注解生效,那么应该使用底层基于AspectJ的静态织入。

/*AnnotationTransactionAttributeSource的相关属性和方法*/

/**
 * 是否存在JTA 1.2的javax.transaction.Transactional事务注解
 */
private static final boolean jta12Present;

/**
 * 是否存在EJB3的javax.ejb.TransactionAttribute事务注解
 */
private static final boolean ejb3Present;

/*
 * 在静态块中判断是否存在其他外部框架的事务注解
 * JTA 1.2的javax.transaction.Transactional事务注解
 * EJB3的javax.ejb.TransactionAttribute事务注解
 */
static {
    ClassLoader classLoader = AnnotationTransactionAttributeSource.class.getClassLoader();
    jta12Present = ClassUtils.isPresent("javax.transaction.Transactional", classLoader);
    ejb3Present = ClassUtils.isPresent("javax.ejb.TransactionAttribute", classLoader);
}

/**
 * 是否仅支持公共的方法应用事务
 */
private final boolean publicMethodsOnly;

/**
 * 事务注解的解析器
 */
private final Set<TransactionAnnotationParser> annotationParsers;


/**
 * 创建一个默认的AnnotationTransactionAttributeSource,除了Spring的org.springframework.transaction.annotation.Transactional注解之外
 * 还支持JTA 1.2的javax.transaction.Transactional事务注解以及EJB3的javax.ejb.TransactionAttribute事务注解
 * <p>
 * 并且仅支持公共的方法应用于事务中
 */
public AnnotationTransactionAttributeSource() {
    this(true);
}

/**
 * @param publicMethodsOnly 是否支持只携带事务注解的公共方法(通常用于基于代理的AOP),还是受保护的私有方法(通常与AspectJ类编织一起使用)
 */
public AnnotationTransactionAttributeSource(boolean publicMethodsOnly) {
    //设置属性
    this.publicMethodsOnly = publicMethodsOnly;
    //如果存在JTA 1.2的javax.transaction.Transactional事务注解或者EJB3的javax.ejb.TransactionAttribute事务注解
    if (jta12Present || ejb3Present) {
        //设置事务注解解析器集合
        this.annotationParsers = new LinkedHashSet<>(4);
        //添加SpringTransactionAnnotationParser解析器
        //用于解析Spring自带的org.springframework.transaction.annotation.Transactional注解
        this.annotationParsers.add(new SpringTransactionAnnotationParser());
        //添加对应的外部事物注解的解析器
        if (jta12Present) {
            this.annotationParsers.add(new JtaTransactionAnnotationParser());
        }
        if (ejb3Present) {
            this.annotationParsers.add(new Ejb3TransactionAnnotationParser());
        }
    } else {
        //如果没有其他的外部事务注解
        //设置事务注解解析器集合,其中仅有一个SpringTransactionAnnotationParser解析器
        //用于解析Spring自带的org.springframework.transaction.annotation.Transactional注解
        this.annotationParsers = Collections.singleton(new SpringTransactionAnnotationParser());
    }
}

2.1.2 isCandidateClass是否是候选class

  我们来看看AnnotationTransactionAttributeSource的isCandidateClass方法。
  原理很简单,遍历注解解析器集合依次判断是否可以解析目标类型,只要有一个解析器能够解析,那么就算合格。

/**
 * AnnotationTransactionAttributeSource的方法
 *
 * @param targetClass 目标类型
 * @return true 表示属于候选class,false 表示不会继续进行方法匹配
 */
@Override
public boolean isCandidateClass(Class<?> targetClass) {
    //遍历注解解析器集合
    for (TransactionAnnotationParser parser : this.annotationParsers) {
        //判断是否可以解析目标类型
        if (parser.isCandidateClass(targetClass)) {
            //只要有一个解析器能够解析,那么就算合格
            return true;
        }
    }
    return false;
}

  我们简单看看SpringTransactionAnnotationParser解析器,因为现在基本上都是使用Spring自带的注解了。
  其内部调用了AnnotationUtils.isCandidateClass方法。该方法的通用逻辑就是:如果任何一个注解的全路径名都不是以"java."开始,并且该Class全路径名以"start."开始或者Class的类型为Ordered.class,那么返回false,否则其他情况都返回true。因此,基本上普通class都会返回true,表示可以进行后续的方法校验。

/**
 * SpringTransactionAnnotationParser的方法
 *
 * @param targetClass 目标类型
 * @return 是否是候选class
 */
@Override
public boolean isCandidateClass(Class<?> targetClass) {
    /*
     * 确定给定的类是否是携带指定注解的候选者(在类型、方法或字段级别)。
     *
     * 如果任何一个注解的全路径名都不是以"java."开始,并且该Class全路径名以"start."开始或者Class的类型为Ordered.class,
     * 那么返回false,否则其他情况都返回true。因此,基本上都会返回true
     */
    return AnnotationUtils.isCandidateClass(targetClass, Transactional.class);
}

2.2 MethodMatcher.matches方法匹配

  TransactionAttributeSourcePointcut实现了MethodMatcher接口,因此它自己就是一个方法匹配器。在匹配每一个方法的时候,将会调用MethodMatcher. matches方法,也就是它自己重写的matches方法。

/**
 1. TransactionAttributeSourcePointcut的方法
 2. <p>
 3. 通过委托AnnotationTransactionAttributeSource的getTransactionAttribute方法来判断该方法是否匹配
 4.  5. @param method      需要匹配的方法
 6. @param targetClass 实际目标类型
 7. @return true 可以被代理,false不可以被代理
 */
@Override
public boolean matches(Method method, Class<?> targetClass) {
    TransactionAttributeSource tas = getTransactionAttributeSource();
    //如果TransactionAttributeSource不为null并且getTransactionAttribute方法返回null
    //那么表示当前bean实例不会进行事务代理,否则表示可以进行事务代理
    return (tas == null || tas.getTransactionAttribute(method, targetClass) != null);
}

2.2.1 getTransactionAttribute获取属性源

  我们来看看AnnotationTransactionAttributeSource的getTransactionAttribute方法。该方法是父类AbstractFallbackTransactionAttributeSource实现的。
  可以看到,该方法的逻辑也比较简单:

  1. 判断如果方法是属于Object,直接返回null,这些方法不会应用事务,比如Object的hashcode、equals……。
  2. 然后根据方法和目标类型获取缓存key,尝试从缓存中获取。如果缓存有值,判断如果是NULL_TRANSACTION_ATTRIBUTE,这是一个表示没有事务属性的常量,那么直接返回null,否则就表示存在事务属性且此前已经解析过了,直接返回缓存的值。实际上,解析过该方法和目标类型,如论有没有事务属性都会放入缓存。
  3. 到这里表示此前没有解析过该方法和目标类型,那么需要解析。调用computeTransactionAttribute那么根据当前方法和目标类型计算出TransactionAttribute。
  4. 如果计算结果为null,那么仍然存入一个表示没有事务属性的固定值NULL_TRANSACTION_ATTRIBUTE到缓存中,再次遇到时不再解析。如果不为null,说明获取到了事务属性,同样存入缓存。最后返回computeTransactionAttribute的结果。
/*AbstractFallbackTransactionAttributeSource的相关属性*/

/**
 * TransactionAttributes的缓存,key定位到目标类上的某个方法。
 */
private final Map<Object, TransactionAttribute> attributeCache = new ConcurrentHashMap<>(1024);


/**
 * 缓存中保存的值常量,表示没有为该方法找到事务属性,但是我们也不需要再次查找。
 */
private static final TransactionAttribute NULL_TRANSACTION_ATTRIBUTE = new DefaultTransactionAttribute() {
    @Override
    public String toString() {
        return "null";
    }
};

/**
 * AbstractFallbackTransactionAttributeSource的方法
 * <p>
 * 确定此方法调用的事务属性。如果没有找到方法属性,则默认为类的事务属性。
 *
 * @param method      当前调用的方法(永不为空)
 * @param targetClass 调用的目标类(可能是null)
 * @return 此方法的TransactionAttribute,如果该方法不是事务性的,则为null
 */
@Override
@Nullable
public TransactionAttribute getTransactionAttribute(Method method, @Nullable Class<?> targetClass) {
    //如果方法是属于Object,直接返回null,这些方法不会应用事务,比如Object的hashcode、equals……
    if (method.getDeclaringClass() == Object.class) {
        return null;
    }

    //获取方法和目标类的缓存key
    Object cacheKey = getCacheKey(method, targetClass);
    //尝试从缓存获取
    TransactionAttribute cached = this.attributeCache.get(cacheKey);
    //如果此前解析过该方法以及目标类,那么Value要么是指示没有事务属性,要么是一个实际的事务属性,一定不会为null
    if (cached != null) {
        //如果value指示没有事务属性,那么返回null
        if (cached == NULL_TRANSACTION_ATTRIBUTE) {
            return null;
        } else {
            //否则直接返回此前解析的事务属性
            return cached;
        }
    } else {
        //到这里表示此前没有解析过该方法和目标类型

        //那么根据当前方法和目标类型计算出TransactionAttribute
        TransactionAttribute txAttr = computeTransactionAttribute(method, targetClass);
        // 如果为null
        if (txAttr == null) {
            //那么存入一个表示没有事务属性的固定值到缓存中,再次遇到时不再解析
            this.attributeCache.put(cacheKey, NULL_TRANSACTION_ATTRIBUTE);
        } else {
            //如果不为null,说明获取到了事务属性

            //获取给定方法的全限定名,基本仅用于输出日志
            String methodIdentification = ClassUtils.getQualifiedMethodName(method, targetClass);
            //如果事务属性属于DefaultTransactionAttribute
            if (txAttr instanceof DefaultTransactionAttribute) {
                //设置描述符
                ((DefaultTransactionAttribute) txAttr).setDescriptor(methodIdentification);
            }
            if (logger.isTraceEnabled()) {
                logger.trace("Adding transactional method '" + methodIdentification + "' with attribute: " + txAttr);
            }
            //将结果存入缓存,再次遇到时不再解析
            this.attributeCache.put(cacheKey, txAttr);
        }
        //返回txAttr,可能为null
        return txAttr;
    }
}


/**
 1. AbstractFallbackTransactionAttributeSource的方法
 2. <p>
 3. 确定给定方法和目标类的缓存key,即MethodClassKey,通过method和class来进行比较
 */
protected Object getCacheKey(Method method, @Nullable Class<?> targetClass) {
    return new MethodClassKey(method, targetClass);
}

  可以看到,这里使用了缓存的逻辑,非常的巧妙,另外,真正直接解析的逻辑位于computeTransactionAttribute方法中。

2.2.1.1 computeTransactionAttribute计算事务属性

  该方法用于计算给定方法和目标类型的事务属性,一般查找顺序为:执行的目标方法、对应的接口方法(如果有)、目标类、接口类(如果有),其中一个找到了就直接返回,不会继续查找。 具体逻辑如下:

  1. 首先就是对于方法的public属性进行判断,默认不允许非公共方法进行事务代理,将直接返回null。但是可以通过重写allowPublicMethodsOnly方法修改规则,子类AnnotationTransactionAttributeSource就重写了该方法,将返回publicMethodsOnly的属性值,这个值可以手动配置,但同样默认也是只有公共方法可以被设为事务性的。
  2. 获取最终要执行的目标方法,有可能参数方法表示的是一个接口的方法,我们需要找到实现类的方法,也就是最终真正调用的目标方法。但是基本上都是最终方法。
  3. 首先通过findTransactionAttribute去找直接标记在目标方法上的事务注解并解析为事务属性,如果方法上有就直接返回,不会再看类上的了事务注解了,这就是方法上的事务注解的优先级更高的原理。findTransactionAttribute方法由子类实现,findTransactionAttribute方法在查找的时候,会先查找目标方法本身的注解,然后才递归的向上会查找父级类对应方法上的注解,返回找到的第一个注解。
  4. 如果方法上没有就通过findTransactionAttribute去查找目标类上的事务注解,有就直接返回。findTransactionAttribute方法由子类实现,findTransactionAttribute方法在查找的时候,会先查找目标类本身的注解,然后才递归的向上会查找父级类上的注解,返回找到的第一个注解。
  5. 到这里表示没有在真正目标方法或者类上找到事务注解。继续判断如果最终的目标方法和当前参数方法不一致,那么在参数方法和类上查找(一般都走不到这一步)。
    1. 通过findTransactionAttribute去查找参数方法上的事务注解,如果找到了就返回。
    2. 如果还是没找到,那么最后一次尝试。通过findTransactionAttribute去查找参数方法的类上是否有事务注解,找到了就返回。
  6. 以上方法都是找不到事务注解,那么将返回null,表示当前方法不会进行事务代理。
/**
 * AbstractFallbackTransactionAttributeSource的方法
 * <p>
 * 计算事务属性的核心方法,但不缓存结果。getTransactionAttribute是这个方法的一个有效的缓存装饰器。
 */
@Nullable
protected TransactionAttribute computeTransactionAttribute(Method method, @Nullable Class<?> targetClass) {
    //默认不允许非公共方法进行事务代理,这里就是判断public方法的逻辑,但是可以通过allowPublicMethodsOnly方法修改
    //如果是其他访问权限,比如default,那么获取其他AOP操作能够代理,但是事务不会生效
    if (allowPublicMethodsOnly() && !Modifier.isPublic(method.getModifiers())) {
        return null;
    }

    //获取最终要执行的目标方法,有可能参数方法表示的是一个接口的方法,我们需要找到实现类的方法
    //通常情况下,参数方法就是最终执行的方法
    Method specificMethod = AopUtils.getMostSpecificMethod(method, targetClass);

    //首先去找直接标记在方法上的事务注解并解析为事务属性
    //如果方法上有就直接返回,不会再看类上的了事务注解了,这就是方法上的事务注解的优先级更高的原理
    //findTransactionAttribute方法由子类实现
    TransactionAttribute txAttr = findTransactionAttribute(specificMethod);
    if (txAttr != null) {
        return txAttr;
    }
    //如果方法上没有就查找目标类上的事务注解,有就直接返回
    //findTransactionAttribute方法由子类实现
    txAttr = findTransactionAttribute(specificMethod.getDeclaringClass());
    if (txAttr != null && ClassUtils.isUserLevelMethod(method)) {
        return txAttr;
    }
    //到这里表示没有在目标方法或者类上找到事务注解,如果最终的目标方法和当前方法不一致,那么在当前方法中查找
    //实际上很难走到这一步,在此前的查找中基本上就返回了
    if (specificMethod != method) {
        //我们会查找参数方法上的事务注解,如果找到了就返回
        //findTransactionAttribute方法由子类实现
        txAttr = findTransactionAttribute(method);
        if (txAttr != null) {
            return txAttr;
        }
        //如果还是没找到,那么最后一次尝试
        //查找类上是否有事务注解,如果找到了就返回
        //findTransactionAttribute方法由子类实现
        txAttr = findTransactionAttribute(method.getDeclaringClass());
        if (txAttr != null && ClassUtils.isUserLevelMethod(method)) {
            return txAttr;
        }
    }
    //以上都找不到事务注解,那么将返回null,表示当前方法不会进行事务代理
    return null;
}


/**
 * 默认情况下,只有公共方法可以被设为事务性的。
 * <p>
 * 子类AnnotationTransactionAttributeSource重写了该方法,将返回publicMethodsOnly的属性值
 * 同样默认也是只有公共方法可以被设为事务性的
 */
@Override
protected boolean allowPublicMethodsOnly() {
    return this.publicMethodsOnly;
}
2.2.1.1.1 findTransactionAttribute查找事务属性

  最终查找事务属性的工作是通过findTransactionAttribute方法来实现的。
  findTransactionAttribute方法在查找方法上的注解的时候,会先查找目标方法本身的注解,然后才递归的向上会查找父级类对应方法上的注解,返回找到的第一个注解。
  findTransactionAttribute方法在查找类上的注解的时候,会先查找目标类本身的注解,然后才递归的向上会查找父级类上的注解,返回找到的第一个注解。
  该方法由子类AnnotationTransactionAttributeSource实现,在其实现中,可以看到,最终还是委托此前配置的TransactionAnnotationParser注解解析器的parseTransactionAnnotation方法来执行解析。

  和此前判断class一样,又是按顺序遍历设置的注解解析器集合,委托内部的解析器解析,找到一个就返回,不会应用后续的解析器,什么意思呢,如果一个方法使用了多种事务注解,仍然会按照解析器的顺序解析,最终只有一个注解会生效,默认头部解析器就是SpringTransactionAnnotationParser,支持Spring的@Transactional注解。

/**
 * 子类AnnotationTransactionAttributeSource实现的方法
 *
 * @param method 当前要查找的方法
 * @return 事务属性源,可能为null
 */
@Override
@Nullable
protected TransactionAttribute findTransactionAttribute(Method method) {
    return determineTransactionAttribute(method);
}

/**
 * 子类AnnotationTransactionAttributeSource实现的方法
 *
 * @param clazz 当前要查找的类
 * @return 事务属性源,可能为null
 */
@Override
@Nullable
protected TransactionAttribute findTransactionAttribute(Class<?> clazz) {
    return determineTransactionAttribute(clazz);
}


/**
 1. 子类AnnotationTransactionAttributeSource的方法
 2. <p>
 3. 确定给定方法或类的事务属性。
 4. 该实现委托配置的TransactionAnnotationParser将已知的注解解析到Spring的元数据属性类中。如果不是事务性的,则返回null。
 5. <p>
 6. 可以覆盖该方法,以支持携带事务元数据的自定义事务注解。
 7.  8. @param element 带注解的方法或类
 9. @return 配置的事务属性,如果没有找到,则为null
 */
@Nullable
protected TransactionAttribute determineTransactionAttribute(AnnotatedElement element) {
    //和此前判断class一样,又是按顺序遍历设置的注解解析器集合,委托内部的解析器解析
    for (TransactionAnnotationParser parser : this.annotationParsers) {
        //这次是调用解析器的parseTransactionAnnotation方法
        TransactionAttribute attr = parser.parseTransactionAnnotation(element);
        //找到一个就返回,不会应用后续的解析器
        //什么意思呢,如果一个方法使用了多种事务注解,仍然会按照解析器的顺序解析,最终只有一个注解会生效
        //默认头部解析器就是SpringTransactionAnnotationParser,支持Spring的@Transactional注解
        if (attr != null) {
            return attr;
        }
    }
    return null;
}
2.2.1.1.2 parseTransactionAnnotation解析事务属性

  最终会依次调用AnnotationTransactionAttributeSource的annotationParsers集中的TransactionAnnotationParser事务注解解析器的parseTransactionAnnotation方法,来解析不同的事务注解,并且封装为一个TransactionAnnotation返回。
  我们主要看SpringTransactionAnnotationParser,它支持Spring的@Transactional注解。它的原理很简单:

  1. 首先就是查找方法或者类上的@Transactional注解,这里会递归的进行向上查找。如果在当前方法/类上没找到,那么会继续在父接口方法/类上查找,最终将使用找到的第一个注解数据。
  2. 没找到就返回null。找到注解之后,将会解析各种设置的属性并且填充到一个新建的RuleBasedTransactionAttribute实例中随后返回,很简单。RuleBasedTransactionAttribute我们在此前基于XML的配置解析中就见过了,<tx:method/>子标签也会被解析为一个个的RuleBasedTransactionAttribute实例。
/**
 * SpringTransactionAnnotationParser的方法
 * <p>
 * 解析方法或者类上的@Transactional注解并解析为一个TransactionAttribute返回
 *
 * @param element 方法或者类元数据
 * @return TransactionAttribute,可能为null
 */
@Override
@Nullable
public TransactionAttribute parseTransactionAnnotation(AnnotatedElement element) {
    //查找方法或者类上的@Transactional注解,这里会递归的进行向上查找
    //如果在当前方法/类上没找到,那么会继续在父类/接口的方法/类上查找,最终将使用找到的第一个注解数据
    //这就是在接口方法或者接口上的事务注解也能生效的原因,但是它们的优先级更低
    AnnotationAttributes attributes = AnnotatedElementUtils.findMergedAnnotationAttributes(
            element, Transactional.class, false, false);
    //如果找到了@Transactional注解
    if (attributes != null) {
        //解析注解属性,封装为一个TransactionAttribute并返回,实际类型为RuleBasedTransactionAttribute
        return parseTransactionAnnotation(attributes);
    } else {
        //返回null
        return null;
    }
}

/**
 * SpringTransactionAnnotationParser的方法
 * <p>
 * 开放的方法,解析指定的注解为一个TransactionAttribute
 *
 * @param ann 注解
 * @return TransactionAttribute
 */
public TransactionAttribute parseTransactionAnnotation(Transactional ann) {
    return parseTransactionAnnotation(AnnotationUtils.getAnnotationAttributes(ann, false, false));
}

/**
 * SpringTransactionAnnotationParser的方法
 * <p>
 * 解析注解的各种属性,封装到一个RuleBasedTransactionAttribute对象中
 *
 * @param attributes 注解
 * @return RuleBasedTransactionAttribute
 */
protected TransactionAttribute parseTransactionAnnotation(AnnotationAttributes attributes) {
    //最终会创建一个RuleBasedTransactionAttribute
    RuleBasedTransactionAttribute rbta = new RuleBasedTransactionAttribute();

    //设置各种属性
    Propagation propagation = attributes.getEnum("propagation");
    rbta.setPropagationBehavior(propagation.value());
    Isolation isolation = attributes.getEnum("isolation");
    rbta.setIsolationLevel(isolation.value());
    rbta.setTimeout(attributes.getNumber("timeout").intValue());
    rbta.setReadOnly(attributes.getBoolean("readOnly"));
    rbta.setQualifier(attributes.getString("value"));

    //设置回滚规则
    //同样,回滚规则在前,不回滚规则在后,如果回滚和不回滚设置了相同的异常,那么抛出异常时将会回滚
    List<RollbackRuleAttribute> rollbackRules = new ArrayList<>();
    //rollbackFor在最前
    for (Class<?> rbRule : attributes.getClassArray("rollbackFor")) {
        rollbackRules.add(new RollbackRuleAttribute(rbRule));
    }
    //rollbackForClassName在第二
    for (String rbRule : attributes.getStringArray("rollbackForClassName")) {
        rollbackRules.add(new RollbackRuleAttribute(rbRule));
    }
    //noRollbackFor在第三
    for (Class<?> rbRule : attributes.getClassArray("noRollbackFor")) {
        rollbackRules.add(new NoRollbackRuleAttribute(rbRule));
    }
    //noRollbackForClassName在最后
    for (String rbRule : attributes.getStringArray("noRollbackForClassName")) {
        rollbackRules.add(new NoRollbackRuleAttribute(rbRule));
    }
    rbta.setRollbackRules(rollbackRules);

    return rbta;
}

3 小结

  本次主要讲解了如何判断基于注解的声明式事物是否可以作用在某个类上。主要就是判断类/接口上、方法上是否有符合规则的事务注解,如果有,那么就可以被增强。默认情况下,基于注解的声明式事务仅支持public方法的增强,这里点是在computeTransactionAttribute源码中进行判断的。
  @Transactional注解也是在判断时解析的。在AutoProxyCreator的postProcessAfterInitialization方法判断当前bean是否可以被代理时,在内部的canApply方法中,在通过BeanFactoryTransactionAttributeSourceAdvisor通知器的TransactionAttributeSourcePointcut切入点的matches方法中进行解析的,该方法看起来是在调用AnnotationTransactionAttributeSource,但如果要精确到最终的解析者,那么就是AnnotationTransactionAttributeSource内部保存的SpringTransactionAnnotationParser这个解析器,而解析的结果也会被缓存到AbstractFallbackTransactionAttributeSource的attributeCache中,后续将直接使用。
  基于注解的Spring声明式事务处理过程中,在BeanFactoryTransactionAttributeSourceAdvisor和TransactionInterceptor都配置了同一个TransactionAttributeSource,因此,在上面,虽然是将结果缓存到BeanFactoryTransactionAttributeSourceAdvisor内部的TransactionAttributeSource中,但是由于它们指向同一个对象,那么TransactionInterceptor中的TransactionAttributeSource也能获取到缓存的数据,这样就相当于配置好了不同的方法对应的事务属性。这类似于此前的<tx:annotation-driven/>标签解析时,配置的NameMatchTransactionAttributeSource,只不过它的规则是直接配置的<tx:method/>标签,而AnnotationTransactionAttributeSource的规则是后来运行时解析的各种事务注解组成的!
  @Transactional注解的解析优先级是:目标方法的@Transactional注解、父类/接口方法的@Transactional注解、目标类的@Transactional注解、父类/接口的@Transactional注解。
  @Transactional注解将被解析为一个RuleBasedTransactionAttribute并缓存到AbstractFallbackTransactionAttributeSource的attributeCache集合中。RuleBasedTransactionAttribute我们在此前基于XML的配置解析中就见过了,<tx:method/>子标签也会被解析为一个个的RuleBasedTransactionAttribute实例。

  简要流程图为:
在这里插入图片描述
  如果某个bean可以被事务增强,那么bean将会创建代理对象,并且BeanFactoryTransactionAttributeSourceAdvisor将会加入代理对象的通知器链中。在后续调用具体方法时,将会根据通知器链中的通知器解析为对应的Interceptor拦截器链,随后即可在目标方法的各个调用点通过责任链模式递归的执行各种AOP增强的逻辑,这其中自然包括BeanFactoryTransactionAttributeSourceAdvisor中的TransactionInterceptor拦截器,因此,下面我们将介绍TransactionInterceptor,它是真正为某个方法执行Spring声明式事务处理的地方,也是又一个重点!

相关文章:
  https://spring.io/
  Spring Framework 5.x 学习
  Spring Framework 5.x 源码

如有需要交流,或者文章有误,请直接留言。另外希望点赞、收藏、关注,我将不间断更新各种Java学习博客!

  • 7
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 8
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 8
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

刘Java

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值