springboot事务管理源码分析--1.启动分析

本文基于springboot(1.5以上)的事务管理的开篇,主要关于Transaction注解的生效过程

Transaction注解怎么生效

springboot开启事务管理需要在启动类加上@EnableTransactionManagement,EnableTransactionManagement使用@Import导入TransactionManagementConfigurationSelector类,TransactionManagementConfigurationSelector实现了ImportSelector接口

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

默认情况下是proxy 模式,会导入AutoProxyRegistrar(将InfrastructureAdvisorAutoProxyCreator纳入spring管理,但是后续会被高优先级的AnnotationAwareAspectJAutoProxyCreator替代)和ProxyTransactionManagementConfiguration。

1.先分析AutoProxyRegistrar

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;
            }
            // springboot中如果在main函数所在的Application类中加了EnableTransactionManagement注解
            // 下面这个判断就会成立,默认情况下mode是proxy,就会使用AopConfigUtils
            // 将InfrastructureAdvisorAutoProxyCreator加入spring容器
            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) {
                    AopConfigUtils.registerAutoProxyCreatorIfNecessary(registry);
                    if ((Boolean) proxyTargetClass) {
                        AopConfigUtils.forceAutoProxyCreatorToUseClassProxying(registry);
                        return;
                    }
                }
            }
        }
    }

AutoProxyRegistrar是一个实现了ImportBeanDefinitionRegistrar接口的类, AopConfigUtils.registerAutoProxyCreatorIfNecessary(registry)会将InfrastructureAdvisorAutoProxyCreator加入了spring管理,但是真正用来创建代理的是AnnotationAwareAspectJAutoProxyCreator类,看下面这张继承关系图,两者都继承了AbstractAdvisorAutoProxyCreator,主要功能是类似的,那么AnnotationAwareAspectJAutoProxyCreator又是什么时候引入spring容器的呢?

Screenshot from 2019-01-22 10-25-46.png

这里就要涉及springboot是怎么实现自动配置的了。@SpringBootApplication会引入@EnableAutoConfiguration,这个注解会使用@Import导入 EnableAutoConfigurationImportSelector,此类的父类AutoConfigurationImportSelector实现了ImportSelector,在selectImports方法中使用AutoConfigurationMetadataLoader.loadMetadata(this.beanClassLoader)导入META-INF/spring-autoconfigure-metadata.properties,导入@EnableAspectJAutoProxy,而@EnableAspectJAutoProxy又使用@Import导入了AspectJAutoProxyRegistrar,在这个类的selectImports会注册上AnnotationAwareAspectJAutoProxyCreator这个Bean,也就是说其实只要是springboot项目都会导入这个Bean,而由于在AopConfigUtils将AnnotationAwareAspectJAutoProxyCreator和InfrastructureAdvisorAutoProxyCreator做了排序,如果存在AnnotationAwareAspectJAutoProxyCreator,那么InfrastructureAdvisorAutoProxyCreator就不会生效。

那什么时候会为加了Transaction注解的方法或者类加上事务管理功能呢?

2.分析AnnotationAwareAspectJAutoProxyCreator

AnnotationAwareAspectJAutoProxyCreator实现了AbstractAutoProxyCreator抽象类,AbstractAutoProxyCreator又实现了BeanPostProcessor接口,熟悉spring IOC的都知道在每个Bean执行完初始化方法后会执行BeanPostProcessor接口的postProcessAfterInitialization方法,AbstractAutoProxyCreator的postProcessAfterInitialization就会判断是否需要为当前Bean生成代理,那是怎么做的呢?

public Object postProcessAfterInitialization(@Nullable Object bean, String beanName) {
        if (bean != null) {
            Object cacheKey = getCacheKey(bean.getClass(), beanName);
            if (!this.earlyProxyReferences.contains(cacheKey)) {
                // Transaction注解就会在这里生成代理类
                return wrapIfNecessary(bean, beanName, cacheKey);
            }
        }
        return bean;
    }

AbstractAutoProxyCreator的postProcessAfterInitialization方法会判断是否需要为当前正在创建的Bean创建一个代理的包装类。

protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) {
        if (StringUtils.hasLength(beanName) && this.targetSourcedBeans.contains(beanName)) {
            return bean;
        }
        if (Boolean.FALSE.equals(this.advisedBeans.get(cacheKey))) {
            return bean;
        }
        if (isInfrastructureClass(bean.getClass()) || shouldSkip(bean.getClass(), beanName)) {
            this.advisedBeans.put(cacheKey, Boolean.FALSE);
            return bean;
        }

        // 如果使用了Transaction注解这里就会返回拦截器,返回BeanFactoryTransactionAttributeSourceAdvisor
        // Create proxy if we have advice.
        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;
    }

主要关注getAdvicesAndAdvisorsForBean方法,在AbstractAdvisorAutoProxyCreator类中,会调用findEligibleAdvisors方法查找符合要求的Advisor。

protected List<Advisor> findEligibleAdvisors(Class<?> beanClass, String beanName) {
        // 查找候选advisor
        List<Advisor> candidateAdvisors = findCandidateAdvisors();
        // 使用AopUtils.findAdvisorsThatCanApply判断时候能应用
        List<Advisor> eligibleAdvisors = findAdvisorsThatCanApply(candidateAdvisors, beanClass, beanName);
        extendAdvisors(eligibleAdvisors);
        if (!eligibleAdvisors.isEmpty()) {
            eligibleAdvisors = sortAdvisors(eligibleAdvisors);
        }
        return eligibleAdvisors;
    }

findEligibleAdvisors方法会通过findCandidateAdvisors查找spring容器中所有存在的Advisor,findCandidateAdvisors掠过不分析,其实也很简单,可以自己分析(主要调用的是AnnotationAwareAspectJAutoProxyCreator的findCandidateAdvisors)。主要分析findAdvisorsThatCanApply方法。

public static List<Advisor> findAdvisorsThatCanApply(List<Advisor> candidateAdvisors, Class<?> clazz) {
        if (candidateAdvisors.isEmpty()) {
            return candidateAdvisors;
        }
        List<Advisor> eligibleAdvisors = new ArrayList<>();
        for (Advisor candidate : candidateAdvisors) {
            // IntroductionAdvisor是为每个对象做的代理,事务是针对每个类的,这个不会成立
            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;
            }
            // 主要是在这里看是否能够应用
            if (canApply(candidate, clazz, hasIntroductions)) {
                eligibleAdvisors.add(candidate);
            }
        }
        return eligibleAdvisors;
    }

调用当前类的canApply方法判断Advisor是否适合当前Bean。

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

那么这里的PointcutAdvisor又是哪一个类的功能呢?

3.分析TransactionManagementConfigurationSelector

是否还记得之前的在TransactionManagementConfigurationSelector导入了两个类,一个是AutoProxyRegistrar,我们已经分析了,另一个就是ProxyTransactionManagementConfiguration,
在ProxyTransactionManagementConfiguration会创建BeanFactoryTransactionAttributeSourceAdvisor,AnnotationTransactionAttributeSource和TransactionInterceptor这三个bean。

此时BeanFactoryTransactionAttributeSourceAdvisor就派上了用场,BeanFactoryTransactionAttributeSourceAdvisor中维护了一个pointcut:TransactionAttributeSourcePointcut,canApply中返回的pointcut就是这个pointcut,那又是怎么判断这个advisor是否适合当前正在创建的Bean呢?

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;
        }
        // 这里返回TransactionAttributeSourcePointcut自己就是一个MethodMatcher,这里返回的是自己
        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) {
            //获取正在创建Bean的所有方法,introductionAwareMethodMatcher我们忽略,调用的是methodMatcher.matches来判断匹配
            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来判断当前正在创建的Bean的每一个方法是否能应用当前Advisor,我们再来看这个matches方法:

public boolean matches(Method method, Class<?> targetClass) {
        if (TransactionalProxy.class.isAssignableFrom(targetClass)) {
            return false;
        }
        TransactionAttributeSource tas = getTransactionAttributeSource();
        return (tas == null || tas.getTransactionAttribute(method, targetClass) != null);
    }

而getTransactionAttributeSource()方法中返回的就是ProxyTransactionManagementConfiguration中创建的另一个Bean:AnnotationTransactionAttributeSource,让我们再来看AnnotationTransactionAttributeSource的getTransactionAttribute方法,AnnotationTransactionAttributeSource继承了AbstractFallbackTransactionAttributeSource,真正使用的是AbstractFallbackTransactionAttributeSource的getTransactionAttribute方法。

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

        // First, see if we have a cached value.
        Object cacheKey = getCacheKey(method, targetClass);
        TransactionAttribute 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 cached;
            }
        }
        else {
            // 通过这里计算是否存在Transaction注解,其他代码是在做缓存,避免重复计算
            // We need to work it out.
            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.isTraceEnabled()) {
                    logger.trace("Adding transactional method '" + methodIdentification + "' with attribute: " + txAttr);
                }
                this.attributeCache.put(cacheKey, txAttr);
            }
            return txAttr;
        }
    }

我们只看这个方法中的TransactionAttribute txAttr = computeTransactionAttribute(method, targetClass);这一行代码,其他代码是在做缓存,避免重复计算,computeTransactionAttribute会使用子类实现的findTransactionAttribute来查找注解,最终会调用determineTransactionAttribute来查找注解。

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

AnnotationTransactionAttributeSource初始化时会添加SpringTransactionAnnotationParser,这里的parseTransactionAnnotation调用的就是SpringTransactionAnnotationParser的parseTransactionAnnotation方法。

public TransactionAttribute parseTransactionAnnotation(AnnotatedElement element) {
        // 查找方法使用了@Transactional相关的属性
        AnnotationAttributes attributes = AnnotatedElementUtils.findMergedAnnotationAttributes(
                element, Transactional.class, false, false);
        if (attributes != null) {
            // 将所有注解属性拼成一个RuleBasedTransactionAttribute返回
            return parseTransactionAnnotation(attributes);
        }
        else {
            return null;
        }
    }

如果存在Tranction注解,最终parseTransactionAnnotation会返回一个RuleBasedTransactionAttribute,里面包含了@Transaction的相关属性比如传播行为,隔离级别等,后面事务管理相关文章会分析这个类。
返回RuleBasedTransactionAttribute后,canApply方法就会返回true,让我们再回到wrapIfNecessary方法。

protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) {
        if (StringUtils.hasLength(beanName) && this.targetSourcedBeans.contains(beanName)) {
            return bean;
        }
        if (Boolean.FALSE.equals(this.advisedBeans.get(cacheKey))) {
            return bean;
        }
        if (isInfrastructureClass(bean.getClass()) || shouldSkip(bean.getClass(), beanName)) {
            this.advisedBeans.put(cacheKey, Boolean.FALSE);
            return bean;
        }

        // 如果使用了Transaction注解这里就会返回拦截器,返回BeanFactoryTransactionAttributeSourceAdvisor
        // Create proxy if we have advice.
        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;
    }

canApply返回true后,getAdvicesAndAdvisorsForBean就会将BeanFactoryTransactionAttributeSourceAdvisor作为符合要求的Advisor返回,接下来就进入了创建代理的过程,我们知道一个Advisor中真正对方法调用起作用的是Advice,而BeanFactoryTransactionAttributeSourceAdvisor使用的Advice就是ProxyTransactionManagementConfiguration中创建的最后一个Bean:TransactionInterceptor。

创建完代理后返回postProcessAfterInitialization,spring就会用这个被代理的对象替换之前传入的Bean,这样就为添加了Transaction注解的Bean添加了代理,引入了事务管理功能。

下一篇文章我们再来看看TransactionInterceptor是怎么起作用的。

写在最后

本篇文章是springboot事务管理的第一篇,鉴于本人才疏学浅,如有不对之处欢迎指正,包括文章的错误和分析的思路等等,大家一起进步。

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

推荐阅读更多精彩内容