Spring5源码(43)-@Transactional声明式事物(一)事物管理步骤简析

简介: Spring5源码(43)-@Transactional声明式事物(一)事物管理步骤简析


1.引

上一节已经分析了基于@Transactional注解的声明式事物事物标签提取,接下来我们就接下来我们就要分析Spring事物的实现过程了,因为这里会涉及到事物传播特性、事物嵌套调用等等很多复杂的内容,所以第一个分析,还是以最简单的形式去分析,及单个service下的单个方法调用。业务方法摘要如下,在业务方法上配置了Transactional注解,并将事物传播特性定义为REQUIRED。在分析完这个最简单的例子之后,再去分析比较复杂的事物调用过程。。。

@Override
@Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
public void delete() throws RuntimeException {
    System.out.println("==调用AccountService的delete方法\n");
    jdbcTemplate.update(insert_sql);
    throw new RuntimeException("==AccountService的delete方法手动抛出异常");
}
2.拦截器链调用回顾

拦截器链调用入口:

public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    MethodInvocation invocation;
    Object oldProxy = null;
    boolean setProxyContext = false;
    TargetSource targetSource = this.advised.targetSource;
    Object target = null;
    try {
        // 1、处理equals方法,如果接口中没有定义equals而在实现类中覆盖了equals方法,那么该equals方法不会被增强
        if (!this.equalsDefined && AopUtils.isEqualsMethod(method)) {
            // The target does not implement the equals(Object) method itself.
            return equals(args[0]);
        }
        // 2、处理hashCode方法,如果接口中没有定义hashCode而在实现类中覆盖了hashCode方法,那么该hashCode方法不会被增强
        else if (!this.hashCodeDefined && AopUtils.isHashCodeMethod(method)) {
            // The target does not implement the hashCode() method itself.
            return hashCode();
        }
        // 3、如果目标对象是DecoratingProxy类型,则返回目标对象的最终对象类型
        // DecoratingProxy接口只有一个getDecoratedClass方法,用于返回目标对象的最终对象类型
        else if (method.getDeclaringClass() == DecoratingProxy.class) {
            // There is only getDecoratedClass() declared -> dispatch to proxy config.
            return AopProxyUtils.ultimateTargetClass(this.advised);
        }
        // 4、如果目标对象是Advice类型,则直接使用反射进行调用
        // opaque-->标记是否需要阻止通过该配置创建的代理对象转换为Advised类型,默认值为false,表示代理对象可以被转换为Advised类型
        // method.getDeclaringClass().isInterface()-->目标对象是接口
        // method.getDeclaringClass().isAssignableFrom(Advised.class)-->
        // 是用来判断一个类Class1和另一个类Class2是否相同或者Class1类是不是Class2的父类。例如:Class1.isAssignableFrom(Class2)
        else if (!this.advised.opaque
                && method.getDeclaringClass().isInterface()
                && method.getDeclaringClass().isAssignableFrom(Advised.class)) {
            // Service invocations on ProxyConfig with the proxy config...
            return AopUtils.invokeJoinpointUsingReflection(this.advised, method, args);
        }
        Object retVal;
        // 5、解决目标对象内部自我调用无法实施切面增强,在这里暴露代理
        if (this.advised.exposeProxy) {
            // Make invocation available if necessary.
            oldProxy = AopContext.setCurrentProxy(proxy);
            setProxyContext = true;
        }
        // Get as late as possible to minimize the time we "own" the target,
        // in case it comes from a pool.
        target = targetSource.getTarget();
        Class<?> targetClass = (target != null ? target.getClass() : null);
        // Get the interception chain for this method.
        // 6、获取当前方法的拦截器链,并执行调用
        List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);
        // 检测是否拦截器链是否为空,如果拦截器链为空,那么直接通过反射调用目标对象的方法,避免创建MethodInvocation
        // Check whether we have any advice. If we don't, we can fallback on direct
        // reflective invocation of the target, and avoid creating a MethodInvocation.
        if (chain.isEmpty()) {
            // We can skip creating a MethodInvocation: just invoke the target directly
            // Note that the final invoker must be an InvokerInterceptor so we know it does
            // nothing but a reflective operation on the target, and no hot swapping or fancy proxying.
            // 通过反射直接调用目标对象的方法
            Object[] argsToUse = AopProxyUtils.adaptArgumentsIfNecessary(method, args);
            retVal = AopUtils.invokeJoinpointUsingReflection(target, method, argsToUse);
        }
        else {
            // 创建MethodInvocation对象并调用proceed方法,拦截器链被封装到了invocation中
            // We need to create a method invocation...
            invocation = new ReflectiveMethodInvocation(proxy, target, method, args, targetClass, chain);
            // Proceed to the joinpoint through the interceptor chain.
            // 调用拦截器链
            retVal = invocation.proceed();
        }
        // 7、返回结果
        // Massage return value if necessary.
        Class<?> returnType = method.getReturnType();
        if (retVal != null
                && retVal == target
                && returnType != Object.class
                && returnType.isInstance(proxy)
                && !RawTargetAccess.class.isAssignableFrom(method.getDeclaringClass())) {
            // Special case: it returned "this" and the return type of the method
            // is type-compatible. Note that we can't help if the target sets
            // a reference to itself in another returned object.
            retVal = proxy;
        }
        else if (retVal == null && returnType != Void.TYPE && returnType.isPrimitive()) {
            throw new AopInvocationException("Null return value from advice does not match primitive return type for: " + method);
        }
        return retVal;
    }
    finally {
        if (target != null && !targetSource.isStatic()) {
            // Must have come from TargetSource.
            targetSource.releaseTarget(target);
        }
        if (setProxyContext) {
            // Restore old proxy.
            AopContext.setCurrentProxy(oldProxy);
        }
    }
}

真正执行拦截器链调用

public Object proceed() throws Throwable {
    //  We start with an index of -1 and increment early.
    if (this.currentInterceptorIndex == this.interceptorsAndDynamicMethodMatchers.size() - 1) {
        return invokeJoinpoint();
    }
    Object interceptorOrInterceptionAdvice = this.interceptorsAndDynamicMethodMatchers.get(++this.currentInterceptorIndex);
    // 动态匹配增强
    if (interceptorOrInterceptionAdvice instanceof InterceptorAndDynamicMethodMatcher) {
        // Evaluate dynamic method matcher here: static part will already have
        // been evaluated and found to match.
        InterceptorAndDynamicMethodMatcher dm = (InterceptorAndDynamicMethodMatcher) interceptorOrInterceptionAdvice;
        Class<?> targetClass = (this.targetClass != null ? this.targetClass : this.method.getDeclaringClass());
        // 匹配成功则执行
        if (dm.methodMatcher.matches(this.method, targetClass, this.arguments)) {
            return dm.interceptor.invoke(this);
        }
        // 匹配失败则跳过并执行下一个拦截器
        else {
            // Dynamic matching failed.
            // Skip this interceptor and invoke the next in the chain.
            return proceed();
        }
    }
    // 静态增强
    else {
        // It's an interceptor, so we just invoke it: The pointcut will have
        // been evaluated statically before this object was constructed.
        // System.out.println(interceptorOrInterceptionAdvice.getClass());
        return ((MethodInterceptor) interceptorOrInterceptionAdvice).invoke(this);
    }
}

以上的代码之前都已经分析过,像其中的拦截器链获取等,就不再详细分析了,当代码执行到这里的proceed方法里,如果我们没有配置其他的AOP增强,那么第一个被执行的拦截器就是TransactionInterceptor(事物拦截器)

3.TransactionInterceptor实现事物管理过程简析

public Object invoke(MethodInvocation invocation) throws Throwable {
    // Work out the target class: may be {@code null}.
    // The TransactionAttributeSource should be passed the target class
    // as well as the method, which may be from an interface.
    Class<?> targetClass = (invocation.getThis() != null ? AopUtils.getTargetClass(invocation.getThis()) : null);
    // Adapt to TransactionAspectSupport's invokeWithinTransaction...
    return invokeWithinTransaction(invocation.getMethod(), targetClass, invocation::proceed);
}

protected Object invokeWithinTransaction(Method method,
                                             @Nullable Class<?> targetClass,
                                             final InvocationCallback invocation) throws Throwable {
    // 1.准备工作
    // If the transaction attribute is null, the method is non-transactional.
    TransactionAttributeSource tas = getTransactionAttributeSource();
    // 获取事物属性
    final TransactionAttribute txAttr = (tas != null ? tas.getTransactionAttribute(method, targetClass) : null);
    // 获取事物管理器
    final PlatformTransactionManager tm = determineTransactionManager(txAttr);
    // 获取目标类全限定类名+连接点方法名 例如:com.lyc.cn.v2.day09.AccountServiceImpl.save
    final String joinpointIdentification = methodIdentification(method, targetClass, txAttr);
    // 2.处理声明式事物
    if (txAttr == null || !(tm instanceof CallbackPreferringPlatformTransactionManager)) {
        // Standard transaction demarcation with getTransaction and commit/rollback calls.
        // 2.2 创建事物
        TransactionInfo txInfo = createTransactionIfNecessary(tm, txAttr, joinpointIdentification);
        System.out.println("==创建了名为:["+joinpointIdentification+"]的事物");
        Object retVal = null;
        try {
            // This is an around advice: Invoke the next interceptor in the chain.
            // This will normally result in a target object being invoked.
            // 2.3 继续调用方法拦截器链,这里一般将会调用目标类的方法,如:com.lyc.cn.v2.day09.AccountServiceImpl.save方法
            retVal = invocation.proceedWithInvocation();
        } catch (Throwable ex) {
            // target invocation exception
            // 2.4 如果目标类方法抛出异常,则在此处理,例如:事物回滚
            completeTransactionAfterThrowing(txInfo, ex);
            throw ex;
        } finally {
            // 2.5 清除上一步创建的事物信息
            cleanupTransactionInfo(txInfo);
        }
        // 2.6 调用成功完成后执行,但不是在异常被处理后执行。如果我们不创建事务,就什么也不做。
        commitTransactionAfterReturning(txInfo);
        return retVal;
    }
    // 3.处理编程式事物
    else {
        final ThrowableHolder throwableHolder = new ThrowableHolder();
        // It's a CallbackPreferringPlatformTransactionManager: pass a TransactionCallback in.
        try {
            Object result = ((CallbackPreferringPlatformTransactionManager) tm).execute(txAttr, status -> {
                TransactionInfo txInfo = prepareTransactionInfo(tm, txAttr, joinpointIdentification, status);
                try {
                    return invocation.proceedWithInvocation();
                } catch (Throwable ex) {
                    if (txAttr.rollbackOn(ex)) {
                        // A RuntimeException: will lead to a rollback.
                        if (ex instanceof RuntimeException) {
                            throw (RuntimeException) ex;
                        } else {
                            throw new ThrowableHolderException(ex);
                        }
                    } else {
                        // A normal return value: will lead to a commit.
                        throwableHolder.throwable = ex;
                        return null;
                    }
                } finally {
                    cleanupTransactionInfo(txInfo);
                }
            });
            // Check result state: It might indicate a Throwable to rethrow.
            if (throwableHolder.throwable != null) {
                throw throwableHolder.throwable;
            }
            return result;
        } catch (ThrowableHolderException ex) {
            throw ex.getCause();
        } catch (TransactionSystemException ex2) {
            if (throwableHolder.throwable != null) {
                logger.error("Application exception overridden by commit exception", throwableHolder.throwable);
                ex2.initApplicationException(throwableHolder.throwable);
            }
            throw ex2;
        } catch (Throwable ex2) {
            if (throwableHolder.throwable != null) {
                logger.error("Application exception overridden by commit exception", throwableHolder.throwable);
            }
            throw ex2;
        }
    }
}

这里对声明式事物和编程式事物分别做了不同的处理,我们主要看声明式事物的实现过程,第一步的准备工作比较简单,第二步是整个事物的核心所在。

  • 创建事物
  • 继续调用方法拦截器链,这里一般将会调用目标类的方法,如:com.lyc.cn.v2.day09.AccountServiceImpl.save方法
  • 如果目标类方法抛出异常,则在此处理,例如:事物回滚
  • 清除上一步创建的事物信息
  • 调用成功完成后执行commitTransactionAfterReturning方法,但不是在异常被处理后执行。如果我们不创建事务,就什么也不做。

分析到这里,我们也仅仅是得到了Spring事物管理的一些步骤,继续分析,接下来就是Spring创建事物的过程了。这一部分的内容太多了,还是分篇幅介绍吧。。。



目录
相关文章
|
前端开发 Java 物联网
智慧班牌源码,采用Java + Spring Boot后端框架,搭配Vue2前端技术,支持SaaS云部署
智慧班牌系统是一款基于信息化与物联网技术的校园管理工具,集成电子屏显示、人脸识别及数据交互功能,实现班级信息展示、智能考勤与家校互通。系统采用Java + Spring Boot后端框架,搭配Vue2前端技术,支持SaaS云部署与私有化定制。核心功能涵盖信息发布、考勤管理、教务处理及数据分析,助力校园文化建设与教学优化。其综合性和可扩展性有效打破数据孤岛,提升交互体验并降低管理成本,适用于日常教学、考试管理和应急场景,为智慧校园建设提供全面解决方案。
768 70
|
11月前
|
设计模式 Java 开发者
如何快速上手【Spring AOP】?从动态代理到源码剖析(下篇)
Spring AOP的实现本质上依赖于代理模式这一经典设计模式。代理模式通过引入代理对象作为目标对象的中间层,实现了对目标对象访问的控制与增强,其核心价值在于解耦核心业务逻辑与横切关注点。在框架设计中,这种模式广泛用于实现功能扩展(如远程调用、延迟加载)、行为拦截(如权限校验、异常处理)等场景,为系统提供了更高的灵活性和可维护性。
1439 0
|
Java 关系型数据库 MySQL
深入解析 @Transactional——Spring 事务管理的核心
本文深入解析了 Spring Boot 中 `@Transactional` 的工作机制、常见陷阱及最佳实践。作为事务管理的核心注解,`@Transactional` 确保数据库操作的原子性,避免数据不一致问题。文章通过示例讲解了其基本用法、默认回滚规则(仅未捕获的运行时异常触发回滚)、因 `try-catch` 或方法访问修饰符不当导致失效的情况,以及数据库引擎对事务的支持要求。最后总结了使用 `@Transactional` 的五大最佳实践,帮助开发者规避常见问题,提升项目稳定性与可靠性。
2121 12
|
存储 监控 数据可视化
SaaS云计算技术的智慧工地源码,基于Java+Spring Cloud框架开发
智慧工地源码基于微服务+Java+Spring Cloud +UniApp +MySql架构,利用传感器、监控摄像头、AI、大数据等技术,实现施工现场的实时监测、数据分析与智能决策。平台涵盖人员、车辆、视频监控、施工质量、设备、环境和能耗管理七大维度,提供可视化管理、智能化报警、移动智能办公及分布计算存储等功能,全面提升工地的安全性、效率和质量。
402 0
|
Java Spring 容器
SpringBoot自动配置的原理是什么?
Spring Boot自动配置核心在于@EnableAutoConfiguration注解,它通过@Import导入配置选择器,加载META-INF/spring.factories中定义的自动配置类。这些类根据@Conditional系列注解判断是否生效。但Spring Boot 3.0后已弃用spring.factories,改用新格式的.imports文件进行配置。
1439 0
|
人工智能 Java 测试技术
Spring Boot 集成 JUnit 单元测试
本文介绍了在Spring Boot中使用JUnit 5进行单元测试的常用方法与技巧,包括添加依赖、编写测试类、使用@SpringBootTest参数、自动装配测试模块(如JSON、MVC、WebFlux、JDBC等),以及@MockBean和@SpyBean的应用。内容实用,适合Java开发者参考学习。
1348 0
|
前端开发 Java 数据库
微服务——SpringBoot使用归纳——Spring Boot集成Thymeleaf模板引擎——Thymeleaf 介绍
本课介绍Spring Boot集成Thymeleaf模板引擎。Thymeleaf是一款现代服务器端Java模板引擎,支持Web和独立环境,可实现自然模板开发,便于团队协作。与传统JSP不同,Thymeleaf模板可以直接在浏览器中打开,方便前端人员查看静态原型。通过在HTML标签中添加扩展属性(如`th:text`),Thymeleaf能够在服务运行时动态替换内容,展示数据库中的数据,同时兼容静态页面展示,为开发带来灵活性和便利性。
588 0
|
9月前
|
JavaScript Java Maven
【SpringBoot(二)】带你认识Yaml配置文件类型、SpringMVC的资源访问路径 和 静态资源配置的原理!
SpringBoot专栏第二章,从本章开始正式进入SpringBoot的WEB阶段开发,本章先带你认识yaml配置文件和资源的路径配置原理,以方便在后面的文章中打下基础
647 4
|
9月前
|
Java 测试技术 数据库连接
【SpringBoot(四)】还不懂文件上传?JUnit使用?本文带你了解SpringBoot的文件上传、异常处理、组件注入等知识!并且带你领悟JUnit单元测试的使用!
Spring专栏第四章,本文带你上手 SpringBoot 的文件上传、异常处理、组件注入等功能 并且为你演示Junit5的基础上手体验
1177 3