42--Spring tx:annotation-driven 标签解析过程

上一节演示了基于tx标签和基于@Transactional注解的声明式事物的使用过程,本节分析一下基于@Transactional注解的声明式事物的实现过程。

1.快速定位Spring自定义标签解析入口

在上节的配置文件中,通过<tx:annotation-driven transaction-manager="transactionManager"/>开启了注解事物,以此为入口,开始今天的分析。

tx是Spring的自定义标签,在32--aspectj-autoproxy解析及Spring解析自定义标签一节中已经分析过自定义标签的解析过程,Spring通过继承NamespaceHandler自定义命名空间的解析,以此推论,tx标签必然也有自己的命名空间解析器,通过全局搜索,可以找到TxNamespaceHandler类下包含了annotation-driven。通过该方法如果大家再遇到Spring的自定义标签,可通过此方式为入口,快速找到分析入口所在。

2.<tx:annotation-driven/>解析过程

按照我们之前章节的分析,注册了BeanDefinitionParser之后紧接着就要调用其parse方法对xml标签进行解析。(不清楚该过程的可以参考:32--aspectj-autoproxy解析及Spring解析自定义标签中的分析)。

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());
        // 注册AnnotationDrivenBeanDefinitionParser
        registerBeanDefinitionParser("annotation-driven", new AnnotationDrivenBeanDefinitionParser());
        registerBeanDefinitionParser("jta-transaction-manager", new JtaTransactionManagerBeanDefinitionParser());
    }

}

在TxNamespaceHandler中完成了对AnnotationDrivenBeanDefinitionParser的注册,接下来就要调用其parse方法。

@Override
@Nullable
public BeanDefinition parse(Element element, ParserContext parserContext) {
    // 1、注册TransactionalEventListenerFactory
    registerTransactionalEventListenerFactory(parserContext);
    // 2、解析标签的mode属性
    String mode = element.getAttribute("mode");
    if ("aspectj".equals(mode)) {
        // mode="aspectj"
        // aspectj模式
        registerTransactionAspect(element, parserContext);
        if (ClassUtils.isPresent("javax.transaction.Transactional", getClass().getClassLoader())) {
            registerJtaTransactionAspect(element, parserContext);
        }
    }
    else {
        // mode="proxy"
        // proxy模式
        AopAutoProxyConfigurer.configureAutoProxyCreator(element, parserContext);
    }
    return null;
}

在parse方法里,首先注册了TransactionalEventListenerFactory,然后解析<tx:annotation-driven/>的mode属性,这里我们主要分析proxy模式。

/**
 * 在代理模式下引入AOP框架依赖关系的内部类。
 * Inner class to just introduce an AOP framework dependency when actually in proxy mode.
 */
private static class AopAutoProxyConfigurer {

    public static void configureAutoProxyCreator(Element element, ParserContext parserContext) {
        // 1、注册InfrastructureAdvisorAutoProxyCreator  Infrastructure-->基础设施
        // 注意:区分@AspectJ:@AspectJ注册的是AnnotationAwareAspectJAutoProxyCreator
        AopNamespaceUtils.registerAutoProxyCreatorIfNecessary(parserContext, element);

        // TRANSACTION_ADVISOR_BEAN_NAME-->org.springframework.transaction.config.internalTransactionAdvisor
        String txAdvisorBeanName = TransactionManagementConfigUtils.TRANSACTION_ADVISOR_BEAN_NAME;

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

            // Create the TransactionAttributeSource definition.
            // 2、创建AnnotationTransactionAttributeSource的BeanDefinition
            RootBeanDefinition sourceDef = new RootBeanDefinition(
                    "org.springframework.transaction.annotation.AnnotationTransactionAttributeSource");
            sourceDef.setSource(eleSource);
            // 设置Role属性,ROLE_INFRASTRUCTURE表示Spring的内部bean
            sourceDef.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
            // 生成bean名称
            String sourceName = parserContext.getReaderContext().registerWithGeneratedName(sourceDef);

            // Create the TransactionInterceptor definition.
            // 3、创建TransactionInterceptor的BeanDefinition
            RootBeanDefinition interceptorDef = new RootBeanDefinition(TransactionInterceptor.class);
            interceptorDef.setSource(eleSource);
            // 设置Role属性,ROLE_INFRASTRUCTURE表示Spring的内部bean
            interceptorDef.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
            // 注册事物管理器
            registerTransactionManager(element, interceptorDef);
            // 将AnnotationTransactionAttributeSource注入到TransactionInterceptor的transactionAttributeSource属性中
            interceptorDef.getPropertyValues().add("transactionAttributeSource", new RuntimeBeanReference(sourceName));
            // 生成bean名称
            String interceptorName = parserContext.getReaderContext().registerWithGeneratedName(interceptorDef);

            // Create the TransactionAttributeSourceAdvisor definition.
            // 4、创建BeanFactoryTransactionAttributeSourceAdvisor的BeanDefinition
            RootBeanDefinition advisorDef = new RootBeanDefinition(BeanFactoryTransactionAttributeSourceAdvisor.class);
            advisorDef.setSource(eleSource);
            // 设置Role属性,ROLE_INFRASTRUCTURE表示Spring的内部bean
            advisorDef.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
            // 将上一步创建的AnnotationTransactionAttributeSource注入到
            // BeanFactoryTransactionAttributeSourceAdvisor的transactionAttributeSource属性中
            advisorDef.getPropertyValues().add("transactionAttributeSource", new RuntimeBeanReference(sourceName));
            // 将上一步创建的TransactionInterceptor注入到
            // BeanFactoryTransactionAttributeSourceAdvisor的adviceBeanName属性中
            advisorDef.getPropertyValues().add("adviceBeanName", interceptorName);
            // 解析<tx:annotation-driven>的order属性标签
            if (element.hasAttribute("order")) {
                advisorDef.getPropertyValues().add("order", element.getAttribute("order"));
            }
            // 将TransactionAttributeSourceAdvisor以txAdvisorBeanName为名称注册到IOC容器中
            parserContext.getRegistry().registerBeanDefinition(txAdvisorBeanName, advisorDef);

            /**
             * 通过上面的操作:
             * 1、创建AnnotationTransactionAttributeSource的BeanDefinition
             * 2、创建TransactionInterceptor的BeanDefinition
             * 3、创建TransactionAttributeSourceAdvisor的BeanDefinition,
             *    并将第一步,第二步创建的BeanDefinition注入到transactionAttributeSource和adviceBeanName中
             *
             *    BeanFactoryTransactionAttributeSourceAdvisor-->TransactionInterceptor
             *    BeanFactoryTransactionAttributeSourceAdvisor-->AnnotationTransactionAttributeSource
             */
            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);
        }
    }
}

上面的这段代码,看起来枯燥无比,但这段代是Spring声明式事物的核心所在,下面对其中的细节逐一分析。

2.1注册InfrastructureAdvisorAutoProxyCreator

在之前的章节分析SpringAOP时,曾经注册了AnnotationAwareAspectJAutoProxyCreator,该类的作用是为目标对象自动创建代理,该类间接实现了BeanPostProcessor接口,所以会在bean的实例化前后调用postProcessBeforeInstantiation和postProcessAfterInitialization方法,这些在之前的章节中已经反复讲解过。

那么InfrastructureAdvisorAutoProxyCreator类是否有同样的作用呢?来查看一下其类继承关系不难发现其也间接实现了BeanPostProcessor接口,那么只要找到该类或者其父类中的postProcessBeforeInstantiation方法和postProcessAfterInitialization方法,就不难分析出其作用。这了也是一点点源码分析的小技巧,关于此类的作用,我们留在后面分析,接下来继续看configureAutoProxyCreator方法。


在这里插入图片描述
2.2 创建AnnotationTransactionAttributeSource的BeanDefinition

该过程比较简单,但是要分析一下AnnotationTransactionAttributeSource的作用。打开该类的代码,看起来毫无头绪,这时候不妨查看一下其类继承关系,或许就有新的发现,这也是阅读源码的小技巧。

在这里插入图片描述

可以看到该类实现了TransactionAttributeSource接口,该接口只提供了一个获取TransactionAttribute的方法TransactionAttribute getTransactionAttribute(Method method, @Nullable Class<?> targetClass);。TransactionAttribute实现了TransactionDefinition接口,该接口提供了Spring事物的传播特性以及事物隔离级别等定义。
综合上述分析,大致可以了解到AnnotationTransactionAttributeSource已经可以获取到事物属性,再通过该类是Annotation开头,也可以再次猜测该类是获取类、接口、方法上的事物注解属性。当然这里只是猜测,到底是不是呢?我们留在后面分析。

2.3 创建TransactionInterceptor的BeanDefinition

TransactionInterceptor是Spring声明式事物管理器的基础,查看其类图结构,TransactionInterceptor继承了TransactionAspectSupport并实现了MethodInterceptor接口。TransactionAspectSupport提供了对事物支持的能力,而MethodInterceptor我们之前已经介绍过,可以用来实现环绕增强。从此我们大致可以推断出,Spring的事物管理是基于环绕增强的。该类的具体方法以及调用过程留在后面分析。


在这里插入图片描述
2.4 创建TransactionAttributeSourceAdvisor的BeanDefinition并注册bean

上面已经创建了事物属性定义、事物增强定义、那么接下来就应该创建切面了。


在这里插入图片描述

从类图上看到BeanFactoryTransactionAttributeSourceAdvisor是PointcutAdvisor类型的切面,创建了该bean的定义之后,又将上面创建的AnnotationTransactionAttributeSource注入到transactionAttributeSource属性中;将TransactionInterceptor注入到adviceBeanName属性中。

当然分析到这里的时候,大家有可能会非常迷茫,不过没关系,我们在后面的章节会详细分析。

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

推荐阅读更多精彩内容