Spring的事务管理功能能让我们非常简单地进行事务管理。只需要进行简单的两步配置即可:

step1:开启事务管理功能

@Configuration
//@EnableTransactionManagement注解有以下几个属性
//proxyTargetClass属相:指定事务的AOP是通过JDK动态代理实现,还是CGLIB动态代理实现。true的话是CGLIB,false的话是JDK动态代理
// 需要注意的是这个属性只有在AdviceMode设置成AdviceMode.PROXY的情况下才会生效,加入使用ASPECTJ这AOP框架的话,这个属性就失效了。
// 另外,这个属性的设定可能会影响其他需要动态代理的类。比如说将这个属性设置成true,@Async注解的方法也会使用CGLIB生成代理类。
// 但是总的来说,这个属性的设置不会造成什么负面影响,毕竟JDK动态代理和CGLIB动态代理都能实现我们的需求 //mode属性:Spring提供的AOP功能有两种实现方式,一种是Spring自带的AOP功能,主要靠JDK代理和CGLIB代理实现,另外一种是通过第三方框架ASPECTJ实现。这个选项
// 就是设定Spring用哪种方式提供AOP功能。AdviceMode.PROXY表示用Spring自带的AOP功能,AdviceMode.ASPECTJ表示使用AdviceMode提供AOP功能。
// 需要注意的是Spring自带的AOP功能不支持本地调用的代理功能,也就是说同一个类中的方法互相调用不会“触发”代理方法。如果想让自调用触发代理,可以考虑使用ASPECTJ。 //order属性:表示当一个连接点(方法)被切多次时(也就是说有多个Advice和连接点关联),这些连接点的执行顺序。
@EnableTransactionManagement
public class TxConfig {
}

step2:在需要事务管理的方法上添加@Transactional注解

@Override
@Transactional
public int saveSysUser(SysUser user) {
int i = sysUserMapper.insert(user);
return i;
}

整个使用流程就这么简单。这篇博客就来简单分析下Spring是怎么实现事务管理的。

对事务管理进行AOP的过程

Spring的很多功能都是通过AOP功能实现的,事务管理也是。我们之前的文章分析过Spring基础AOP实现的原理。这边再简单提下Spring实现AOP的

原理:

Spring基础的AOP功能的开关是@EnableAspectJAutoProxy,这个注解注册了一个Bean——AnnotationAwareAspectJAutoProxyCreator,这个Bean才是实现AOP功能的关键。

这个Bean实现了InstantiationAwareBeanPostProcessor接口(这个接口是BeanPostProcessor的子接口)。熟悉Spring的读者知道,实现BeanPostProcessor接口的Bean

会在其他Bean初始化之前初始,然后在其他Bean初始化的时候,BeanPostProcessor的实现会对这些Bean进行“加工处理”。

这边AnnotationAwareAspectJAutoProxyCreator就承担了加工处理类的角色。这个Bean在其他Bean初始化前后会判断这个Bean中的方法是不是有对应的Advice,如果有的话就会

通过动态代理的方式生成动态代理类将通知织入进去。

我们发现开启事务管理的方式和开启AOP功能的方式很像,也是通过Enable注解开启。所以很自然就猜想事务管理是不是也是通过BeanPostProcessor的方式实现的。带着这个猜想去看下@EnableTransactionManagement注解。


@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import(TransactionManagementConfigurationSelector.class)
public @interface EnableTransactionManagement { boolean proxyTargetClass() default false;
AdviceMode mode() default AdviceMode.PROXY;
int order() default Ordered.LOWEST_PRECEDENCE;
}

看到上面的代码,我们很自然的会去看TransactionManagementConfigurationSelector的代码。Spring有两种方式提供AOP功能,一种是自带的动态代理的功能,一种是

通过ASPECTJ的方式提供。这边主要讨论Spring自带的AOP功能。

protected String[] selectImports(AdviceMode adviceMode) {
switch (adviceMode) {
//用代理的方式实现事务管理的AOP功能
case PROXY:
return new String[] {AutoProxyRegistrar.class.getName(),
ProxyTransactionManagementConfiguration.class.getName()};
case ASPECTJ:
return new String[] {determineTransactionAspectClass()};
default:
return null;
}
}

上面的代码中,我们主要关注PROXY这个case中的方法。这个case中注册了两个类:AutoProxyRegistrar和ProxyTransactionManagementConfiguration。

首先我们来看AutoProxyRegistrar这个类,层层点进入,我们发现这个类最终就是注册了InfrastructureAdvisorAutoProxyCreator这个类。仔细看InfrastructureAdvisorAutoProxyCreator

这个类实现的接口的话,你会发现这个类也是BeanPostProcesser系列的类。看到这里,我的直觉是事务管理的AOP过程和Spring基础的AOP功能原理可能是一样的。

再仔细看InfrastructureAdvisorAutoProxyCreator对BeanPostProcesser系列接口的实现,你会发现都是继承的AbstractAutoProxyCreator。看到这个验证了我之前的想法。

下面是Spring对事务管理进行AOP的过程,你会发现和基础的AOP功能是一套代码。

@Override
public Object postProcessAfterInitialization(@Nullable Object bean, String beanName) {
if (bean != null) {
Object cacheKey = getCacheKey(bean.getClass(), beanName);
if (this.earlyProxyReferences.remove(cacheKey) != bean) {
return wrapIfNecessary(bean, beanName, cacheKey);
}
}
return 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;
} // Create proxy if we have advice.
// 代码1
// 这边是获取Advice和Advisor的具体代码
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;
}

再来看看ProxyTransactionManagementConfiguration做了些啥?点进源代码你会发现这个类的功能很简单,就是注册了下面几个事务管理相关的基础Bean。

  • BeanFactoryTransactionAttributeSourceAdvisor;
  • TransactionAttributeSource;
  • TransactionInterceptor。

事务管理的生效过程

上面的章节中讲了Spring是怎么生成事务相关的AOP代理类的。这边来讲下Spring的事务管理是怎么生效的——怎么开启事务,怎么回滚事务,怎么提交事务,Spring中的事务传播

机制是怎么生效的。

这块的代码主要是在TransactionAspectSupport的invokeWithinTransaction方法中(不要问我是怎么找到这段代码的...)。下面讲下这个方法中的几个关键点。


protected Object invokeWithinTransaction(Method method, @Nullable Class<?> targetClass,
final InvocationCallback invocation) throws Throwable { // If the transaction attribute is null, the method is non-transactional.
TransactionAttributeSource tas = getTransactionAttributeSource();
//获取TransactionAttribute,这个类主要是@Transactional注解的配置信息
final TransactionAttribute txAttr = (tas != null ? tas.getTransactionAttribute(method, targetClass) : null);
//确认事务管理器
final TransactionManager tm = determineTransactionManager(txAttr); if (this.reactiveAdapterRegistry != null && tm instanceof ReactiveTransactionManager) {
ReactiveTransactionSupport txSupport = this.transactionSupportCache.computeIfAbsent(method, key -> {
if (KotlinDetector.isKotlinType(method.getDeclaringClass()) && KotlinDelegate.isSuspend(method)) {
throw new TransactionUsageException(
"Unsupported annotated transaction on suspending function detected: " + method +
". Use TransactionalOperator.transactional extensions instead.");
}
ReactiveAdapter adapter = this.reactiveAdapterRegistry.getAdapter(method.getReturnType());
if (adapter == null) {
throw new IllegalStateException("Cannot apply reactive transaction to non-reactive return type: " +
method.getReturnType());
}
return new ReactiveTransactionSupport(adapter);
});
return txSupport.invokeWithinTransaction(
method, targetClass, invocation, txAttr, (ReactiveTransactionManager) tm);
} PlatformTransactionManager ptm = asPlatformTransactionManager(tm);
final String joinpointIdentification = methodIdentification(method, targetClass, txAttr); if (txAttr == null || !(ptm instanceof CallbackPreferringPlatformTransactionManager)) {
// Standard transaction demarcation with getTransaction and commit/rollback calls.
TransactionInfo txInfo = createTransactionIfNecessary(ptm, txAttr, joinpointIdentification); Object retVal;
try {
// This is an around advice: Invoke the next interceptor in the chain.
// This will normally result in a target object being invoked.
retVal = invocation.proceedWithInvocation();
}
catch (Throwable ex) {
// target invocation exception
completeTransactionAfterThrowing(txInfo, ex);
throw ex;
}
finally {
cleanupTransactionInfo(txInfo);
} if (vavrPresent && VavrDelegate.isVavrTry(retVal)) {
// Set rollback-only in case of Vavr failure matching our rollback rules...
TransactionStatus status = txInfo.getTransactionStatus();
if (status != null && txAttr != null) {
retVal = VavrDelegate.evaluateTryFailure(retVal, txAttr, status);
}
} commitTransactionAfterReturning(txInfo);
return retVal;
}else {
final ThrowableHolder throwableHolder = new ThrowableHolder(); // It's a CallbackPreferringPlatformTransactionManager: pass a TransactionCallback in.
try {
Object result = ((CallbackPreferringPlatformTransactionManager) ptm).execute(txAttr, status -> {
TransactionInfo txInfo = prepareTransactionInfo(ptm, txAttr, joinpointIdentification, status);
try {
Object retVal = invocation.proceedWithInvocation();
if (vavrPresent && VavrDelegate.isVavrTry(retVal)) {
// Set rollback-only in case of Vavr failure matching our rollback rules...
retVal = VavrDelegate.evaluateTryFailure(retVal, txAttr, status);
}
return retVal;
}
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;
}
}
}

事务操作的主要代码都在这个方法中,要详细将这个方法能写很多内容。这边就不详细展开了,大家感兴趣的可以仔细研究下这个方法。

重要类总结

  • InfrastructureAdvisorAutoProxyCreator:事务管理AOP注册
  • BeanFactoryTransactionAttributeSourceAdvisor:Spring事务管理基础Bean
  • TransactionAttributeSource:Spring事务管理基础Bean
  • TransactionInterceptor:Spring事务管理基础Bean
  • TransactionAspectSupport的invokeWithinTransaction方法:事务处理的主要方法

Spring系列.事务管理原理简析的更多相关文章

  1. Spring系列.事务管理

    Spring提供了一致的事务管理抽象.这个抽象是Spring最重要的抽象之一, 它有如下的优点: 为不同的事务API提供一致的编程模型,如JTA.JDBC.Hibernate和MyBatis数据库层 ...

  2. Spring系列.@EnableRedisHttpSession原理简析

    在集群系统中,经常会需要将Session进行共享.不然会出现这样一个问题:用户在系统A上登陆以后,假如后续的一些操作被负载均衡到系统B上面,系统B发现本机上没有这个用户的Session,会强制让用户重 ...

  3. [转载] Thrift原理简析(JAVA)

    转载自http://shift-alt-ctrl.iteye.com/blog/1987416 Apache Thrift是一个跨语言的服务框架,本质上为RPC,同时具有序列化.发序列化机制:当我们开 ...

  4. 通俗的讲法理解spring的事务实现原理

    拿房屋买卖举例,流程:销售房屋 -- 接待员 -- 销售员 -- 财务 售楼处 存放着所有待售和已售的房屋数据(数据源 datasource) 总经理 带领一套自己的班底,下属员工都听自己的,服务于售 ...

  5. 深入Spring:自定义事务管理

    转自: http://www.jianshu.com/p/5347a462b3a5 前言 上一篇文章讲了Spring的Aop,这里讲一下Spring的事务管理,Spring的事务管理是建立在Aop的基 ...

  6. 【spring源码学习】spring的事务管理的源码解析

    [一]spring事务管理(1)spring的事务管理,是基于aop动态代理实现的.对目标对象生成代理对象,加入事务管理的核心拦截器==>org.springframework.transact ...

  7. Linux内存管理机制简析

    Linux内存管理机制简析 本文对Linux内存管理机制做一个简单的分析,试图让你快速理解Linux一些内存管理的概念并有效的利用一些管理方法. NUMA Linux 2.6开始支持NUMA( Non ...

  8. Spring的事务管理

    事务 事务:是逻辑上一组操作,要么全都成功,要么全都失败. 事务特性(ACID) 原子性:事务不可分割 一致性:事务执行的前后,数据完整性保持一致 隔离性:一个事务执行的时候,不应该受到其他事务的打扰 ...

  9. spring笔记--事务管理之声明式事务

    事务简介: 事务管理是企业级应用开发中必不可少的技术,主要用来确保数据的完整性和一致性, 事务:就是一系列动作,它们被当作一个独立的工作单元,这些动作要么全部完成,要么全部不起作用. Spring中使 ...

随机推荐

  1. N3飞控踩坑指南

    1.想要使用上位机仿真的话,在本次连接上位机的过程中不要点击IMU校准. 2.两路12S电池并联为飞控供电时(DJI智能电池),需要确保所有电池均为满电.否则如果上电时电量不平衡,电池之间将会自动互相 ...

  2. windows核心编程课程实践---多线程文件搜索器(MFC界面)

    课上完了连老师见都没见一面QAQ....记录一下该小项目 效果如下: 1.实现文件搜索功能,并封装为类 1)首先是文件搜索类Rapidfinder的构造函数和析构函数和文件信息初始化函数和文件路径规格 ...

  3. Beta冲刺——凡事预则立

    这个作业属于哪个课程 软件工程 这个作业要求在哪里 Beta冲刺 这个作业的目标 Beta冲刺 作业正文 正文 github链接 项目地址 其他参考文献 无 1.讨论组长是否重选的议题和结论 经过讨论 ...

  4. Rocket - diplomacy - LazyModule的实例化

    https://mp.weixin.qq.com/s/9PsBt4_4qHx4i6C5XtuiUw   介绍LazyModule和Node构造方法的执行过程,即实例化过程.     1. NullIn ...

  5. 带你学够浪:Go语言基础系列-环境配置和 Hello world

    文章每周持续更新,原创不易,「三连」让更多人看到是对我最大的肯定.可以微信搜索公众号「 后端技术学堂 」第一时间阅读(一般比博客早更新一到两篇) 前面几周陆陆续续写了一些后端技术的文章,包括数据库.微 ...

  6. Nginx 笔记(二)nginx常用的命令和配置文件

    个人博客网:https://wushaopei.github.io/    (你想要这里多有) 1.nginx常用的命令 (1)启动命令 在/usr/local/nginx/sbin 目录下执行 ./ ...

  7. footer部分,当页面主题内容不满一屏时,始终位于页面底部

    比如上面这种情况,footer部分本来应该位于最底部,但是main内容太少导致连在一起,影响美观 解决方案: 给footer加上margin-top:负值 值的大小为footer的高度 写了个小dem ...

  8. WALT(Window Assisted Load Tracking)学习

    QCOM平台使用WALT(Window Assisted Load Tracking)作为CPU load tracking的方法:相对地,ARM使用的是PELT(Per-Entity Load Tr ...

  9. Java实现 LeetCode 674 最长连续递增序列(暴力)

    674. 最长连续递增序列 给定一个未经排序的整数数组,找到最长且连续的的递增序列. 示例 1: 输入: [1,3,5,4,7] 输出: 3 解释: 最长连续递增序列是 [1,3,5], 长度为3. ...

  10. Java实现算法提高十进制数转八进制数

    算法提高 十进制数转八进制数 时间限制:1.0s 内存限制:512.0MB 编写函数,其功能为把一个十进制数转换为其对应的八进制数.程序读入一个十进制数,调用该函数实现数制转换后,输出对应的八进制数. ...