Spring事务源码解析获取增强

Posted zhixiang-org-cn

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Spring事务源码解析获取增强相关的知识,希望对你有一定的参考价值。

在上一篇文章@EnableTransactionManagement注解解析中,我们搭建了源码阅读的环境,以及解析了开启Spring事务功能的注解@EnableTransactionManagement的实现逻辑

在进行接下来的源码解析之前我想大家应该知道,当我们使用传统的jdbc应用事务的时候是不是做了如下操作:

  1. 开启事务
  2. save、update、delete等操作
  3. 出现异常进行回滚
  4. 正常情况提交事务

而在Spring中我们好像只需要关心第三步,也就是我们的业务,而其他的操作都不需要关心。那么我想你应该猜到了Spring是如何实现的呢?
答案就是基于@Transactional注解的SpringAOP实现,在接着往下阅读本篇文章的时候希望您对于SpringAOP的源码有一定的了解,如果不了解可以参考如下文章:

  1. 基于注解的SpringAOP源码解析(一)
  2. 基于注解的SpringAOP源码解析(二)
  3. 基于注解的SpringAOP源码解析(三)
获取增强

在阅读完AOP的原理之后,我们知道,当一个bean实例化之后会尝试获取所有适用于此Bean的增强。而在上篇文章中,我们已经发现了,@EnableTransactionManagement注解会往Spring中注入一个增强BeanFactoryTransactionAttributeSourceAdvisor。经过一番代码调用以后,会进入这么一个方法,这里的第一个入参就是BeanFactoryTransactionAttributeSourceAdvisor增强

    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 
            return true;
        
    
 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;
        
        MethodMatcher methodMatcher = pc.getMethodMatcher();
        if (methodMatcher == MethodMatcher.TRUE) 
            return true;
        

        IntroductionAwareMethodMatcher introductionAwareMethodMatcher = null;
        if (methodMatcher instanceof IntroductionAwareMethodMatcher) 
            introductionAwareMethodMatcher = (IntroductionAwareMethodMatcher) methodMatcher;
        

        Set<Class<?>> classes = new LinkedHashSet<>(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;
    

到这里的时候就进入事务相关的类TransactionAttributeSourcePointcut,看名字就能知道,这是个切点类.那么接下来的逻辑应该可以想象到,无非就是判断是否是个事务方法

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

如果是事务方法则继续往下走

public TransactionAttribute getTransactionAttribute(Method method, 
      @Nullable Class<?> targetClass) 
    // 如果当前方法是Object类中的方法,则直接返回
    if (method.getDeclaringClass() == Object.class) 
        return null;
    

    // 获取当前方法缓存使用的key
    Object cacheKey = getCacheKey(method, targetClass);
    Object cached = this.attributeCache.get(cacheKey);
    // 从缓存中获取当前方法解析的事务属性,如果解析过,则将解析结果返回
    if (cached != null) 
        if (cached == NULL_TRANSACTION_ATTRIBUTE) 
            return null;
         else 
            return (TransactionAttribute) cached;
        
     else 
        // 解析当前方法的事务属性,这里很重要,下面说
        TransactionAttribute txAttr = computeTransactionAttribute(method, targetClass);
        if (txAttr == null) 
            // 如果当前方法上没有事务属性,则缓存一个表示空事务属性的对象
            this.attributeCache.put(cacheKey, NULL_TRANSACTION_ATTRIBUTE);
         else 
            // 获取方法的签名
            String methodIdentification = 
                ClassUtils.getQualifiedMethodName(method, targetClass);
            // 如果生成的事务属性是DefaultTransactionAttribute类型的,则将方法签名设置到其descriptor属性中
            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;
    

接着看一下方法的事务属性是如何解析的

protected TransactionAttribute computeTransactionAttribute(Method method, 
       @Nullable Class<?> targetClass) 
    // 如果设置了只对public方法进行事务代理,并且当前方法不是public的,则返回null
    if (allowPublicMethodsOnly() && !Modifier.isPublic(method.getModifiers())) 
        return null;
    

    Class<?> userClass = (targetClass != null ? 
        ClassUtils.getUserClass(targetClass) : null);
    // 获取最为准确的方法,即如果传入的method只是一个接口方法,则会去找其实现类的同一方法进行解析
    Method specificMethod = ClassUtils.getMostSpecificMethod(method, userClass);
    // 如果当前方法是一个泛型方法,则会找Class文件中实际实现的方法
    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) 
        // 对传入方法解析事务属性,如果存在,则直接返回
        txAttr = findTransactionAttribute(method);
        if (txAttr != null) 
            return txAttr;
        

        // 对传入方法所在类进行事务属性解析,如果存在,则直接返回
        txAttr = findTransactionAttribute(method.getDeclaringClass());
        if (txAttr != null && ClassUtils.isUserLevelMethod(method)) 
            return txAttr;
        
    

    return null;

这里对事务属性的解析主要分为对目标方法进行解析和对传入方法进行解析,接着看findTransactionAttribute方法

protected TransactionAttribute findTransactionAttribute(Method method) 
        return determineTransactionAttribute(method);
    
    protected TransactionAttribute determineTransactionAttribute(AnnotatedElement ae) 
        for (TransactionAnnotationParser annotationParser : this.annotationParsers) 
            TransactionAttribute attr = annotationParser.parseTransactionAnnotation(ae);
            if (attr != null) 
                return attr;
            
        
        return null;
    
    public TransactionAttribute parseTransactionAnnotation(AnnotatedElement ae) 
        AnnotationAttributes attributes = AnnotatedElementUtils.findMergedAnnotationAttributes(
                ae, Transactional.class, false, false);
        if (attributes != null) 
            return parseTransactionAnnotation(attributes);
        
        else 
            return null;
        
    
public TransactionAttribute parseTransactionAnnotation(AnnotatedElement ae) 
    // 判断目标方法上是否存在@Transactional注解,如果不存在,则直接返回
    AnnotationAttributes attributes = AnnotatedElementUtils
        .findMergedAnnotationAttributes(ae, Transactional.class, false, false);
    if (attributes != null) 
        // 如果目标方法上存在@Transactional注解,则获取注解值,并且封装为TransactionAttribute返回
        return parseTransactionAnnotation(attributes);
     else 
        return null;
    


protected TransactionAttribute parseTransactionAnnotation(
        AnnotationAttributes attributes) 
    RuleBasedTransactionAttribute rbta = new RuleBasedTransactionAttribute();
    // 获取注解上的propagation值
    Propagation propagation = attributes.getEnum("propagation");
    rbta.setPropagationBehavior(propagation.value());
    // 获取注解上的isolation属性值
    Isolation isolation = attributes.getEnum("isolation");
    rbta.setIsolationLevel(isolation.value());
    // 获取注解上的timeout属性值
    rbta.setTimeout(attributes.getNumber("timeout").intValue());
    // 获取注解上的readOnly属性值
    rbta.setReadOnly(attributes.getBoolean("readOnly"));
    // 获取注解上的value属性值
    rbta.setQualifier(attributes.getString("value"));
    ArrayList<RollbackRuleAttribute> rollBackRules = new ArrayList<>();
    // 获取注解上的rollbackFor属性列表
    Class<?>[] rbf = attributes.getClassArray("rollbackFor");
    for (Class<?> rbRule : rbf) 
        RollbackRuleAttribute rule = new RollbackRuleAttribute(rbRule);
        rollBackRules.add(rule);
    
    // 获取注解上的rollbackForClassName属性列表
    String[] rbfc = attributes.getStringArray("rollbackForClassName");
    for (String rbRule : rbfc) 
        RollbackRuleAttribute rule = new RollbackRuleAttribute(rbRule);
        rollBackRules.add(rule);
    
    // 获取注解上的noRollbackFor属性列表
    Class<?>[] nrbf = attributes.getClassArray("noRollbackFor");
    for (Class<?> rbRule : nrbf) 
        NoRollbackRuleAttribute rule = new NoRollbackRuleAttribute(rbRule);
        rollBackRules.add(rule);
    
    // 获取注解上的noRollbackForClassName属性列表
    String[] nrbfc = attributes.getStringArray("noRollbackForClassName");
    for (String rbRule : nrbfc) 
        NoRollbackRuleAttribute rule = new NoRollbackRuleAttribute(rbRule);
        rollBackRules.add(rule);
    
    rbta.getRollbackRules().addAll(rollBackRules);
    return rbta;

可以看到这里已经把方法上或者类上的@Transactional注解的属性封装成TransactionAttribute返回了,关于@Transactional注解的更多知识可参考我的这篇文章:
Spring@Transactional注解

以上是关于Spring事务源码解析获取增强的主要内容,如果未能解决你的问题,请参考以下文章

Spring事务源码:创建代理类

Spring 事务提交回滚源码解析

Spring 事务提交回滚源码解析

杨老师课堂之springAOP事务控制源码解析

Spring AOP源码解析:Spring事务实现原理

Spring 事务Transaction源码深度解析