43--Spring @Transactional注解的声明式事物事物标签提取

1.引

上一节分析了<tx:annotation-driven/> 标签的解析过程,并简单介绍了InfrastructureAdvisorAutoProxyCreator,这一节接着分析Spring事物的实现过程,InfrastructureAdvisorAutoProxyCreator注册后的后续操作,InfrastructureAdvisorAutoProxyCreator实现了BeanPostProcessor接口,那么接下来我们就以BeanPostProcessor接口的postProcessBeforeInstantiation方法和postProcessAfterInitialization为入口,开始今天的分析。(不了解BeanPostProcessor或者bean生命周期的同学可以参考前面的章节,都有介绍)

接下来的分析可能在分析AOP的时候有过介绍,但是大家要注意分析其中的一些不同点。

postProcessBeforeInstantiation方法在之前已经有过详细的介绍,这里我们不再分析,重点看postProcessAfterInitialization。而且接下来很大一部分的方法之前都有分析过,我们只分析事物标签提取的过程。其他的过程不在详解。

2.postProcessAfterInitialization方法简析
/**
 * 如果bean被子类标识为要代理的bean,则使用配置的拦截器创建代理。
 * Create a proxy with the configured interceptors if the bean is
 * identified as one to proxy by the subclass.
 * @see #getAdvicesAndAdvisorsForBean
 */
@Override
public Object postProcessAfterInitialization(@Nullable Object bean, String beanName) {
    if (bean != null) {
        // 为beanName和beanClass构建缓存key
        Object cacheKey = getCacheKey(bean.getClass(), beanName);
        if (!this.earlyProxyReferences.contains(cacheKey)) {
            // 包装bean(创建代理的入口方法)
            return wrapIfNecessary(bean, beanName, cacheKey);
        }
    }
    return bean;
}
protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) {
    // 1、如果已经处理过或者不需要创建代理,则返回
    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;
    }

    // 2、创建代理
    // 2.1 根据指定的bean获取所有的适合该bean的增强
    Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null);
    if (specificInterceptors != DO_NOT_PROXY) {
        // 2.2 为指定bean创建代理
        this.advisedBeans.put(cacheKey, Boolean.TRUE);
        Object proxy = createProxy(bean.getClass(), beanName, specificInterceptors, new SingletonTargetSource(bean));
        this.proxyTypes.put(cacheKey, proxy.getClass());
        return proxy;
    }

    // 3、缓存
    this.advisedBeans.put(cacheKey, Boolean.FALSE);
    return bean;
}

这里又涉及到了增强的提取,套路还是一样,先找出所有的增强,再找出适合当前类的增强。

3.查找适合当前类的增强
protected Object[] getAdvicesAndAdvisorsForBean(Class<?> beanClass, String beanName, @Nullable TargetSource targetSource) {
    /**
     * 获取符合条件的增强并返回
     */
    List<Advisor> advisors = findEligibleAdvisors(beanClass, beanName);
    // 如果获取到的增强是个空的集合,则返回DO_NOT_PROXY-->空数组
    if (advisors.isEmpty()) {
        return DO_NOT_PROXY;
    }
    // 将获取到的增强转换为数组并返回
    return advisors.toArray();
}
/**
 * 为当前bean获取所有需要自动代理的增强
 */
protected List<Advisor> findEligibleAdvisors(Class<?> beanClass, String beanName) {
    // 1、查找所有候选增强
    List<Advisor> candidateAdvisors = findCandidateAdvisors();
    // 2、从所有增强集合中查找适合当前bean的增强
    List<Advisor> eligibleAdvisors = findAdvisorsThatCanApply(candidateAdvisors, beanClass, beanName);
    // 3、在eligibleAdvisors集合首位加入ExposeInvocationInterceptor增强
    // ExposeInvocationInterceptor的作用是可以将当前的MethodInvocation暴露为一个thread-local对象,该拦截器很少使用
    // 使用场景:一个切点(例如AspectJ表达式切点)需要知道它的全部调用上线文环境
    extendAdvisors(eligibleAdvisors);
    if (!eligibleAdvisors.isEmpty()) {
        // 4.对增强进行排序
        eligibleAdvisors = sortAdvisors(eligibleAdvisors);
    }
    return eligibleAdvisors;
}
3.1 查找所有增强
public List<Advisor> findAdvisorBeans() {
    // Determine list of advisor bean names, if not cached already.
    // 获取缓存的增强
    String[] advisorNames = this.cachedAdvisorBeanNames;
    // 缓存增强为空,则重新查找增强并缓存
    if (advisorNames == null) {
        // Do not initialize FactoryBeans here: We need to leave all regular beans
        // uninitialized to let the auto-proxy creator apply to them!
        // 从当前BeanFactory中获取所有类型为Advisor的bean
        advisorNames = BeanFactoryUtils.beanNamesForTypeIncludingAncestors(
                this.beanFactory,
                Advisor.class,
                true,
                false);
        this.cachedAdvisorBeanNames = advisorNames;
    }
    // 当前BeanFactory中没有类型为Advisor的bean则返回一个空的集合
    if (advisorNames.length == 0) {
        return new ArrayList<>();
    }

    // 循环所有获取到的bean
    List<Advisor> advisors = new ArrayList<>();
    for (String name : advisorNames) {
        if (isEligibleBean(name)) {
            // 跳过正在创建的增强
            if (this.beanFactory.isCurrentlyInCreation(name)) {
                if (logger.isDebugEnabled()) {
                    logger.debug("Skipping currently created advisor '" + name + "'");
                }
            }
            else {
                try {
                    // 通过getBean方法获取bean实例
                    advisors.add(this.beanFactory.getBean(name, Advisor.class));
                }
                catch (BeanCreationException ex) {
                    Throwable rootCause = ex.getMostSpecificCause();
                    if (rootCause instanceof BeanCurrentlyInCreationException) {
                        BeanCreationException bce = (BeanCreationException) rootCause;
                        String bceBeanName = bce.getBeanName();
                        if (bceBeanName != null && this.beanFactory.isCurrentlyInCreation(bceBeanName)) {
                            // 跳过正在创建的增强
                            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;
}

注意这里与之前分析AOP的不同点
此处,我们没有在配置文件、类中描述任何增强增强信息(例如:在之前的AOP分析中,我们在DogAspect类上开启了@Aspect),那么此处应该获取到什么样的增强信息呢?

这时候就用到了上一节分析的内容了,在上一节手动向容器中注册了TransactionInterceptor,那么这里获取到的增强就是TransactionInterceptor,并通过getBean方法将其实例化。那么到这里,我们已经拿到了所有增强(如果没有配置其他的增强的话)

3.2 查找适合当前类的增强

这里其他的非关键代码就不粘贴了,前面都有过介绍,直接来看canApply方法(不了解其中过程的同学可以参考前面AOP的介绍),在canApply方法中有这样的代码片段:

// 5、循环代理目标的所有接口和实现类的所有方法并调用matches方法做匹配判断
for (Class<?> clazz : classes) {
    Method[] methods = ReflectionUtils.getAllDeclaredMethods(clazz);
    for (Method method : methods) {
        if (introductionAwareMethodMatcher != null ?
                // 如果上一步得到的introductionAwareMethodMatcher对象不为空,则使用该对象的matches匹配
                introductionAwareMethodMatcher.matches(method, targetClass, hasIntroductions) :
                // 否则使用Pointcut的methodMatcher对象做匹配
                methodMatcher.matches(method, targetClass)) {
            return true;
        }
    }
}

这里会调用methodMatcher.matches(method, targetClass)完成目标类方法的匹配工作,并在此方法里完成了事物标签(注解)的提取工作。

public boolean matches(Method method, Class<?> targetClass) {
    if (TransactionalProxy.class.isAssignableFrom(targetClass)) {
        return false;
    }
    // 获取事物属性源
    TransactionAttributeSource tas = getTransactionAttributeSource();
    // 如果获取事物属性源为空,或者没有提取到事物标签,那么返回false,表示改目标类不会被事物增强代理
    // 在getTransactionAttribute完成了事物标签的提取工作
    return (tas == null || tas.getTransactionAttribute(method, targetClass) != null);
}
4.事物标签(注解)提取
public TransactionAttribute getTransactionAttribute(Method method, @Nullable Class<?> targetClass) {
    if (method.getDeclaringClass() == Object.class) {
        return null;
    }

    // 1.检查是否缓存过该方法上的事物标签
    // 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.
        // 判断缓存的事物标签,如果缓存的方法上没有具体的事物标签,返回null;否则返回对应的事物标签
        if (cached == NULL_TRANSACTION_ATTRIBUTE) {
            return null;
        } else {
            return (TransactionAttribute) cached;
        }
    }
    // 2.如果没有缓存的事物标签,则重新提取事物标签并缓存
    else {
        // 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.isDebugEnabled()) {
                logger.debug("Adding transactional method '" + methodIdentification + "' with attribute: " + txAttr);
            }
            this.attributeCache.put(cacheKey, txAttr);
        }
        return txAttr;
    }
}

该方法比较简单,我们继续看computeTransactionAttribute提取事物标签的过程:

protected TransactionAttribute computeTransactionAttribute(Method method, @Nullable Class<?> targetClass) {
    // Don't allow no-public methods as required.
    // 1.如果设置了只允许公共(public)方法允许定义事物语义,而该方法是非公共(public)的则返回null
    // allowPublicMethodsOnly --> 该方法默认返回false,即允许非公共方法定义事物语义
    if (allowPublicMethodsOnly() && !Modifier.isPublic(method.getModifiers())) {
        return null;
    }

    // The method may be on an interface, but we need attributes from the target class.
    // If the target class is null, the method will be unchanged.
    // 2.如果method是一个接口,那么根据该接口和目标类,找到目标类上的对应的方法(如果有)
    // 注意:该原始方法不一定是接口方法
    Method specificMethod = AopUtils.getMostSpecificMethod(method, targetClass);


    // 3.提取事物标签
    // First try is the method in the target class.
    // 3.1 首先尝试从目标类的方法上提取事物标签
    TransactionAttribute txAttr = findTransactionAttribute(specificMethod);
    if (txAttr != null) {
        return txAttr;
    }

    // Second try is the transaction attribute on the target class.
    // 3.2 其次,尝试从目标类上提取事物标签
    txAttr = findTransactionAttribute(specificMethod.getDeclaringClass());
    if (txAttr != null && ClassUtils.isUserLevelMethod(method)) {
        return txAttr;
    }

    // 3.3 如果从目标类上提取的方法,不等于原始方法 todo lyc 这里还要再次分析下
    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;
}

这里我们看到,方法上的事物注解是优先于类上的事物注解的。而且这部分的处理,分为了两个部分:从给定的类或方法上提取事物注解。

/**
 * 从给定的方法上提取事物标签
 * @param method the method to retrieve the attribute for
 * @return
 */
@Override
@Nullable
protected TransactionAttribute findTransactionAttribute(Method method) {
    return determineTransactionAttribute(method);
}

/**
 * 从给定的类上提取事物标签
 * @param clazz the class to retrieve the attribute for
 * @return
 */
@Override
@Nullable
protected TransactionAttribute findTransactionAttribute(Class<?> clazz) {
    return determineTransactionAttribute(clazz);
}

但是这两个方法最终会调用同一个方法来获取事物标签:

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

这里又涉及到三个事物注解转换器,SpringTransactionAnnotationParser、JtaTransactionAnnotationParser、Ejb3TransactionAnnotationParser,我们只关心SpringTransactionAnnotationParser的转换就行了。接下来以SpringTransactionAnnotationParser为例看具体的事物标签提取和转换:

public TransactionAttribute parseTransactionAnnotation(AnnotatedElement ae) {
    // 1.提取事物注解标签(该方法较长就不粘贴了,感兴趣的自己看一下,比较简单)
    AnnotationAttributes attributes = AnnotatedElementUtils.findMergedAnnotationAttributes(
            ae,
            Transactional.class,
            false,
            false);
    // 2.解析事物标签
    if (attributes != null) {
        return parseTransactionAnnotation(attributes);
    }
    else {
        return null;
    }
}
/**
 * 解析事物标签属性
 */
protected TransactionAttribute parseTransactionAnnotation(AnnotationAttributes attributes) {
    RuleBasedTransactionAttribute rbta = new RuleBasedTransactionAttribute();
    // 1.解析propagation属性
    Propagation propagation = attributes.getEnum("propagation");
    rbta.setPropagationBehavior(propagation.value());
    // 2.解析isolation属性
    Isolation isolation = attributes.getEnum("isolation");
    rbta.setIsolationLevel(isolation.value());
    // 3.解析timeout属性
    rbta.setTimeout(attributes.getNumber("timeout").intValue());
    // 4.解析readOnly属性
    rbta.setReadOnly(attributes.getBoolean("readOnly"));
    // 5.解析value属性
    rbta.setQualifier(attributes.getString("value"));
    ArrayList<RollbackRuleAttribute> rollBackRules = new ArrayList<>();
    // 6.解析rollbackFor属性
    Class<?>[] rbf = attributes.getClassArray("rollbackFor");
    for (Class<?> rbRule : rbf) {
        RollbackRuleAttribute rule = new RollbackRuleAttribute(rbRule);
        rollBackRules.add(rule);
    }
    // 7.解析rollbackForClassName属性
    String[] rbfc = attributes.getStringArray("rollbackForClassName");
    for (String rbRule : rbfc) {
        RollbackRuleAttribute rule = new RollbackRuleAttribute(rbRule);
        rollBackRules.add(rule);
    }
    // 8.解析noRollbackFor属性
    Class<?>[] nrbf = attributes.getClassArray("noRollbackFor");
    for (Class<?> rbRule : nrbf) {
        NoRollbackRuleAttribute rule = new NoRollbackRuleAttribute(rbRule);
        rollBackRules.add(rule);
    }
    // 9.解析noRollbackForClassName属性
    String[] nrbfc = attributes.getStringArray("noRollbackForClassName");
    for (String rbRule : nrbfc) {
        NoRollbackRuleAttribute rule = new NoRollbackRuleAttribute(rbRule);
        rollBackRules.add(rule);
    }
    rbta.getRollbackRules().addAll(rollBackRules);
    return rbta;
}

到这里,我们就完成了目标类或者目标类方法上的事物标签提取了。

代理的创建等工作之前已经分析过,大家可以参考AOP的分析自己跟踪代码,接下来的章节,我们分析Spring事物代理方法的调用,解析Spring是如何实现事物的。

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

推荐阅读更多精彩内容