-
2021-06-25 21:35:08
🍅 作者简介:哪吒,CSDN2021博客之星亚军🏆、新星计划导师✌、博客专家💪
🍅 哪吒多年工作总结:Java学习路线总结,搬砖工逆袭Java架构师
🍅 关注公众号【哪吒编程】,回复1024,获取Java学习路线思维导图、大厂面试真题、加入万粉计划交流群、一起学习进步
目录
更多相关内容 -
使用Spring事件机制实现异步的方法
2020-08-27 08:16:53主要介绍了使用Spring事件机制实现异步的方法,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧 -
Spring boot使用spring retry重试机制的方法示例
2020-08-25 04:47:21主要介绍了Spring boot使用spring retry重试机制的方法示例,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧 -
详解Spring Cloud 熔断机制--断路器
2020-08-27 16:28:21主要介绍了详解Spring Cloud 熔断机制--断路器,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧 -
Spring缓存机制实例代码
2020-08-28 01:23:57主要介绍了Spring缓存机制实例代码,分享了相关代码示例,小编觉得还是挺不错的,具有一定借鉴价值,需要的朋友可以参考下 -
EventBus与Spring Event区别详解(EventBus 事件机制,Spring Event事件机制)
2020-08-25 02:03:05主要介绍了EventBus与Spring Event区别,需要的朋友可以参考下 -
深入理解spring的事务管理机制
2017-10-06 22:14:24【免费】深入描述spring的事务处理机制,很不错的资源。 -
Spring Cloud Gateway重试机制的实现
2020-08-26 06:21:50主要介绍了Spring Cloud Gateway重试机制的实现,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧 -
利用Spring IOC技术实现用户登录验证机制
2020-09-01 10:47:40主要为大家详细介绍了Spring IOC技术实现用户登录验证机制的相关资料,具有一定的参考价值,感兴趣的小伙伴们可以参考一下 -
Spring的核心机制
2018-03-15 16:12:02Spring目的 让对象与对象(模块与模块)之间的关系没有通过代码来关联,都是通过配置类说明管理的 (Spring根据这些配置 内部通过反射去动态的组装对象) *.Spring是一个容器,凡是在容器里的对象才会有Spring所...Spring目的
让对象与对象(模块与模块)之间的关系没有通过代码来关联,都是通过配置类说明管理的
(Spring根据这些配置 内部通过反射去动态的组装对象)
*.Spring是一个容器,凡是在容器里的对象才会有Spring所提供的这些服务和功能。
Spring工作原理
Spring框架的核心组件是Spring IoC容器(BeanFacory接口或者ApplicationContext接口)和Spring配置文件(或者注解配置信息)。
Spring的基本思想是:把对象之间的依赖关系转移到配置文件(或者注解配置)中,由工厂类(BeanFacory接口)来创建对象。
由容器动态地创建并注入对象,决定所配置对象的创建、管理和销毁。
程序执行过程:
(1)根据业务要求创建实体类或给出接口的实现;
(2)利用所创建的类,在配置文件中配置Bean信息;
(3)创建Spring容器(该容器与配置文件相关联——读取配置信息)
(4)从容器中获取实例对象,由对象调用方法完成所需要的业务处理。
Spring容器和被管理的bean
Spring有两个核心接口:BeanFactory和ApplicationContext,其中ApplicationContext是
BeanFactory的子接口。他们都可以代表Spring容器。
Spring容器是生成Bean实例的工厂,并管理Spring中的bean。包括数据源、Hibernate的SessionFactory、事务管理器。
①Spring最基本的接口就是BeanFactory
BeanFactory有很多实现类,通常使用XmlBeanFactory,推荐使用ApplictionContext,它是
BeanFactory的子接口,
ApplictionContext的实现类为
FileSystemXmlApplicationContext 和ClassPathXmlApplicationContext
FileSystemXmlApplicationContext:基于文件系统的XML配置文件创建ApplicationContext;
ClassPathXmlApplicationContext:基于类加载路径下的xml配置文件创建ApplicationContext。
②ApplicationContext的事件机制
ApplicationContext事件机制是基于观察者设计模式实现的。
通过ApplicationEvent类和ApplicationListener接口,
ApplicationEvent:容器事件,必须由ApplicationContext发布;
ApplicationListener:监听器,可有容器内的任何监听器Bean担任。
③容器中bean的作用域
singleton:单例模式,在整个Spring IoC容器中,singleton作用域的Bean将只生成一个实例。
prototype:每次通过容器的getBean()方法获取prototype作用域的Bean时,都将产生一个新的Bean实例。
request:对于一次HTTP请求,request作用域的Bean将只生成一个实例,这意味着,在同一次HTTP请求内,程序每次请求该Bean,得到的总是同一个实例。只有在Web应用中使用Spring时,该作用域才真正有效。
对于一次HTTP会话,session作用域的Bean将只生成一个实例,这意味着,在同一次HTTP会话内,程序每次请求该Bean,得到的总是同一个实例。只有在Web应用中使用Spring时,该作用域才真正有效。
global session:每个全局的HTTP Session对应一个Bean实例。在典型的情况下,仅在使用portlet context的时候有效,同样只在Web应用中有效。
注意:request和session作用域只在web应用中才生效,并且必须在web应用中增加额外的
配置才会生效,为了让request,session两个作用域生效,必须将HTTP请求对象绑定到为该请
求提供服务的线程上,这使得具有request和session作用域的Bean实例能够在后面的调用链中被
访问。
当支持Servlet2.4及以上规范的web容器时,我们可以在web应用的web.xml增加如下Listener配
置,该Listener负责为request作用域生效:
<listener> <listener-class>org.springframework.web.context.request.RequestContextListener</listener-class> </listener> 如果仅使用了支持Servlet2.4以前规范的web容器,则该容器不支持Listener规范,故无法使用这种配置,可以使用Filter配置方式,我们可以在web应用的web.xml增加如下Filter配置: <filter> <filter-name>requestContextFilter</filter-name> <filter-class>org.springframework.web.filter.RequestContextFilter</filter-class> </filter> <filter-mapping> <filter-name>requestContextFilter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping>
再如下面的代码:
<bean id="p" class="lee.Person" scope="request"/>
这样容器就会为每次HTTP请求生成一个lee.Person的实例当该请求响应结束时,该实例也随之
消失。
如果Web应用直接使用Spring MVC作为MVC框架,即使用SpringDispatchServlet或
DispatchPortlet来拦截所有用户请求,则无需这些额外的配置,因为SpringDispatchServlet或
DispatchPortlet已经处理了所有和请求有关的状态处理。
④获取容器的引用:
通常情况下:
Bean无需访问Spring容器,而是通过Spring容器访问的,即使 需要手动访问Spring容器,程序
也已通过类似下面的代码获取Spring容器 的引用。
ApllicationContext cts = ClassPathApplalicationContext("bean.xml");
但在一些极端的情况下,可能Bean需要访问Spring容器。Spring提供另一种方法访问Spring容
器:
实现BeanFactoryAware接口的Bean,拥有访问Spring容器的能力,实现BeanFactoryAware的
Bean被容器实例化后,会拥有一个引用指向创建他的BeanFactory。BeanFactoryAware只有一
个方法setBeanFactory(BeanFactory beanFactory)该参数指向创建他的BeanFactory。
缺点:污染了代码,使代码与Spring接口耦合在一起,因此没有特别的必要,建议不要直接访问
容器。
创建Bean的3种方式
使用构造器创建Bean实例
使用构造器来创建Bean实例是最常见的情况,如果不采用构造注入,Spring底层会调用Bean类的无参数构造器来创建实例,因此要求该Bean类提供无参数的构造器。
采用默认的构造器创建Bean实例,Spring对Bean实例的所有属性执行默认初始化,即所有的基本类型的值初始化为0或false;所有的引用类型的值初始化为null。
使用静态工厂方法创建Bean
使用静态工厂方法创建Bean实例时,class属性也必须指定,但此时class属性并不是指定Bean实例的实现类,而是静态工厂类,Spring通过该属性知道由哪个工厂类来创建Bean实例。
除此之外,还需要使用factory-method属性来指定静态工厂方法,Spring将调用静态工厂方法返回一个Bean实例,一旦获得了指定Bean实例,Spring后面的处理步骤与采用普通方法创建Bean实例完全一样。如果静态工厂方法需要参数,则使用
<constructor-arg.../>
元素指定静态工厂方法的参数。调用实例工厂方法创建Bean
实例工厂方法与静态工厂方法只有一个不同:调用静态工厂方法只需使用工厂类即可,而调用实例工厂方法则需要工厂实例。使用实例工厂方法时,配置Bean实例的
factory-bean: 该属性的值为工厂Bean的id factory-method: 该属性指定实例工厂的工厂方法
若调用实例工厂方法时需要传入参数,则使用
<constructor-arg.../>
元素确定参数值。
协调作用域不同步的Bean当singleton作用域的Bean依赖于prototype作用域的Bean时,会产生不同步的现象,原因是因为当Spring容器初始化时,容器会预初始化容器中所有的singleton Bean,由于singleton Bean依赖于prototype Bean,因此Spring在初始化singleton Bean之前,会先创建prototypeBean——然后才创建singleton Bean,接下里将prototype Bean注入singleton Bean。
解决不同步的方法有两种放弃依赖注入: singleton作用域的Bean每次需要prototype作用域的Bean时,主动向容器请求新的Bean实例,即可保证每次注入的prototype Bean实例都是最新的实例 利用方法注入: 方法注入通常使用lookup方法注入,使用lookup方法注入可以让Spring容器重写容器中Bean的抽象或具体方法,返回查找容器中其他Bean的结果,被查找的Bean通常是一个non-singleton Bean。Spring通过使用JDK动态代理或cglib库修改客户端的二进制码,从而实现上述要求
建议采用第二种方法,使用方法注入。为了使用lookup方法注入,大致需要如下两步
将调用者Bean的实现类定义为抽象类,并定义一个抽象方法来获取被依赖的Bean 在<bean.../>元素中添加<lookup-method.../>子元素让Spring为调用者Bean的实现类实现指定的抽象方法
Spring的核心机制
内部最核心的就是IOC了,动态注入,让一个对象的创建不用new了,可以自动的生产,这其
实就是利用java里的反射,反射其实就是在运行时动态的去创建、调用对象,Spring就是在运
行时,跟xml Spring的配置文件来动态的创建对象,和调用对象里的方法的 。
Spring还有一个核心就是AOP这个就是面向切面编程,可以为某一类对象 进行监督和控制
(也就是 在调用这类对象的具体方法的前后去调用你指定的 模块)从而达到对一个模块扩充的
功能。这些都是通过 配置类达到的
IOC(依赖注入)
Rod Johnson是第一个高度重视以配置文件来管理Java实例的协作关系的人,他给这种方式起
了一个名字:控制反转(Inverse of Control,IoC)。后来Martine Fowler为这种方式起了另一
个名称:依赖注入(Dependency Injection),因此不管是依赖注入,还是控制反转,其含义
完全相同。当某个Java对象(调用者)需要调用另一个Java对象(被依赖对象)的方 法时,在
传统模式下通常有两种做法.
原始做法:调用者主动创建被依赖对象,然后再调用被依赖对象的方法。 简单工厂模式:调用者先找到被依赖对象的工厂,然后主动通过工厂去获取被依赖对象, 最后再调用被依赖对象的方法。
注意上面的主动二字,这必然会导致调用者与被依赖对象实现类的硬编码耦合,非常不利于项
目升级的维护。使用Spring框架之后,调用者无需主动获取被依赖对象,调用者只要被动
接受Spring容器为调用者的成员变量赋值即可,由此可见,使用Spring后,调用者获取被依
赖对象的方式由原来的主动获取,变成了被动接受——所以Rod Johnson称之为控制反转。
另外从Spring容器的角度来看,Spring容器负责将被依赖对象赋值给调用者的成员变量——
相当于为调用者注入它依赖的实例,因此Martine Fowler称之为依赖注入。
注入方式
1. 设置注入:IoC容器使用属性的setter方式注入被依赖的实例。
<property name="" ref="">
这种注入方式简单、直观,因而在Spring的依赖注入里大量使用。
优点:
- 与传统的JavaBean的写法更相似,程序开发人员更容易理解、接受。通过setter方法设定
依赖关系显得更加直观、自然。 - 对于复杂的依赖关系,如果采用构造注入,会导致构造器过于臃肿,难以阅读。Spring在
创建Bean实例时,需要同时实例化其依赖的全部实例,因而导致性能下降。而使用设值注入
,则能避免这些问题。 - 尤其在某些成员变量可选的情况下,多参数的构造器更加笨重。
2.构造注入:IoC容器使用构造器来注入被依赖的实例。
<constructor-arg ref="">
通俗来说,就是驱动Spring在底层以反射方式执行带指定参数的构造器,当执行带参数的构造
器时,就可利用构造器参数对成员变量执行初始化——这就是构造注入的本质。
优点:
- 构造注入可以在构造器中决定依赖关系的注入顺序,优先依赖的优先注入。
- 对于依赖关系无需变化的Bean,构造注入更有用处。因为没有setter方法,所有的依赖关系全部在构造器内设定,无须担心后续的代码对依赖关系产生破坏。
- 依赖关系只能在构造器中设定,则只有组件的创建者才能改变组件的依赖关系,对组件的调用者而言,组件内部的依赖关系完全透明,更符合高内聚的原则。
AOP
为什么需要AOP
AOP(Aspect Orient Programming)也就是面向切面编程,作为面向对象编程的一种补充,已经成为一种比较成熟的编程方式。其实AOP问世的时间并不太长,AOP和OOP互为补充,面向切面编程将程序运行过程分解成各个切面。
AOP专门用于处理系统中分布于各个模块(不同方法)中的交叉关注点的问题,在JavaEE应用中,常常通过AOP来处理一些具有横切性质的系统级服务,如事务管理、安全检查、缓存、对象池管理等,AOP已经成为一种非常常用的解决方案。
使用AspectJ实现AOPAspectJ是一个基于Java语言的AOP框架,提供了强大的AOP功能,其他很多AOP框架都借鉴或采纳其中的一些思想。其主要包括两个部 分:一个部分定义了如何表达、定义AOP编程中的语法规范,通过这套语法规范,可以方便地用AOP来解决Java语言中存在的交叉关注点的问题;另一个部 分是工具部分,包括编译、调试工具等。
AOP实现可分为两类
- 静态AOP实现: AOP框架在编译阶段对程序进行修改,即实现对目标类的增强,生成静态的AOP代理类,以AspectJ为代表
动态AOP实现: AOP框架在运行阶段动态生成AOP代理,以实现对目标对象的增强,以Spring AOP为代表
动态代理与静态代理区别静态代理:由程序员创建,再对其编译。在程序运行之前.class文件已经存在了。静态代理:在程序运行时,运用反射的机制动态创建而完成。无需程序员手动编写代码,利用jdk的动态代理机制即可,不仅简化了编程工作,且提高了软件的可扩展性,因为java的反射机制可以生成任意类型的动态代理。
动态代理使用场景:不允许直接访问某些类,对访问要做特殊处理;或者对原始方法进行统一的扩展,例如日志的记录。
一般来说,静态AOP实现具有较好的性能,但需要使用特殊的编译器。动态AOP实现是纯Java实现,因此无须特殊的编译器,但是通常性能略差。
AOP的基本概念
关于面向切面编程的一些术语
切面(Aspect): 切面用于组织多个Advice,Advice放在切面中定义
连接点(Joinpoint):程序执行过程中明确的点,如方法的调用,或者异常的抛出。在Spring AOP中,连接点总是方法的调用
通知(Advice):AOP框架在特定的切入点执行的 通知。处理有“around”、“before”和“after”等类型
切入点(Pointcut):可以插入 通知的连接点。简而言之,当某个连接点满足指定要求时,该连接点将被添加 通知,该连接点也就变成了切入点
目标对象(Target):目标对象就是被通知的对象,在AOP中,目标对象可以专心实现自身的业务逻辑,通知功能可以在程序运行期间自动引入。
代理(Proxy): 代理是目标对象中使用通知以后创建的对象,这个对象既拥有目标对象的全部功能,而且拥有通知提供的附加功能。
Spring的AOP支持
Spring中的AOP代理由Spring的IoC容器负责生成、管理,其依赖关系也由IoC容器负责管理。
为了在应用中使用@AspectJ支持,Spring需要添加三个库- aspectjweaver.jar
- aspectjrt.jar
- aopalliance.jar
-
spring事物的7大传播机制,5个隔离机制
2018-09-28 11:24:23上文理解到对spring事物,事物的隔离机制,这片具体说下事物的传播机制和隔离机制 -
spring事物隔离和传播机制
2018-09-28 10:05:01对于spring事物的简单理解,对面试或许有帮助,也是加深自己的记忆 -
Spring AOP实现机制
2018-08-02 22:15:30Spring AOP的实现机制中文版,动态代理及原理,自定义类加载器 -
Spring事务传播机制
2022-01-13 03:44:59二、Spring的事务传播机制 2.1 子事务的传播机制为REQUIRED 2.2 子事务的传播机制为REQUIRES_NEW 2.3 子事务的传播机制为NESTED 当我们在使用Spring所提供的事务功能时,如果是仅仅处理单个的事务,是比较容易...目录
1.1 开启事务(DataSourceTransactionManager.doBegin)
当我们在使用Spring所提供的事务功能时,如果是仅仅处理单个的事务,是比较容易把握事务的提交与回滚,不过一旦引入嵌套事务后,多个事务的回滚和提交就会变得复杂起来,各个事务之间是如何相互影响的,是一个值得讨论的点。
一、事务在Spring中是如何运作的
在了解嵌套事务之前,可以先看下单个事务在Spring中的处理流程,以便后面可以更清晰地认识嵌套事务的逻辑。
Spring事务使用AOP的机制实现,会在@Transactional注解修饰的方法前后分别织入开启事务的逻辑,以及提交或回滚的逻辑。@Transactional可以修饰在方法或者类上,区别就在于修饰于类上的,会对该类下符合条件的方法(例如private修饰的方法就不符合条件)前后都织入事务的逻辑。
具体的处理逻辑如下(具体的方法路径为TransactionInterceptor.invoke -> TransactionAspectSupport.invokeWithinTransaction):
1.1 开启事务(DataSourceTransactionManager.doBegin)
这里主要做了获取连接,并关闭自动提交,将@Transactional注解中的一些参数初始化到txObject对象中。
1.2 异常回滚(TransactionAspectSupport.completeTransactionAfterThrowing)
这里是事务异常回滚的地方,这里有个注意点是回滚会先用rollbackOn这个方法判断,默认情况下只有RunTimeException以及Error是会进行回滚的,除非在@Transactional显式声明了rollbackFor。
二、Spring的事务传播机制
当出现多个事务嵌套的场景发生时,Spring事务的处理会变得复杂一些,需要考虑嵌套事务下的提交顺序,以及回滚顺序。对此,Spring提供了多种的传播机制,每种传播机制的效果都不尽相同,以便应对各种复杂的业务场景。
正常处理的嵌套事务流程如下:
传播机制以及它们的效果如下:
REQUIRED:默认值,支持当前事务,如果没有事务会创建一个新的事务
SUPPORTS:支持当前事务,如果没有事务的话以非事务方式执行
MANDATORY:支持当前事务,如果没有事务抛出异常
REQUIRES_NEW:创建一个新的事务并挂起当前事务
NOT_SUPPORTED:以非事务方式执行,如果当前存在事务则将当前事务挂起
NEVER:以非事务方式进行,如果存在事务则抛出异常
NESTED:如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,则进行与REQUIRED类似的操作
以下的说明均建立在父事务的传播机制为REQUIRED的前提下,来探讨各种传播机制的回滚策略。
2.1 子事务的传播机制为REQUIRED
子事务
主事务
结果
异常
正常,并try-catch异常
均回滚
正常
异常
均回滚
正常
异常,并try-catch异常
不回滚
这里详细说下第一种场景,子事务异常情况下,主事务捕获了子事务的异常却仍发生了回滚。从代码来看:
回滚的原因在于,子事务失败的时候在回滚代码中设置了全局回滚的标识(AbstractPlatformTransactionManager.processRollback)。
之后主事务在进行事务提交时,会判断全局回滚标识是否存在。若存在就会进行回滚动作。(AbstractPlatformTransactionManager.commit)
2.2 子事务的传播机制为REQUIRES_NEW
子事务
主事务
结果
异常
正常,并try-catch异常
子回滚,主不回滚
正常
异常
子不回滚,主回滚
异常
正常
均回滚
这里主要说下第三种场景,因为从REQUIRES_NEW的描述中容易造成误解:创建一个新的事务并挂起当前事务,很容易理解为子事务独立于主事务,子事务回滚后不会影响主事务的执行。
我认为问题点主要在于对挂起的认识,可以看下Spring时如何执行的(AbstractPlatformTransactionManager.handleExistingTransaction):
在这个挂起动作中主要做了两件事,一是将全局ThreadLocal中的配置初始化,二是将原事务的信息保存在SuspendedResourcesHolder对象中。
当子事务异常回滚后,就会通过AbstractPlatformTransactionManager.resume方法恢复主事务。
恢复后,子会再向外抛出一个异常,因此主事务会接收到该异常,因此主事务也发生回滚。
总的来说,挂起的动作并不代表子完全独立于主。
2.3 子事务的传播机制为NESTED
子事务
主事务
结果
异常
正常,并try-catch异常
子回滚,主不回滚
正常
异常
均回滚
异常
正常
均回滚
NESTED是通过rollback的savepoint机制,实现异常回滚。相比于子事务为REQUIRED下,子事务异常,主事务try-catch了异常,REQUIRED情况下主子均会回滚,但NESTED模式主不会回滚。因此可以看下场景一,NESTED是如何处理的。
子事务如果是NESTED模式,则会保存savepoint,以便后面回滚到该savepoint。
回滚时,直接回滚到savepoint,且不会设置全局回滚标识。因此即相当于就是回滚了子事务。主事务由于try-catch了异常,因此执行方法的时候也没有抛出异常,正常走提交流程,且没有全局回滚标识,故不会回滚,正常提交。
PS: mysql的savepoint机制
BEGIN;
INSERT INTO test_entity values (1, 'aaa', 10); ①
SAVEPOINT savepoint1;
INSERT INTO test_entity values (2, 'bbb', 11); ②
ROLLBACK TO savepoint1;
RELEASE SAVEPOINT savepoint1;
COMMIT;
回滚到savepoint1,①会入库,②被回滚。
-
Spring中ApplicationContext加载机制
2011-06-03 17:22:06Spring中ApplicationContext加载机制 -
spring事务的传播机制
2020-08-03 09:41:24而spring事务是封装在数据库事务之上的一种事务处理机制,它有两种管理方式:编程式事务和声明式事务。在平时使用中,我们大多使用@Transactional声明式事务来管理,这也是spring推荐的方式,下面例子也统一采用此种...什么是事务?
数据库事务是指一系列严密操作,要么全部成功,要么全部失败。它有四种特性:原子性、一致性、隔离性和持久性。
而spring事务是封装在数据库事务之上的一种事务处理机制,它有两种管理方式:编程式事务和声明式事务。在平时使用中,我们大多使用@Transactional声明式事务来管理,这也是spring推荐的方式,下面例子也统一采用此种方式。
下面我们主要来看看spring事务的传播机制
spring事务的传播机制
spring事务的传播机制有七种:REQUIRED、REQUIRES_NEW、NESTED、SUPPORTS、NOT_SUPPORTED、MANDATORY和NEVER。
首先要知道事务的传播指的是一个方法调用另外一个方法并将事务传递给它,而传播机制是针对被调用者,控制它是否被传播或者被怎样传播。注:后面多次提到 此方法在/不在事务中,指的是调用者是否开启了事务。
下面我们来仔细分析一下这几种传播方式。
REQUIRED(有事务则加入,没有则创建)
REQUIRED是spring事务的默认方式,如果当前方法不在事务中,就创建一个新的事务;如果当前方法在事务中,就加入到这个事务中。
举个例子,我们操作book书籍表和title章节表,先插入book表,然后再插入title表(下面几种传播方式都以这两张表为例)。BookServiceImpl中方法(调用者)调用TitleServiceImpl中方法(被调用者)。下面分两种情况:
1、调用者有事务
@Slf4j @Service @AllArgsConstructor public class BookServiceImpl implements BookService { private final BookMapper bookMapper; private final TitleService titleService; @Override @Transactional public void bookTransaction() { String transactionName = TransactionSynchronizationManager.getCurrentTransactionName(); log.info("调用者中事务:{}", transactionName); bookMapper.insert(new Book().setAuthor("zpg")); //传播事务 titleService.titleTransaction(); } } @Slf4j @Service @AllArgsConstructor public class TitleServiceImpl implements TitleService { private final TitleMapper titleMapper; @Override @Transactional(propagation = Propagation.REQUIRED) public void titleTransaction() { String transactionName = TransactionSynchronizationManager.getCurrentTransactionName(); log.info("被调用者中事务:{}", transactionName); titleMapper.insert(new Title().setName("第一章")); } } // 输出,被调用者使用的是调用者的事务 调用者中事务:com.example.demo.transaction.service.impl.BookServiceImpl.requiredTest 被调用者中事务:com.example.demo.transaction.service.impl.BookServiceImpl.requiredTest
2、调用者无事务
//BookServiceImpl类 @Override public void bookTransaction() { String transactionName = TransactionSynchronizationManager.getCurrentTransactionName(); log.info("调用者中事务:{}", transactionName); bookMapper.insert(new Book().setAuthor("zpg")); //传播事务 titleService.titleTransaction(); } //TitleServiceImpl类 @Override @Transactional(propagation = Propagation.REQUIRED) public void titleTransaction() { String transactionName = TransactionSynchronizationManager.getCurrentTransactionName(); log.info("被调用者中事务:{}", transactionName); titleMapper.insert(new Title().setName("第一章")); } // 输出,被调用者创建新事务 调用者中事务:null 被调用者中事务:com.example.demo.transaction.service.impl.TitleServiceImpl.requiredTest
REQUIRES_NEW(新事务,有事务就创建新事务)
如果当前方法存在事务,则把当前事务挂起,(这个挂起怎么理解呢?就是将现存的事务放在一边,我不用这个事务),自己新建一个事务。调用者事务和被调用者事务两不相干。
//BookServiceImpl类 @Override @Transactional public void bookTransaction() { String transactionName = TransactionSynchronizationManager.getCurrentTransactionName(); log.info("调用者中事务:{}", transactionName); bookMapper.insert(new Book().setAuthor("zpg")); //传播事务 titleService.titleTransaction(); } //TitleServiceImpl类 @Override @Transactional(propagation = Propagation.REQUIRES_NEW) public void titleTransaction() { String transactionName = TransactionSynchronizationManager.getCurrentTransactionName(); log.info("被调用者中事务:{}", transactionName); titleMapper.insert(new Title().setName("第一章")); } // 输出,被调用者和调用者是两个不同事务 调用者中事务:com.example.demo.transaction.service.impl.BookServiceImpl.requiredTest 被调用者中事务:com.example.demo.transaction.service.impl.TitleServiceImpl.requiredTest
NESTED(嵌套事务,有事务就创建子事务)
如果当前方法存在事务,则创建一个子事务,等父事务提交以后,子事务再提交;如果当前没有事务,则新建事务。(子事务异常,父事务不一定回滚;但父事务异常,则子事务肯定回滚)
1、子事务异常
//BookServiceImpl类 @Override @Transactional public void bookTransaction() { String transactionName = TransactionSynchronizationManager.getCurrentTransactionName(); log.info("调用者中事务:{}", transactionName); bookMapper.insert(new Book().setAuthor("zpg")); try { // 传播事务,捕获异常 titleService.titleTransaction(); } catch (Exception e) { e.printStackTrace(); } } //TitleServiceImpl类 @Override @Transactional(propagation = Propagation.NESTED) public void titleTransaction() { String transactionName = TransactionSynchronizationManager.getCurrentTransactionName(); log.info("被调用者中事务:{}", transactionName); titleMapper.insert(new Title().setName("第一章")); throw new RuntimeException("子事务异常"); } // 输出,title表插入回滚了,但是book表插入未回滚 调用者中事务:com.example.demo.transaction.service.impl.BookServiceImpl.requiredTest 被调用者中事务:com.example.demo.transaction.service.impl.BookServiceImpl.requiredTest java.lang.RuntimeException: 子事务异常
2、父事务异常
//BookServiceImpl类 @Override @Transactional public void bookTransaction() { String transactionName = TransactionSynchronizationManager.getCurrentTransactionName(); log.info("调用者中事务:{}", transactionName); bookMapper.insert(new Book().setAuthor("zpg")); //传播事务 titleService.titleTransaction(); throw new RuntimeException("父事务异常"); } //TitleServiceImpl类 @Override @Transactional(propagation = Propagation.NESTED) public void titleTransaction() { String transactionName = TransactionSynchronizationManager.getCurrentTransactionName(); log.info("被调用者中事务:{}", transactionName); titleMapper.insert(new Title().setName("第一章")); } // 输出,book的插入和title的插入都回滚了 调用者中事务:com.example.demo.transaction.service.impl.BookServiceImpl.requiredTest 被调用者中事务:com.example.demo.transaction.service.impl.BookServiceImpl.requiredTest java.lang.RuntimeException: 父事务异常
SUPPORTS(支持事务,有没有都可以)
如果当前方法存在事务,则加入事务;如果当前方法不存在事务,则以非事务方式运行。这个传播行为和不写没多大区别,以后有这需求,可以不用写@Transactional。
代码:略
NOT_SUPPORTED(不支持事务,有事务也是以非事务方式执行)
这个传播行为就是,不管咋地都传不到我身上,我就是一直以非事务方式执行。
//BookServiceImpl类 @Override @Transactional public void bookTransaction() { String transactionName = TransactionSynchronizationManager.getCurrentTransactionName(); log.info("调用者中事务:{}", transactionName); bookMapper.insert(new Book().setAuthor("zpg")); //传播事务 titleService.titleTransaction(); } //TitleServiceImpl类 @Override @Transactional(propagation = Propagation.NOT_SUPPORTED) public void titleTransaction() { String transactionName = TransactionSynchronizationManager.getCurrentTransactionName(); log.info("被调用者中事务:{}", transactionName); titleMapper.insert(new Title().setName("第一章")); throw new RuntimeException("异常"); } // 输出,虽然被调用者中有事务,也抛出异常,但是数据不会回滚 调用者中事务:com.example.demo.transaction.service.impl.BookServiceImpl.requiredTest 被调用者中事务:com.example.demo.transaction.service.impl.TitleServiceImpl.requiredTest java.lang.RuntimeException: 异常
MANDATORY(必须有事务,没有就抛异常)
这个传播行为就是不管咋地我都要在事务中,如果当前方法在事务中,就加入当前事务中;如果当前方法不在事务中,就抛异常。
1、当前方法存在事务
//BookServiceImpl类 @Override @Transactional public void bookTransaction() { String transactionName = TransactionSynchronizationManager.getCurrentTransactionName(); log.info("调用者中事务:{}", transactionName); bookMapper.insert(new Book().setAuthor("zpg")); //传播事务 titleService.titleTransaction(); } //TitleServiceImpl类 @Override @Transactional(propagation = Propagation.MANDATORY) public void titleTransaction() { String transactionName = TransactionSynchronizationManager.getCurrentTransactionName(); log.info("被调用者中事务:{}", transactionName); titleMapper.insert(new Title().setName("第一章")); } // 输出,被调用者加入到当前事务中 调用者中事务:com.example.demo.transaction.service.impl.BookServiceImpl.requiredTest 被调用者中事务:com.example.demo.transaction.service.impl.BookServiceImpl.requiredTest
2、当前方法不存在事务
//BookServiceImpl类 @Override public void bookTransaction() { String transactionName = TransactionSynchronizationManager.getCurrentTransactionName(); log.info("调用者中事务:{}", transactionName); bookMapper.insert(new Book().setAuthor("zpg")); //传播事务 titleService.titleTransaction(); } //TitleServiceImpl类 @Override @Transactional(propagation = Propagation.MANDATORY) public void titleTransaction() { String transactionName = TransactionSynchronizationManager.getCurrentTransactionName(); log.info("被调用者中事务:{}", transactionName); titleMapper.insert(new Title().setName("第一章")); } // 输出,无事务则抛异常 调用者中事务:null org.springframework.transaction.IllegalTransactionStateException: No existing transaction found for transaction marked with propagation 'mandatory'
NEVER(不可能有事务,有事务就抛异常)
此传播方式就是不管咋地,谁调用我,你就不许有事务,否则我就抛异常。
1、调用者有事务
//BookServiceImpl类 @Override @Transactional public void bookTransaction() { String transactionName = TransactionSynchronizationManager.getCurrentTransactionName(); log.info("调用者中事务:{}", transactionName); bookMapper.insert(new Book().setAuthor("zpg")); //传播事务 titleService.titleTransaction(); } //TitleServiceImpl类 @Override @Transactional(propagation = Propagation.NEVER) public void titleTransaction() { String transactionName = TransactionSynchronizationManager.getCurrentTransactionName(); log.info("被调用者中事务:{}", transactionName); titleMapper.insert(new Title().setName("第一章")); } // 输出,调用者有事务,则被调用者就抛异常 调用者中事务:com.example.demo.transaction.service.impl.BookServiceImpl.requiredTest org.springframework.transaction.IllegalTransactionStateException: Existing transaction found for transaction marked with propagation 'never'
2、调用者无事务
//BookServiceImpl类 @Override public void bookTransaction() { String transactionName = TransactionSynchronizationManager.getCurrentTransactionName(); log.info("调用者中事务:{}", transactionName); bookMapper.insert(new Book().setAuthor("zpg")); //传播事务 titleService.titleTransaction(); } //TitleServiceImpl类 @Override @Transactional(propagation = Propagation.NEVER) public void titleTransaction() { String transactionName = TransactionSynchronizationManager.getCurrentTransactionName(); log.info("被调用者中事务:{}", transactionName); titleMapper.insert(new Title().setName("第一章")); } // 输出,调用者无事务,被调用者虽然有事务,但是会以无事务方式执行 调用者中事务:null 被调用者中事务:com.example.demo.transaction.service.impl.TitleServiceImpl.requiredTest
总结
接下来我们总结一下各种传播方式下,调用者和被调用者是怎么操作事务的。注:A方法是调用者,B方法是被调用者。对于A方法来说,就两种情况:有事务和无事务,而对于B方法来说,有七种情况,下面看看每种情况下B方法是怎样操作事务的。
A方法调用B方法,B方法定义的事务类型 A方法有事务时 A方法无事务时 REQUIRED:默认 B和A事务合并成一个事务 B新建一个事务 REQUIRES_NEW:必须新的 B新建一个事务,和A事务无关,互不影响 B新建一个事务 NESTED:嵌套 B新建一个A的子事务,A异常影响B,B异常不影响A B新建一个事务 SUPPORTS:支持 B加入到A事务中 B无事务 NOT_SUPPORTED:不支持 挂起A事务,B以无事务方式执行 B无事务 MANDATORY:强制 B加入到A事务中 B抛异常 NEVER:绝不 B抛异常 B无事务
扫一扫,关注我 -
Spring的事务机制实例代码
2020-08-28 01:21:50主要介绍了Spring的事务机制实例代码,分享了相关代码示例,小编觉得还是挺不错的,具有一定借鉴价值,需要的朋友可以参考下 -
spring 时间机制
2013-09-23 18:11:41Spring ApplicationContext 事件机制 -
Spring原理机制
2018-10-19 09:17:011.Spring是一个开源的轻量级框架,他的核心主要有两部分组成IOC(Inversion of control)控制反转和AOP(Aspect oriented programming)面向切面编程. 2.那么什么是IOC:调用类中的方法不是通过new它的对象来实现而是... -
spring事务传播机制
2021-07-26 15:12:52spring事务传播机制前言一、7种事务传播类型1.1 支持当前事务1.2 不支持当前事务1.3 NESTED二、示例2.1 required2.读入数据总结 前言 spring事务传播行为的含义: 简单的理解就是多个事务方法相互调用时,事务如何... -
spring 事务机制总结
2020-06-28 19:37:58目录 为什么会有传播机制 传播机制生效条件 传播机制类型 示例代码 ...spring 对事务的控制,是使用 aop 切面实现的,我们不用关心事务的开始,提交 ,回滚,只需要在方法上加 @Transactional 注解, -
spring 锁机制
2021-01-20 10:20:37从JDK1.6版本之后,synchronized本身也在不断优化锁的机制,有些情况下他并不会是一个很重量级的锁了。优化机制包括自适应锁、自旋锁、锁消除、锁粗化、轻量级锁和偏向锁。 锁的状态从低到高依次为自旋锁->偏向锁... -
SpringIOC和AOP实现机制模拟
2014-11-11 17:05:18SpringIOC和AOP实现机制模拟,来自与网络。 -
Spring Boot运行机制
2020-05-31 13:47:51这个问题通常会被问到,之前丁甲也只是背书应付面试,但这样始终是好的,于是我决定看看Spring Boot注解的源码,并从源码层面上来看看Spring Boot的运行机制究竟是什么。 首先,我们随便打开一个Spring Boot项目... -
《手撸 Spring》 • 小傅哥.pdf
2021-08-11 16:32:43通过带着读者手写简化版 Spring 框架,了解 Spring 核心原理。在手写Spring 源码的过程中会摘取整体框架中的核心逻辑,简化代码实现过程,保留核心功能,例如:IOC、AOP、Bean生命周期、上下文、作用域、资源处理等... -
Spring系列第27篇:spring事件机制详解
2020-05-10 16:36:54月底免费送书活动,这两天是最后的机会,大家尽快参与!面试官:看你是85年的我:嗯,35了面试官:那应该经验很丰富了,那我们来聊聊spring吧我:好,这块我用了10几年了,你随便问吧面试... -
Spring中的事件机制
2018-06-10 17:00:193、Spring事件机制实现 事件,ApplicationEvent,继承自EventObject。 事件监听器, ApplicationListener,是一个接口,继承自EventListener,实际中需要实现其onApplicationEvent方法。 事件发布, ... -
Spring事务传播机制详解
2018-08-21 15:55:28但是Spring事务有自己的特点,也就是事务传播机制。 所谓事务传播机制,也就是在事务在多个方法的调用中是如何传递的,是重新创建事务还是使用父方法的事务?父方法的回滚对子方法的事务是否有影响?这些都是可以...