@Transactional 源码解析——事务准备阶段

前言

@Transaction是 Spring 声明式事务的使用方式。它让我们从复杂的事务处理中得到解脱,使我们再也不需要去处理获得连接、关闭连接、事务提交和事务回滚等操作,再也不需要在与事务相关的方法中处理大量的 try...catch...finally 代码。下面我们分别基于 Spring 和 基于 SpringBoot 两种方式,来了解其实现机制。

1. 基于 Spring

1.1 事务配置

在 Spring 中是基于 xml 文件配置的。

<?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:tx="http://www.springframework.org/schema/tx"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
                           http://www.springframework.org/schema/beans/spring-beans.xsd
                           http://www.springframework.org/schema/tx
                           http://www.springframework.org/schema/tx/spring-tx.xsd">

    <!-- 事务支持 -->
    <tx:annotation-driven/>
    
    <!-- 配置事务管理器,注意这里的dataSource和SqlSessionFactoryBean的dataSource要一致,不然事务就没有作用了 -->
    <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="dataSource" />
    </bean>

    <!-- 配置数据源-->
    <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource" init-method="init" destroy-method="close">
        <property name="url">
            <value>
                ${jdbc.druid.url}
            </value>
        </property>
        <property name="username">
            <value>${jdbc.druid.user}</value>
        </property>
        <property name="password">
            <bean class="com.tongbanjie.commons.support.spring.EncryptDBPasswordFactory">
                <property name="password" value="${jdbc.druid.password}"/>
            </bean>
        </property>
        <property name="filters">
            <value>${jdbc.druid.filters}</value>
        </property>
        <property name="maxActive">
            <value>${jdbc.druid.maxActive}</value>
        </property>
        <property name="initialSize">
            <value>${jdbc.druid.initialSize}</value>
        </property>
        <property name="maxWait">
            <value>${jdbc.druid.maxWait}</value>
        </property>
        <property name="minIdle">
            <value>${jdbc.druid.minIdle}</value>
        </property>
        <property name="timeBetweenEvictionRunsMillis">
            <value>${jdbc.druid.timeBetweenEvictionRunsMillis}</value>
        </property>
        <property name="minEvictableIdleTimeMillis">
            <value>${jdbc.druid.minEvictableIdleTimeMillis}</value>
        </property>
        <property name="validationQuery">
            <value>${jdbc.druid.validationQuery}</value>
        </property>
        <property name="testWhileIdle">
            <value>${jdbc.druid.testWhileIdle}</value>
        </property>
        <property name="testOnBorrow">
            <value>${jdbc.druid.testOnBorrow}</value>
        </property>
        <property name="testOnReturn">
            <value>${jdbc.druid.testOnReturn}</value>
        </property>
        <property name="poolPreparedStatements">
            <value>${jdbc.druid.poolPreparedStatements}</value>
        </property>
        <property name="maxOpenPreparedStatements">
            <value>${jdbc.druid.maxOpenPreparedStatements}</value>
        </property>
    </bean>

</beans>

1.2 事务标签解析

在 Spring 中,事务的开关是这个配置:<tx:annotation-driven/>,于是,我们从这个配置开始分析。我们在 spring-tx 包下找到其 spring.handlers 文件:

http\://www.springframework.org/schema/tx=org.springframework.transaction.config.TxNamespaceHandler

"http://www.springframework.org/schema/tx" 这个自定义的命名空间处理器是org.springframework.transaction.config.TxNamespaceHandler。于是我们直接跳到该类:

public class TxNamespaceHandler extends NamespaceHandlerSupport {

    static final String TRANSACTION_MANAGER_ATTRIBUTE = "transaction-manager";

    static final String DEFAULT_TRANSACTION_MANAGER_BEAN_NAME = "transactionManager";


    static String getTransactionManagerName(Element element) {
        return (element.hasAttribute(TRANSACTION_MANAGER_ATTRIBUTE) ?
                element.getAttribute(TRANSACTION_MANAGER_ATTRIBUTE) : DEFAULT_TRANSACTION_MANAGER_BEAN_NAME);
    }


    @Override
    public void init() {
        registerBeanDefinitionParser("advice", new TxAdviceBeanDefinitionParser());
        registerBeanDefinitionParser("annotation-driven", new AnnotationDrivenBeanDefinitionParser());
        registerBeanDefinitionParser("jta-transaction-manager", new JtaTransactionManagerBeanDefinitionParser());
    }

}

在这里,我们找到了我们想要的"<annotation-driven/>"标签的解析器:AnnotationDrivenBeanDefinitionParser,于是我们直接看其 parse 方法:

public BeanDefinition parse(Element element, ParserContext parserContext) {
        registerTransactionalEventListenerFactory(parserContext);
        String mode = element.getAttribute("mode");
        if ("aspectj".equals(mode)) {
            // mode="aspectj"
            registerTransactionAspect(element, parserContext);
        }
        else {
            // mode="proxy"
            AopAutoProxyConfigurer.configureAutoProxyCreator(element, parserContext);
        }
        return null;
    }

在解析中存在对于 mode 属性的判断,而其默认的 mode = "proxy"。

1.2.1 注册 InfrastructureAdvisorAutoProxyCreator

我们以默认配置为例子进行分析,进入AopAutoProxyConfigurer类的 configureAutoProxyCreator方法:

    public static void configureAutoProxyCreator(Element element, ParserContext parserContext) {
            
            // 注册  InfrastructureAdvisorAutoProxyCreator
            AopNamespaceUtils.registerAutoProxyCreatorIfNecessary(parserContext, element);

            String txAdvisorBeanName = TransactionManagementConfigUtils.TRANSACTION_ADVISOR_BEAN_NAME;
            if (!parserContext.getRegistry().containsBeanDefinition(txAdvisorBeanName)) {
                Object eleSource = parserContext.extractSource(element);

                // 创建 TransactionAttributeSource 的 bean
                RootBeanDefinition sourceDef = new RootBeanDefinition(
                        "org.springframework.transaction.annotation.AnnotationTransactionAttributeSource");
                sourceDef.setSource(eleSource);
                sourceDef.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
                String sourceName = parserContext.getReaderContext().registerWithGeneratedName(sourceDef);

                // 创建 TransactionInterceptor  的 bean
                RootBeanDefinition interceptorDef = new RootBeanDefinition(TransactionInterceptor.class);
                interceptorDef.setSource(eleSource);
                interceptorDef.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
                registerTransactionManager(element, interceptorDef);
                interceptorDef.getPropertyValues().add("transactionAttributeSource", new RuntimeBeanReference(sourceName));
                String interceptorName = parserContext.getReaderContext().registerWithGeneratedName(interceptorDef);

                // 创建 TransactionAttributeSourceAdvisor  的 bean
                RootBeanDefinition advisorDef = new RootBeanDefinition(BeanFactoryTransactionAttributeSourceAdvisor.class);
                advisorDef.setSource(eleSource);
                advisorDef.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
                // 将 TransactionAttributeSource 的 bean 注入 advisorDef 的 transactionAttributeSource 属性中
                advisorDef.getPropertyValues().add("transactionAttributeSource", new RuntimeBeanReference(sourceName));
                // 将 TransactionInterceptor 的 bean 注入 advisorDef 的 adviceBeanName 属性中
                advisorDef.getPropertyValues().add("adviceBeanName", interceptorName);
                if (element.hasAttribute("order")) {
                    advisorDef.getPropertyValues().add("order", element.getAttribute("order"));
                }
                parserContext.getRegistry().registerBeanDefinition(txAdvisorBeanName, advisorDef);

                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);
            }
        }

上面的代码注册了代理类InfrastructureAdvisorAutoProxyCreator及 3 个 bean,其中的两个 bean (AnnotationTransactionAttributeSourceTransactionInterceptor) 被注册到了一个 bean 名为 advisorDef 的 bean 中,advisorDef 就是BeanFactoryTransactionAttributeSourceAdvisor

那么,我们不禁会问,注册代理类 InfrastructureAdvisorAutoProxyCreator 的 目的是什么呢?查看这个类的层次,如图所示:


从上面的层次结构中可以看到,InfrastructureAdvisorAutoProxyCreator实现了BeanPostProcessor,也就是说在 Spring 中,所有 bean 初始化之后都会调用postProcessAfterInitialization方法,其实现是在父类AbstractAutoProxyCreator类中实现。

public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        if (bean != null) {
            // 构建一个缓存 key,一般为 beanName
            Object cacheKey = getCacheKey(bean.getClass(), beanName);
            // 是否是由于避免循环依赖而创建的 bean 代理
            if (!this.earlyProxyReferences.contains(cacheKey)) {
                // 对 bean 进行封装
                return wrapIfNecessary(bean, beanName, cacheKey);
            }
        }
        return bean;
    }

这里实现的主要目的是对指定 bean 进行封装。我们看wrapIfNecessary函数:

protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) {
        // 如果已经处理过
        if (beanName != null && this.targetSourcedBeans.contains(beanName)) {
            return bean;
        }
        // 不应该被代理的 bean
        if (Boolean.FALSE.equals(this.advisedBeans.get(cacheKey))) {
            return bean;
        }
        // 给定的 bean 类是否是一个基础类,如 Advice、Pointcut 等基础类型的类,或者配置了指定 bean 不需要自动代理
        if (isInfrastructureClass(bean.getClass()) || shouldSkip(bean.getClass(), beanName)) {
            this.advisedBeans.put(cacheKey, Boolean.FALSE);
            return bean;
        }

        // 如果存在增强器,则创建代理
        Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null);
        if (specificInterceptors != DO_NOT_PROXY) {
            this.advisedBeans.put(cacheKey, Boolean.TRUE);
            Object proxy = createProxy(
                    bean.getClass(), beanName, specificInterceptors, new SingletonTargetSource(bean));
            this.proxyTypes.put(cacheKey, proxy.getClass());
            return proxy;
        }

        this.advisedBeans.put(cacheKey, Boolean.FALSE);
        return bean;
    }

该方法逻辑上理解起来还是相对简单的,主要的工作如下:

  1. 找出指定 bean 对应的增强器
  2. 根据找出的增强器创建代理

看起来似乎简单的逻辑,Spring 中又做了哪些复杂的工作呢?

1.2.2 获取对应 class/method 的增强器

获取指定 bean 对应的增强器。其中包含两个关键字:增强器与对应。也就是说在getAdvicesAndAdvisorsForBean方法中,不但要找出增强器,而且还需要判断增强器是否满足要求。

protected Object[] getAdvicesAndAdvisorsForBean(Class<?> beanClass, String beanName, TargetSource targetSource) {
        List<Advisor> advisors = findEligibleAdvisors(beanClass, beanName);
        if (advisors.isEmpty()) {
            return DO_NOT_PROXY;
        }
        return advisors.toArray();
    }

protected List<Advisor> findEligibleAdvisors(Class<?> beanClass, String beanName) {
        List<Advisor> candidateAdvisors = findCandidateAdvisors();
        List<Advisor> eligibleAdvisors = findAdvisorsThatCanApply(candidateAdvisors, beanClass, beanName);
        extendAdvisors(eligibleAdvisors);
        if (!eligibleAdvisors.isEmpty()) {
            eligibleAdvisors = sortAdvisors(eligibleAdvisors);
        }
        return eligibleAdvisors;
    }

第一步,寻找候选增强器findCandidateAdvisors:

public List<Advisor> findAdvisorBeans() {
        // Determine list of advisor bean names, if not cached already.
        String[] advisorNames = null;
        synchronized (this) {
            advisorNames = this.cachedAdvisorBeanNames;
            if (advisorNames == null) {
                // 获取 BeanFactory 中 Advisor 类型的 beanNames
                advisorNames = BeanFactoryUtils.beanNamesForTypeIncludingAncestors(
                        this.beanFactory, Advisor.class, true, false);
                this.cachedAdvisorBeanNames = advisorNames;
            }
        }
        if (advisorNames.length == 0) {
            return new LinkedList<Advisor>();
        }

        List<Advisor> advisors = new LinkedList<Advisor>();
        for (String name : advisorNames) {
            if (isEligibleBean(name)) {
                // 判断是否存在循环依赖
                if (this.beanFactory.isCurrentlyInCreation(name)) {
                    if (logger.isDebugEnabled()) {
                        logger.debug("Skipping currently created advisor '" + name + "'");
                    }
                }
                else {
                    try {
                        // 实例化 Advisor 类型的 bean,并且放入 advisors 中
                        advisors.add(this.beanFactory.getBean(name, Advisor.class));
                    }
                    catch (BeanCreationException ex) {
                        Throwable rootCause = ex.getMostSpecificCause();
                        if (rootCause instanceof BeanCurrentlyInCreationException) {
                            BeanCreationException bce = (BeanCreationException) rootCause;
                            if (this.beanFactory.isCurrentlyInCreation(bce.getBeanName())) {
                                if (logger.isDebugEnabled()) {
                                    logger.debug("Skipping advisor '" + name +
                                            "' with dependency on currently created bean: " + ex.getMessage());
                                }
                                // Ignore: indicates a reference back to the bean we're trying to advise.
                                // We want to find advisors other than the currently created bean itself.
                                continue;
                            }
                        }
                        throw ex;
                    }
                }
            }
        }
        return advisors;
    }

首先,通过BeanFactoryUtils类提供的工具方法获取所有对应 Advisor 类型的 bean 名称,并且实例化这些 Advisor。或许你已经忘记了之前留下的悬念,在解析“<annnotation-driven/>”标签时,会注册BeanFactoryTransactionAttributeSourceAdvisor 的 bean。而在此 bean 中又注入了另外两个 bean,那么此时这个 bean 就会被使用了。因为BeanFactoryTransactionAttributeSourceAdvisor同样实现了 Advisor 接口。那么在获取所有增强器时自然会将此 bean 提取出来,并在后续步骤中被织入代理。

第二步,候选增强器中找到匹配项findAdvisorsThatCanApply:

当找出对应的增强器之后,接下来任务就是看这些增强器是否与对应 的 bean 匹配了。

public static List<Advisor> findAdvisorsThatCanApply(List<Advisor> candidateAdvisors, Class<?> clazz) {
        if (candidateAdvisors.isEmpty()) {
            return candidateAdvisors;
        }
        List<Advisor> eligibleAdvisors = new LinkedList<Advisor>();
        //首先处理引介增强
        for (Advisor candidate : candidateAdvisors) {
            if (candidate instanceof IntroductionAdvisor && canApply(candidate, clazz)) {
                eligibleAdvisors.add(candidate);
            }
        }
        boolean hasIntroductions = !eligibleAdvisors.isEmpty();
        for (Advisor candidate : candidateAdvisors) {
            if (candidate instanceof IntroductionAdvisor) {
                // already processed
                continue;
            }
            // 对于普通 bean 的处理
            if (canApply(candidate, clazz, hasIntroductions)) {
                eligibleAdvisors.add(candidate);
            }
        }
        return eligibleAdvisors;
    }

public static boolean canApply(Advisor advisor, Class<?> targetClass, boolean hasIntroductions) {
        if (advisor instanceof IntroductionAdvisor) {
            return ((IntroductionAdvisor) advisor).getClassFilter().matches(targetClass);
        }
        else if (advisor instanceof PointcutAdvisor) {
            PointcutAdvisor pca = (PointcutAdvisor) advisor;
            return canApply(pca.getPointcut(), targetClass, hasIntroductions);
        }
        else {
            // It doesn't have a pointcut so we assume it applies.
            return true;
        }
    }

当前的 advisor 就是之前查找出来的BeanFactoryTransactionAttributeSourceAdvisor类型的 bean 实例。而通过类层次结构,我们知道:BeanFactoryTransactionAttributeSourceAdvisor间接实现了PointcutAdvisor,因此会进入 canApply 函数中的第二个 if 判断。会将BeanFactoryTransactionAttributeSourceAdvisor中的getPointcut()方法的返回值作为参数继续调用 canApply 方法,而 getPointcut()方法返回的是TransactionAttributeSourcePointcut类型的实例。

public class BeanFactoryTransactionAttributeSourceAdvisor extends AbstractBeanFactoryPointcutAdvisor {

    private TransactionAttributeSource transactionAttributeSource;

    private final TransactionAttributeSourcePointcut pointcut = new TransactionAttributeSourcePointcut() {
        @Override
        protected TransactionAttributeSource getTransactionAttributeSource() {
            return transactionAttributeSource;
        }
    };
    ...
}

transactionAttributeSource这个属性大家还有印象吗?这是在解析事务标签的时候注入进去的。

那么,使用TransactionAttributeSourcePointcut类型的实例作为函数参数继续跟踪canApply:

public static boolean canApply(Pointcut pc, Class<?> targetClass, boolean hasIntroductions) {
        Assert.notNull(pc, "Pointcut must not be null");
        if (!pc.getClassFilter().matches(targetClass)) {
            return false;
        }
        // 此时的 pc 表示 TransactionAttributeSourcePointcut
        //  pc.getMethodMatcher() 返回的还是自身(this)
        MethodMatcher methodMatcher = pc.getMethodMatcher();
        if (methodMatcher == MethodMatcher.TRUE) {
            // No need to iterate the methods if we're matching any method anyway...
            return true;
        }

        IntroductionAwareMethodMatcher introductionAwareMethodMatcher = null;
        if (methodMatcher instanceof IntroductionAwareMethodMatcher) {
            introductionAwareMethodMatcher = (IntroductionAwareMethodMatcher) methodMatcher;
        }
        // 获取对应类的所有接口连同类自身
        Set<Class<?>> classes = new LinkedHashSet<Class<?>>(ClassUtils.getAllInterfacesForClassAsSet(targetClass));
        classes.add(targetClass);
        for (Class<?> clazz : classes) {
            // 获取类中的所有方法
            Method[] methods = ReflectionUtils.getAllDeclaredMethods(clazz);
            for (Method method : methods) {
                if ((introductionAwareMethodMatcher != null &&
                        introductionAwareMethodMatcher.matches(method, targetClass, hasIntroductions)) ||
                        // 开始匹配操作
                        methodMatcher.matches(method, targetClass)) {
                    return true;
                }
            }
        }

        return false;
    }

通过上面的函数大致可以理清大体脉络,首先获取对应类的所有接口并连同类自身一起遍历,遍历过程中又对类的方法再次遍历,一旦匹配成功便认为这个类适用于当前增强器。

做匹配的时候,methodMatcher.matches(method, targetClass)会使用TransactionAttributeSourcePointcut#matches方法:

public boolean matches(Method method, Class<?> targetClass) {
        if (targetClass != null && TransactionalProxy.class.isAssignableFrom(targetClass)) {
            return false;
        }
        // 获取 TransactionAttributeSource,事务标签解析时注入
        TransactionAttributeSource tas = getTransactionAttributeSource();
        return (tas == null || tas.getTransactionAttribute(method, targetClass) != null);
    }

此时的tas表示AnnotationTransactionAttributeSource类型,于是我们跟踪AnnotationTransactionAttributeSource#getTransactionAttribute方法:

public TransactionAttribute getTransactionAttribute(Method method, Class<?> targetClass) {
        if (method.getDeclaringClass() == Object.class) {
            return null;
        }

        // 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 {
            // 获取事务属性
            TransactionAttribute txAttr = computeTransactionAttribute(method, targetClass);
            // Put it in the cache.
            if (txAttr == null) {
                this.attributeCache.put(cacheKey, NULL_TRANSACTION_ATTRIBUTE);
            }
            else {
                String methodIdentification = ClassUtils.getQualifiedMethodName(method, targetClass);
                if (txAttr instanceof DefaultTransactionAttribute) {
                    ((DefaultTransactionAttribute) txAttr).setDescriptor(methodIdentification);
                }
                if (logger.isDebugEnabled()) {
                    logger.debug("Adding transactional method '" + methodIdentification + "' with attribute: " + txAttr);
                }
                this.attributeCache.put(cacheKey, txAttr);
            }
            return txAttr;
        }
    }

该方法的作用是获取事务属性TransactionAttribute,而具体的操作是在computeTransactionAttribute方法中:

protected TransactionAttribute computeTransactionAttribute(Method method, Class<?> targetClass) {
        // 必须是 public 方法,否则代理类无法调用该方法
        if (allowPublicMethodsOnly() && !Modifier.isPublic(method.getModifiers())) {
            return null;
        }

        // Ignore CGLIB subclasses - introspect the actual user class.
        Class<?> userClass = ClassUtils.getUserClass(targetClass);
        // method 为接口中的方法,specificMethod 为实现类中的方法
        Method specificMethod = ClassUtils.getMostSpecificMethod(method, userClass);
        // 处理桥生方法:父类是泛型,子类是具体类型而滋生的类型转换方法
        specificMethod = BridgeMethodResolver.findBridgedMethod(specificMethod);

        // 查看方法是否存在事务声明
        TransactionAttribute txAttr = findTransactionAttribute(specificMethod);
        if (txAttr != null) {
            return txAttr;
        }

        // 查看方法所在的类是否存在事务声明
        txAttr = findTransactionAttribute(specificMethod.getDeclaringClass());
        if (txAttr != null && ClassUtils.isUserLevelMethod(method)) {
            return txAttr;
        }
        // 如果存在接口,则到接口中去寻找
        if (specificMethod != method) {
            // Fallback is to look at the original method.
            txAttr = findTransactionAttribute(method);
            if (txAttr != null) {
                return txAttr;
            }
            // Last fallback is the class of the original method.
            txAttr = findTransactionAttribute(method.getDeclaringClass());
            if (txAttr != null && ClassUtils.isUserLevelMethod(method)) {
                return txAttr;
            }
        }

        return null;
    }

对于事务属性的获取规则相信大家都很清楚了。如果方法中存在事务声明,则使用方法上的事务属性,否则使用方法所在类上的事务属性;如果方法所在类上没有事务声明,则再搜索接口上的方法,再没有的话,最后搜索接口的类上的事务声明。

搜索事务声明的任务委托给了findTransactionAttribute方法执行:

    protected TransactionAttribute findTransactionAttribute(Class<?> clazz) {
        return determineTransactionAttribute(clazz);
    }

    protected TransactionAttribute determineTransactionAttribute(AnnotatedElement ae) {
        if (ae.getAnnotations().length > 0) {
            for (TransactionAnnotationParser annotationParser : this.annotationParsers) {
                TransactionAttribute attr = annotationParser.parseTransactionAnnotation(ae);
                if (attr != null) {
                    return attr;
                }
            }
        }
        return null;
    }

this.annotationParsers是在当前类AnnotationTransactionAttributeSource实例化的时候(构造函数)初始化的,其中加入了SpringTransactionAnnotationParser,也就是当进行属性获取的时候其实是使用SpringTransactionAnnotationParser类的parseTransactionAnnotation方法进行解析的:

public TransactionAttribute parseTransactionAnnotation(AnnotatedElement ae) {
        AnnotationAttributes attributes = AnnotatedElementUtils.getMergedAnnotationAttributes(ae, Transactional.class);
        if (attributes != null) {
            return parseTransactionAnnotation(attributes);
        }
        else {
            return null;
        }
    }

至此,我们终于看到了想要看到的获取事务注解标记的代码。首先会判断当前的类是否含有Transactional注解,这是事务属性的基础。如果有的话,继续调用parseTransactionAnnotation方法解析详细的属性:

protected TransactionAttribute parseTransactionAnnotation(AnnotationAttributes attributes) {
        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"));
        ArrayList<RollbackRuleAttribute> rollBackRules = new ArrayList<RollbackRuleAttribute>();
        Class<?>[] rbf = attributes.getClassArray("rollbackFor");
        for (Class<?> rbRule : rbf) {
            RollbackRuleAttribute rule = new RollbackRuleAttribute(rbRule);
            rollBackRules.add(rule);
        }
        String[] rbfc = attributes.getStringArray("rollbackForClassName");
        for (String rbRule : rbfc) {
            RollbackRuleAttribute rule = new RollbackRuleAttribute(rbRule);
            rollBackRules.add(rule);
        }
        Class<?>[] nrbf = attributes.getClassArray("noRollbackFor");
        for (Class<?> rbRule : nrbf) {
            NoRollbackRuleAttribute rule = new NoRollbackRuleAttribute(rbRule);
            rollBackRules.add(rule);
        }
        String[] nrbfc = attributes.getStringArray("noRollbackForClassName");
        for (String rbRule : nrbfc) {
            NoRollbackRuleAttribute rule = new NoRollbackRuleAttribute(rbRule);
            rollBackRules.add(rule);
        }
        rbta.getRollbackRules().addAll(rollBackRules);
        return rbta;
    }

上面的方法实现了对事务属性的解析,你会在这个方法中看到任何你常用的或者不常用的属性提取。

至此,我们终于完成事务标签的解析。我们是不是分析的太远了,似乎已经忘记了从哪里开始了。再回顾一下,我们现在的任务就是找出某个增强器是否适用于对应的类,而是否匹配的关键在于是否从指定的类或者类中的方法上找到对应的事务属性。

至此,事务的准备阶段已经完成。当判断某个 bean 适用于事务增强时,也就是适用于事务增强器BeanFactoryTransactionAttributeSourceAdvisor,没错,还是这个类,所以说,在事务标签解析时,注入的类成为了整个事务功能的基础。

BeanFactoryTransactionAttributeSourceAdvisor作为Advisor的实现类,自然要遵循Advisor的处理方式,当代理被调用时会调用这个类的增强方法,也就是此 bean 的Advice,又因为在解析事务标签时我们把TransactionInterceptor类型的 bean 注入到了BeanFactoryTransactionAttributeSourceAdvisoradviceBeanName属性中。创建代理的时候,会调用Advisor#getAdvice方法,此时就会得到的该增强方法TransactionInterceptor,因为TransactionInterceptor实现了Advice,将其作为 CGLIB 的 CallBack 信息,@Transactional修饰方法所在的类作为 CGLIB 的父类信息,CGLIB 根据这些信息生成对应的子类的 Class 对象作为代理类。所以,在调用事务增强器增强的代理类时,会首先执行TransactionInterceptor进行增强,同时,也就是在TransactionInterceptor类中的invoke方法中完成了整个事务的逻辑。该部分逻辑我会单独拎出一篇文章来讲解。

2. 基于 SpringBoot

上面是基于 Spring 的事务标签"<annotation-driven/>"解析流程讲解,但是在去 xml 化的 SpringBoot 中,事务属性又是如何解析的呢?我们先看事务在 SpringBoot 中,是如何配置的。

2.1 事务配置

@Configuration
@EnableTransactionManagement
@MapperScan(basePackages = "xxx.mapper", sqlSessionFactoryRef = "sqlSessionFactory")
public class DataSourceConfig {

    @Bean(name = "dataSource")
    @ConfigurationProperties(prefix = "spring.datasource.xxx")
    public DataSource dataSource() {
        return new DruidDataSource();
    }

    @Bean(name = "sqlSessionFactory")
    public SqlSessionFactory sqlSessionFactory(@Qualifier("dataSource") DataSource dataSource) throws Exception {
        SqlSessionFactoryBean sessionFactoryBean = new SqlSessionFactoryBean();
        sessionFactoryBean.setDataSource(dataSource);
        org.apache.ibatis.session.Configuration configuration = new org.apache.ibatis.session.Configuration();
        configuration.setMapUnderscoreToCamelCase(Boolean.TRUE);
        configuration.setUseGeneratedKeys(Boolean.TRUE);
        sessionFactoryBean.setConfiguration(configuration);
        sessionFactoryBean.setMapperLocations(new PathMatchingResourcePatternResolver()
                .getResources("classpath:xxx.mapper/*.xml"));
        return sessionFactoryBean.getObject();
    }

    @Bean(name = "transactionManager")
    public DataSourceTransactionManager transactionManager(@Qualifier("dataSource") DataSource dataSource) {
        return new DataSourceTransactionManager(dataSource);
    }
}

在 SpringBoot 中,是通过@EnableTransactionManagement注解,开启事务管理的,类似于 Spring 中的事务标签"<annotation-driven/>"。我们可以大胆的猜测,该注解就是跟Spring 中的事务标签"<annotation-driven/>"做的事情是一致的,都是找出指定 bean 对应的事务增强器,然后根据事务增强器创建代理。为了验证我们的猜测,我们接下来看看这个注解。

2.2 事务 @EnableTransactionManagement 注解解析

首先,看EnableTransactionManagement注解的结构:

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import(TransactionManagementConfigurationSelector.class)
public @interface EnableTransactionManagement {

    // 是否需要使用 CGLIB
    boolean proxyTargetClass() default false;
    // 增强方式
    AdviceMode mode() default AdviceMode.PROXY;

    int order() default Ordered.LOWEST_PRECEDENCE;

}

这里使用了@Import注解来引入配置类,相当于 Spring 中的"<import/>"标签。@Import注解的参数可以是一个@Configuration配置类,也可以是一个ImportSelector接口,也可以是ImportBeanDefinitionRegistrar接口的实现类。如果是一个 @Configuration配置类,会将类中@Bean修饰的 bean 注册到 IOC 容器;如果是ImportSelector接口的实现类,那就会根据实现的逻辑对@Configuration配置类或者ImportBeanDefinitionRegistrar实现类进行筛选;如果是一个ImportBeanDefinitionRegistrar接口实现类,那么也会根据该实现类的逻辑来创建 Bean。

2.2.1 TransactionManagementConfigurationSelector

于是我们继续跟踪TransactionManagementConfigurationSelector:

public class TransactionManagementConfigurationSelector extends AdviceModeImportSelector<EnableTransactionManagement> {

    @Override
    protected String[] selectImports(AdviceMode adviceMode) {
        switch (adviceMode) {
            case PROXY:
                return new String[] {AutoProxyRegistrar.class.getName(), ProxyTransactionManagementConfiguration.class.getName()};
            case ASPECTJ:
                return new String[] {TransactionManagementConfigUtils.TRANSACTION_ASPECT_CONFIGURATION_CLASS_NAME};
            default:
                return null;
        }
    }

}

TransactionManagementConfigurationSelectorImportSelector接口的实现类,于是它会将selectImports方法返回的配置类名数组,根据配置信息来创建指定的 bean。同样,我们以默认配置“adviceMode == PROXY”为例,进行分析。其会生成两个配置类AutoProxyRegistrarProxyTransactionManagementConfiguration

2.2.2 注册 InfrastructureAdvisorAutoProxyCreator

我们先看AutoProxyRegistrar的结构:

public class AutoProxyRegistrar implements ImportBeanDefinitionRegistrar {

    private final Log logger = LogFactory.getLog(getClass());

    @Override
    public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
        boolean candidateFound = false;
        Set<String> annoTypes = importingClassMetadata.getAnnotationTypes();
        for (String annoType : annoTypes) {
            AnnotationAttributes candidate = AnnotationConfigUtils.attributesFor(importingClassMetadata, annoType);
            if (candidate == null) {
                continue;
            }
            Object mode = candidate.get("mode");
            Object proxyTargetClass = candidate.get("proxyTargetClass");
            if (mode != null && proxyTargetClass != null && AdviceMode.class == mode.getClass() &&
                    Boolean.class == proxyTargetClass.getClass()) {
                candidateFound = true;
                if (mode == AdviceMode.PROXY) {
                    // 注册  InfrastructureAdvisorAutoProxyCreator
                    AopConfigUtils.registerAutoProxyCreatorIfNecessary(registry);
                    if ((Boolean) proxyTargetClass) {
                        AopConfigUtils.forceAutoProxyCreatorToUseClassProxying(registry);
                        return;
                    }
                }
            }
        }
        ...
}

AutoProxyRegistrar实现了ImportBeanDefinitionRegistrar接口,于是就可以在registerBeanDefinitions方法中,注册自定义的 bean。注册了哪些 bean 呢?没错,你没看错,它注册的正是InfrastructureAdvisorAutoProxyCreator,跟 Spring 解析事务标签时这注册的同一个 bean。我们知道,Spring 在解析事务标签时,除了注册InfrastructureAdvisorAutoProxyCreator,还生成了三个 bean,分别是AnnotationTransactionAttributeSourceTransactionInterceptor以及BeanFactoryTransactionAttributeSourceAdvisor。在AutoProxyRegistrar并没有找到这些逻辑,于是我们猜测,应该在ProxyTransactionManagementConfiguration中。

2.2.3 配置事务相关的 bean

我们继续跟踪ProxyTransactionManagementConfiguration:

@Configuration
public class ProxyTransactionManagementConfiguration extends AbstractTransactionManagementConfiguration {

    @Bean(name = TransactionManagementConfigUtils.TRANSACTION_ADVISOR_BEAN_NAME)
    @Role(BeanDefinition.ROLE_INFRASTRUCTURE)
    public BeanFactoryTransactionAttributeSourceAdvisor transactionAdvisor() {
        BeanFactoryTransactionAttributeSourceAdvisor advisor = new BeanFactoryTransactionAttributeSourceAdvisor();
        advisor.setTransactionAttributeSource(transactionAttributeSource());
        advisor.setAdvice(transactionInterceptor());
        advisor.setOrder(this.enableTx.<Integer>getNumber("order"));
        return advisor;
    }

    @Bean
    @Role(BeanDefinition.ROLE_INFRASTRUCTURE)
    public TransactionAttributeSource transactionAttributeSource() {
        return new AnnotationTransactionAttributeSource();
    }

    @Bean
    @Role(BeanDefinition.ROLE_INFRASTRUCTURE)
    public TransactionInterceptor transactionInterceptor() {
        TransactionInterceptor interceptor = new TransactionInterceptor();
        interceptor.setTransactionAttributeSource(transactionAttributeSource());
        if (this.txManager != null) {
            interceptor.setTransactionManager(this.txManager);
        }
        return interceptor;
    }
}

不出我们所料,ProxyTransactionManagementConfiguration是一个典型的配置类,它创建了事务准备阶段所需要的3个 bean,AnnotationTransactionAttributeSourceTransactionInterceptor以及BeanFactoryTransactionAttributeSourceAdvisor。并且前面两个 bean 都注入到了BeanFactoryTransactionAttributeSourceAdvisor类型的 bean 中。

至此,@EnableTransactionManagement注解,完成了和 Spring 中事务标签的解析工作所做的事情。后面 SpringBoot 继承了 Spring 的逻辑,依据这一个代理和 3 个 bean,找出指定 bean 对应的事务增强器,然后根据事务增强器创建代理,从而完成了事务的准备阶段。

3. 结语

本文分别基于 Spring 和 SpringBoot,介绍了@Transactional注解在事务准备阶段所做的事情。

  1. 找出指定 bean 对应的事务增强器。
  2. 验证事务增强器与对应 bean 是否匹配(对应 bean 的类或者类中的方法上是否有事务属性)。
  3. 根据事务增强器创建代理。

创建代理之后,当程序调用事务方法时,就会走代理流程,下一篇文章我们将继续探讨。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,390评论 5 459
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,821评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,632评论 0 319
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,170评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,033评论 4 355
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,098评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,511评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,204评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,479评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,572评论 2 309
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,341评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,213评论 3 312
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,576评论 3 298
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,893评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,171评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,486评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,676评论 2 335

推荐阅读更多精彩内容