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注解的更多知识可参考我的这篇文章:
[email protected]注解

原文地址:https://www.cnblogs.com/zhixiang-org-cn/p/11456935.html

时间: 2024-08-10 12:30:13

Spring事务源码解析(二)获取增强的相关文章

结合ThreadLocal来看spring事务源码,感受下清泉般的洗涤!

在我的博客spring事务源码解析中,提到了一个很关键的点:将connection绑定到当前线程来保证这个线程中的数据库操作用的是同一个connection.但是没有细致的讲到如何绑定,以及为什么这么绑定:另外也没有讲到连接池的相关问题:如何从连接池获取,如何归还连接到连接池等等.那么下面就请听我慢慢道来. ThreadLocal 讲spring事务之前,我们先来看看ThreadLocal,它在spring事务中是占据着比较重要的地位:不管你对ThreadLocal熟悉与否,且都静下心来听我唐僧

Spring 源码解析之HandlerAdapter源码解析(二)

Spring 源码解析之HandlerAdapter源码解析(二) 前言 看这篇之前需要有Spring 源码解析之HandlerMapping源码解析(一)这篇的基础,这篇主要是把请求流程中的调用controller流程单独拿出来了 解决上篇文章遗留的问题 getHandler(processedRequest) 这个方法是如何查找到对应处理的HandlerExecutionChain和HandlerMapping的,比如说静态资源的处理和请求的处理肯定是不同的HandlerMapping ge

spring事务源码研读1

转载摘录自:Spring事务源码分析(一)Spring事务入门 有时为了保证一些操作要么都成功,要么都失败,这就需要事务来保证. 传统的jdbc事务如下: @Test public void testAdd(){ Connection con=null; try { con=DriverManager.getConnection(url , username , password ) con.setAutoCommit(false); //操作一 PreparedStatement ps = c

Spring IoC源码解析之getBean

一.实例化所有的非懒加载的单实例Bean 从org.springframework.context.support.AbstractApplicationContext#refresh方法开发,进入到实例化所有的非懒加载的单实例Bean的finishBeanFactoryInitialization(beanFactory)的方法: protected void finishBeanFactoryInitialization(ConfigurableListableBeanFactory bea

spring mvc源码解析

1.从DispatcherServlet开始     与很多使用广泛的MVC框架一样,SpringMVC使用的是FrontController模式,所有的设计都围绕DispatcherServlet 为中心来展开的.见下图,所有请求从DispatcherServlet进入,DispatcherServlet根据配置好的映射策略确定处理的 Controller,Controller处理完成返回ModelAndView,DispatcherServlet根据配置好的视图策略确定处理的 View,由V

spring boot 源码解析 启动流程

spring boot 源码解析 启动流程 在面试过程中经常被问到过spring boot的启动流程,今天就翻一下源码整体看一下: 首先,新建一个启动类,可以看到是首先调用的SpringApplication的静态方法run @SpringBootApplication public class SourceReadApplillcation { public static void main(String[] args) { SpringApplication.run(SourceReadAp

Flink 源码解析 —— 如何获取 ExecutionGraph ?

https://t.zsxq.com/UnA2jIi 博客 1.Flink 从0到1学习 -- Apache Flink 介绍 2.Flink 从0到1学习 -- Mac 上搭建 Flink 1.6.0 环境并构建运行简单程序入门 3.Flink 从0到1学习 -- Flink 配置文件详解 4.Flink 从0到1学习 -- Data Source 介绍 5.Flink 从0到1学习 -- 如何自定义 Data Source ? 6.Flink 从0到1学习 -- Data Sink 介绍 7

Spring事务源码

启动事务 @EnableTransactionManagement 注解来启用事务能力. 参数解释 proxyTargetClass:默认为false,表示使用 JDK 的代理模式,true表示用 CGLib 的代理模式,仅在 mode 是 PROXY 时才有效. mode:默认为PROXY,表示使用 AOP 代理来实现事务,ASPECTJ表示用 ASPECTJ 来实现事务,ASPECTJ 相比 PROXY 减少了一些使用限制,比如支持在同一个类内部方法调用. order:事务通知执行的顺序,默

Spring IoC源码解析——Bean的创建和初始化

Spring介绍 Spring(http://spring.io/)是一个轻量级的Java 开发框架,同时也是轻量级的IoC和AOP的容器框架,主要是针对JavaBean的生命周期进行管理的轻量级容器,可以单独使用,也可以和Struts框架,MyBatis框架等组合使用. IoC介绍 IoC是什么 Ioc-Inversion of Control,即"控制反转",不是什么技术,而是一种设计思想.在Java开发中,Ioc意味着将你设计好的对象交给容器控制,而不是传统的在你的对象内部直接控