导航:
【黑马Java笔记+踩坑汇总】JavaSE+JavaWeb+SSM+SpringBoot+瑞吉外卖+SpringCloud+黑马旅游+谷粒商城+学成在线+常见面试题
目录
1、简单介绍Spring
2、说说你对IOC的理解
3、说说你对AOP的理解
4、说说Bean的生命周期
5、说说循环依赖和三级缓存
6、说说Bean的几种注册方式
7、说说Bean的作用域,以及默认的作用域
8、说说BeanFactory和FactoryBean的区别
9、说说@Autowired和@Resource注解的区别
10、说说Spring的两种事务模型
11、说说声明式事务失效的11个场景
12、为什么不推荐使用声明式事务?
13、 说说Spring事务的隔离级别
14、 说说Spring事务的传播行为
得分点:
IOC、Bean、DI、AOP、事务
为什么使用Spring?
Spring是一个用来简化web开发、微服务、分布式开发的框架,并且可以进行事务处理和整合其他框架。
IOC控制反转思想:创建对象的控制权由内部(即new实例化)反转到外部(即IOC容器)。
Bean:IOC容器中存放的一个个对象
DI依赖注入:绑定IOC容器中bean与bean之间的依赖关系。例如将dao层对象注入到service层对象。
AOP面向切面编程:在不改原有代码的前提下对代码进行增强。抽取共性代码(即通知),植入到待增强的方法(即切入点)。通知和切入点的关系即切面,增强的方法是创建原始对象增强后的代理对象。
事务:保证数据层或业务层一系列的数据库操作同成功同失败
标准回答
Spring框架包含众多模块,如Core、Testing、Data Access、Web Servlet等,其中Core是整个Spring框架的核心模块。
Core模块提供了IoC容器、AOP功能、数据绑定、类型转换等一系列的基础功能,而这些功能以及其他模块的功能都是建立在IoC和AOP之上的,所以IoC和AOP是Spring框架的核心。
IoC 控制反转
创建对象的控制权由内部(new实例化)反转到外部(即IOC容器),此思想称为控制反转。使用对象时,由主动new产生对象转换为由外部提供对象。
IoC(Inversion of Control)是控制反转的意思,这是一种面向对象编程的设计思想。在不采用这种思想的情况下,我们需要自己维护对象与对象之间的依赖关系,很容易造成对象之间的耦合度过高,在一个大型的项目中这十分的不利于代码的维护。IoC则可以解决这种问题,它可以帮我们维护对象与对象之间的依赖关系,降低对象之间的耦合度。
DI依赖注入
说到IoC就不得不说DI(Dependency Injection),DI是依赖注入的意思,它是IoC实现的实现方式,就是说IoC是通过DI来实现的。由于IoC这个词汇比较抽象而DI却更直观,所以很多时候我们就用DI来代替它,在很多时候我们简单地将IoC和DI划等号,这是一种习惯。而实现依赖注入的关键是IoC容器,它的本质就是一个工厂。
AOP 面向切面编程
AOP(Aspect Oriented Programing)是面向切面编程思想,这种思想是对OOP的补充,它可以在OOP的基础上进一步提高编程的效率。简单来说,它可以统一解决一批组件的共性需求(如权限检查、记录日志、事务管理等)。在AOP思想下,我们可以将解决共性需求的代码独立出来,然后通过配置的方式,声明这些代码在什么地方、什么时机调用。当满足调用条件时,AOP会将该业务代码植入到我们指定的位置,从而统一解决了问题,又不需要修改这一批组件的代码。
参考:
Spring基础1——Spring(配置开发版),IOC和DI_vincewm的博客-CSDN博客
Spring基础3——AOP,事务管理_spring aop事务管理_vincewm的博客-CSDN博客
得分点
控制反转与依赖注入含义
标准回答
IOC控制反转思想:创建对象的控制权由内部(即new实例化)反转到外部(即IOC容器)。
Bean:IOC容器中存放的一个个对象
DI依赖注入:绑定IOC容器中bean与bean之间的依赖关系。例如将dao层对象注入到service层对象。
IoC是控制反转的意思,是一种面向对象编程的设计思想。在不采用这种思想的情况下,我们需要自己维护对象与对象之间的依赖关系,很容易造成对象之间的耦合度过高,在一个大型的项目中这十分的不利于代码的维护。IoC则可以解决这种问题,它可以帮我们维护对象与对象之间的依赖关系,并且降低对象之间的耦合度。
说到IoC就不得不说DI,DI是依赖注入的意思,它是IoC实现的实现方式。由于IoC这个词汇比较抽象而DI比较直观,所以很多时候我们就用DI来代替它,在很多时候我们简单地将IoC和DI划等号,这是一种习惯。实现依赖注入的关键是IoC容器,它的本质就是一个工厂。
加分回答
IoC是Java EE企业应用开发中的就偶组件之间复杂关系的利器。
在以Spring为代表的轻量级Java EE开发风行之前,实际开发中是使用更多的是EJB为代表的开发模式。在EJB开发模式中,开发人员需要编写EJB组件,这种组件需要满足EJB规范才能在EJB容器中运行,从而完成获取事务,生命周期管理等基本服务,Spring提供的服务和EJB并没有什么区别,只是在具体怎样获取服务的方式上两者的设计有很大不同:Spring IoC提供了一个基本的JavaBean容器,通过IoC模式管理依赖关系,并通过依赖注入和AOP切面增强了为JavaBean这样的POJO对象服务于事务管理、生命周期管理等基本功能;而对于EJB,一个简单的EJB组件需要编写远程/本地接口、Home接口和Bean的实体类,而且EJB运行不能脱离EJB容器,查找其他EJB组件也需要通过诸如JNDI的方式,这就造成了对EJB容器和技术规范的依赖。也就是说Spring把EJB组件还原成了POJO对象或者JavaBean对象,以此降低了用用开发对于传统J2EE技术规范的依赖。
在应用开发中开发人员设计组件时往往需要引用和调用其他组件的服务,这种依赖关系如果固化在组件设计中,会造成依赖关系的僵化和维护难度的增加,这个时候使用IoC把资源获取的方向反转,让IoC容器主动管理这些依赖关系,将这些依赖关系注入到组件中,这就会让这些依赖关系的适配和管理更加灵活。
得分点
AOP概念、AOP作用、AOP的实现方式
标准回答
AOP面向切面编程:抽取共性代码(即通知),植入到待增强的方法(即切入点)。
作用:在不改原有代码的前提下对代码进行增强。
核心概念:
AOP底层实现方式:
通知类型:前置、后置、环绕(@Around("pt()")和ProceedingJoinPoint参数)、异常后、返回后通知
应用场景:日志和事务。
AOP是一种编程思想,是通过预编译方式和运行期动态代理的方式实现不修改源代码的情况下给程序动态统一添加功能的技术。面向对象编程将程序抽象成各个层次的对象,而面向切面编程是将程序抽象成各个切面。
所谓切面,相当于应用对象间的横切点,我们可以将其单独抽象为单独的模块。AOP技术利用一种称为“横切”的技术,剖解开封装对象的内部,将影响多个类的公共行为封装到一个可重用的模块中,并将其命名为切面。所谓的切面,简单来说就是与业务无关,却为业务模块所共同调用的逻辑,将其封装起来便于减少系统的重复代码,降低模块的耦合度,有利用未来的可操作性和可维护性。
利用AOP可以对业务逻辑各个部分进行隔离,从而使业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高开发效率。
AOP可以有多种实现方式,而Spring AOP支持如下两种实现方式。
- JDK动态代理:这是Java提供的动态代理技术,可以在运行时创建接口的代理实例。Spring AOP默认采用这种方式,在接口的代理实例中织入代码。
- CGLib(Code Generation Library)动态代理:采用底层的字节码技术,在运行时创建子类代理的实例。当目标对象不存在接口时,Spring AOP就会采用这种方式,在子类实例中织入代码。
加分回答-应用场景
在应用场景方面,Spring AOP为IoC的使用提供了更多的便利,一方面,应用可以直接使用AOP的功能,设计应用的横切关注点,把跨越应用程序多个模块的功能抽象出来,并通过简单的AOP的使用,灵活地编制到模块中,比如可以通过AOP实现应用程序中的日志功能。另一方面,在Spring内部,例如事务处理之类的一些支持模块也是通过Spring AOP来实现的。
AOP不能增强的类:
1. Spring AOP只能对IoC容器中的Bean进行增强,对于不受容器管理的对象不能增强。
2. 由于CGLib采用动态创建子类的方式生成代理对象,所以不能对final修饰的类进行代理。
得分点
Bean生命周期的四大部分以及详细步骤
生命周期四大部分 :Bean 定义、Bean 的初始化、Bean的生存期、Bean 的销毁。
Spring生命周期:
Bean 生命周期大致分为 Bean 定义、Bean 的初始化、Bean的生存期和 Bean 的销毁4个部分。
- 创建Bean:Spring启动,查找并加载需要被Spring管理的bean,进行Bean的实例化
- 属性填充:Bean实例化后对将Bean的引用和值注入到Bean的属性中
- BeanNameAware:如果Bean实现了BeanNameAware接口的话,Spring将Bean名传递给setBeanName()方法
- @PostConstruct:如果自定义初始化方法,并在该方法前增加@PostConstruct注解,Spring将执行这个方法
- BeanFactoryAware:如果Bean实现了BeanFactoryAware接口的话,Spring将调用setBeanFactory()方法,将BeanFactory容器实例传入
- ApplicationContextAware:如果Bean实现了ApplicationContextAware接口的话,Spring将调用Bean的setApplicationContext()方法,将bean所在应用上下文引用传入进来。
- BeanPostProcessor:如果Bean实现了BeanPostProcessor接口,Spring就将调用他们的postProcessBeforeInitialization()方法。
- InitializingBean:如果Bean 实现了InitializingBean接口,Spring将调用他们的afterPropertiesSet()方法。类似的,如果bean使用init-method声明了初始化方法,该方法也会被调用
- BeanPostProcessor:如果Bean 实现了BeanPostProcessor接口,Spring就将调用他们的postProcessAfterInitialization()方法。
- 生存期:Bean已就绪,可以被使用,所有Bean将一直驻留在应用上下文中,直到应用上下文被销毁。
- @PreDestroy:如果有方法注解了@PreDestroy;Spring容器将在自身销毁前,调用这个方法。
- 销毁:如果bean实现了DisposableBean接口,Spring将调用它的destory()接口方法,同样,如果bean使用了destory-method 声明销毁方法,该方法也会被调用。
循环依赖、三级缓存解决循环依赖底层原理、三级缓存介绍
循环依赖:
AService{注入BService}
BService{注入AService}
三级缓存解决循环依赖底层原理:
AService创建过程:
三级缓存:
singletonFactories存Lambda表达式示例:
singletonFactories.put('AService',()-> getEarlyBeanReference(beanName,mbd,AService普通对象);
三级缓存:
- singletonObjects:缓存的是已经经历了完整生命周期的bean对象。
- earlySingletonObjects:缓存未经过完整生命周期的bean,如果某个bean出现了循环依赖,就会提前把这个暂时未经过完整生命周期的bean放入earlySingletonObjects中,这个bean如果要经过AOP,那么就会把代理对象放入earlySingletonObjects中,否则就是把原始对象放入earlySingletonObjects,但是不管怎么样,代理对象所代理的原始对象也是没有经过完整生命周期的,所以放入earlySingletonObjects我们就可以统一认为是未经过完整生命周期的bean。
- singletonFactories:缓存的是一个ObjectFactory,也就是一个Lambda表达式。在每个Bean的生成过程中,经过实例化得到一个原始对象后,都会提前基于原始对象暴露一个Lambda表达式,并保存到三级缓存中,这个Lambda表达式可能用到,也可能用不到,如果当前Bean没有出现循环依赖,那么这个Lambda表达式没用,当前bean按照自己的生命周期正常执行,执行完后直接把当前bean放入singletonObjects中,如果当前bean在依赖注入时发现出现了循环依赖(当前正在创建的bean被其他bean依赖了),则从三级缓存中拿到Lambda表达式,并执行Lambda表达式得到一个对象,并把得到的对象放入二级缓存(如果当前Bean需要AOP,那么执行lambda表达式,得到就是对应的代理对象,如果无需AOP,则直接得到一个原始对象)。
Spring解决循环依赖的核心代码:一二级缓存找不到会去执行三级缓存的lambda表达式,并放入二级缓存:
protected Object getSingleton(String beanName, boolean allowEarlyReference) { // Quick check for existing instance without full singleton lock // 单例池存在Bean Object singletonObject = this.singletonObjects.get(beanName); // 单例池不存在,并且正在创建,去二级缓存中找 if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) { singletonObject = this.earlySingletonObjects.get(beanName); // 二级缓存中不存在并且第二个参数为true(默认为true,放入二级缓存) if (singletonObject == null && allowEarlyReference) { synchronized (this.singletonObjects) { // Consistent creation of early reference within full singleton lock singletonObject = this.singletonObjects.get(beanName); if (singletonObject == null) { singletonObject = this.earlySingletonObjects.get(beanName); if (singletonObject == null) { // 从三级缓存中得到当前Bean对应的信息 ObjectFactory> singletonFactory = this.singletonFactories.get(beanName); // 存在这些信息的时候 if (singletonFactory != null) { // 执行三级缓存的lambda表达式,并且赋值给外部的结果 singletonObject = singletonFactory.getObject(); // 将执行结果放入二级缓存 this.earlySingletonObjects.put(beanName, singletonObject); // 删除当前数据在三级缓存总的存储值 this.singletonFactories.remove(beanName); } } } } } } // 返回从单例池,或者早期单例池,或者刚执行完lambda表达式的结果 return singletonObject; }
得分点
singleton、prototype、request、session、globalSession、自定义作用域
标准回答
五种作用域:
在Bean上注解@Scope,设置作用域为singleton(默认)、prototype、request、session、globalSession。
自定义作用域:
在默认情况下,Bean在Spring容器中是单例的,但我们可以通过@Scope注解来修改Bean的作用域。这个注解有五个不同的取值,代表了Bean的五种不同类型作用域
singleton单例(默认) :在Spring容器中仅存在一个实例,即Bean以单例的形式存在。
prototype原型:每次调用getBean()时,都会执行new操作,返回一个新的实例。
request :每次HTTP请求都会创建一个新的Bean。
session :同一个HTTP Session共享一个Bean,不同的HTTP Session使用不同的Bean。
globalSession:同一个全局的Session共享一个Bean,一般用于Portlet环境。
加分回答-自定义作用域:
使用Spring的注解方式自定义Bean作用域很简单,您可以通过实现org.springframework.beans.factory.config.Scope接口并结合Spring框架中提供的@Scope注解来实现。
具体步骤如下:
- 实现Scope接口,并重写get()和remove()方法。
- 在实现类上添加@Scope注解,并指定其值为自定义Scope的名称。
get()方法用于从当前作用域中获取给定名称的对象实例。如果找不到该对象,则应返回null。 remove()方法用于从当前作用域中删除指定名称的对象实例。 如果找不到该对象,则无动作。
举个例子:假设您需要实现一个MyScope的自定义作用域,可以按照以下步骤:
定义MyScope类,该类实现Scope接口,如下所示:
public class MyScope implements Scope { private final Map
scopedObjects = new HashMap<>(); @Override public Object get(String name, ObjectFactory> objectFactory) { if (!scopedObjects.containsKey(name)) { scopedObjects.put(name, objectFactory.getObject()); } return scopedObjects.get(name); } @Override public Object remove(String name) { return scopedObjects.remove(name); } // 其他方法省略 }
得分点
BeanFactory定义、FactoryBean定义
标准回答
BeanFactory就是IOC容器,它有一些方法可以判断容器里Bean是否存在、是否单例等。
FactoryBean是接口,用来注册Bean。重写getObject()方法,返回的对象注册为Bean。
它们在拼写上非常相似,一个是Factory,也就是IoC容器或对象工厂,一个是Bean。
BeanFactory:
在Spring中,所有的Bean都是由BeanFactory(也就是IoC容器)来进行管理的。
FactoryBean:
但对FactoryBean而言,这个Bean不是简单的Bean,而是一个能产生或者修饰对象生成的工厂Bean,它的实现与设计模式中的工厂模式和修饰器模式类似。用户使用容器时,可以使用转义符“&”来得到FactoryBean本身,用来区分获取FactoryBean产生的对象和获取FactoryBean本身。举例来说,如果alphaObject是一个FactoryBean,那么使用&alphaObject得到 的是FactoryBean,而不是alphaObject这个FactoryBean产生出来的对象。其中,alphaObject是定义Bean的时候所指定的名字。
public class MyFactoryBean implements FactoryBean
{ //创建并返回了MyBean的实例 @Override public MyBean getObject() throws Exception { // 创建MyBean对象 return new MyBean(); } @Override public Class> getObjectType() { //返回的是MyBean的Class类型 return MyBean.class; } //指示了MyBean是否为单例模式 @Override public boolean isSingleton() { return true; } } 加分回-BeanFactory定义方法:
- getBean(String name):
Spring容器中获取对应Bean对象的方法,如存在,则返回该对象
- contnsBean(String name):Spring容器中是否存在该对象
- isSingleton(String name):通过beanName是否为单例对象
- isPrototype(String name):判断bean对象是否为多例对象
- isTypeMatch(String name, ResolvableType typeToMatch):判断name值获取出来的bean与typeToMath是否匹配
- getType(String name):获取Bean的Class类型
- getAliases(String name):获取name所对应的所有的别名
FactoryBean方法:
- T getObject():返回实例
- Class getObjectType();:返回该装饰对象的Bean的类型
- default boolean isSingleton():Bean是否为单例
得分点
注解来源、注入方式、@Resource装配顺序
两个注解的作用都是依赖注入。
@Autowired:
@Resource:
三种注入方法:
@Qualifier注解
@Autowired @Qualifier("bookDao1") private BookDao bookDao;
属性注入:
@RestController public class AppointmentNumberConfigurationController { @Autowired private AppointmentNumberConfigurationService numberConfigurationService; }
Setter注入:
@RestController public class UserController { private UserService userService; @Autowired public void setUserService(UserService userService) { this.userService= userService; }
Field注入:
@RestController public class UserController { final UserService userService; public UserController(UserService userService) { this.userService= userService; } }
得分点
两种事务编程模型、@Transactional原理、为什么不推荐使用@Transactional、隔离级别、传播机制、声明式事务失效的场景
两种事务模型:
Spring为事务管理提供了一致的模板,在高层次上建立了统一的事物抽象。
Spring支持两种事务编程模型:
1. 编程式事务
Spring提供了TransactionTemplate模板,利用该模板我们可以通过编程的方式实现事务管理,而无需关注资源获取、复用、释放、事务同步及异常处理等操作。相对于声明式事务来说,这种方式相对麻烦一些,但是好在更为灵活,我们可以将事务管理的范围控制的更为精确。
@Service public class MyService { @Autowired private TransactionTemplate transactionTemplate; public void doSomething() { transactionTemplate.execute(new TransactionCallback
() { public Void doInTransaction(TransactionStatus status) { try { // 业务逻辑代码 return null; } catch (Exception ex) { status.setRollbackOnly(); throw ex; } } }); } } 2. 声明式事务
Spring事务管理的亮点在于声明式事务管理,它允许我们通过声明的方式,在IoC配置中指定事务的边界和事务属性,Spring会自动在指定的事务边界上应用事务属性。相对于编程式事务来说,这种方式十分的方便,只需要在需要做事务管理的方法上,增加@Transactional注解,以声明事务特征即可。
声明式事务管理是Spring事务管理的亮点,它允许我们通过声明的方式,在IoC配置中指定事务的边界和事务属性,Spring会自动在指定的事务边界上应用事务属性。相对于编程式事务来说,这种方式十分的方便,只需要在需要做事务管理的方法上,增加@Transactional注解,以声明事务特征即可。
当我们在业务方法上添加@Transactional注解时,Spring会根据该注解生成一个代理对象,并将该代理对象包装成一个事务增强器,在业务方法执行前后自动开启和提交/回滚事务。
事务的打开、回滚和提交是由事务管理器来完成的,我们使用不同的数据库访问框架,就要使用与之对应的事务管理器。在Spring Boot中,当你添加了数据库访问框架的起步依赖时,它就会进行自动配置,即自动实例化正确的事务管理器。
对于声明式事务,是使用@Transactional进行标注的。这个注解可以标注在类或者方法上。
- 当它标注在类上时,代表这个类所有公共(public)非静态的方法都将启用事务功能。
- 当它标注在方法上时,代表这个方法将启用事务功能。
在@Transactional注解上,我们可以使用isolation属性声明事务的隔离级别,使用propagation属性声明事务的传播机制。
声明式事务失效的场景:任何使aop或异常捕获失效的场景
解决多线程事务失效:
public class TransactionDemo { private static final String DB_URL = "jdbc:mysql://localhost:3306/mydatabase"; private static final String DB_USER = "username"; private static final String DB_PASSWORD = "password"; public static void main(String[] args) { Connection connection = null; try { // 创建数据库连接 connection = DriverManager.getConnection(DB_URL, DB_USER, DB_PASSWORD); // 关闭自动提交 connection.setAutoCommit(false); // 创建并启动多个线程执行事务操作 Thread thread1 = new TransactionThread(connection, "Thread 1"); Thread thread2 = new TransactionThread(connection, "Thread 2"); thread1.start(); thread2.start(); // 等待线程执行完毕 thread1.join(); thread2.join(); // 提交事务 connection.commit(); System.out.println("事务提交成功!"); } catch (SQLException | InterruptedException e) { e.printStackTrace(); // 发生异常,回滚事务 try { if (connection != null) { connection.rollback(); System.out.println("事务回滚成功!"); } } catch (SQLException ex) { ex.printStackTrace(); } } finally { // 关闭数据库连接 try { if (connection != null) { connection.close(); } } catch (SQLException e) { e.printStackTrace(); } } } }
class TransactionThread extends Thread { private Connection connection; private String threadName; public TransactionThread(Connection connection, String threadName) { this.connection = connection; this.threadName = threadName; } @Override public void run() { try { // 在每个线程中执行事务操作 System.out.println(threadName + "开始执行事务操作..."); // connection执行事务相关的操作 // ... // 模拟发生异常 if (threadName.equals("Thread 2")) { throw new RuntimeException("模拟发生异常"); } System.out.println(threadName + "事务操作执行完毕!"); } catch (RuntimeException e) { e.printStackTrace(); // 发生异常,抛出异常以回滚事务 throw e; } } }
不推荐使用@Transactional的原因:
多个事务同时操作同一个数据库时,一个事务读取到其他事务已经提交但未持久化到数据库中的数据所带来的影响。
脏读:读到了脏数据。当前事务读到另一个未提交事务刚改的数据。
不可重复读:前后重复读的数据不一样。前后两次读同数据,这期间数据被其他事务改了,导致前后读取的数据不同。
幻读:前后读的数据是一样,但多了几行或少了几行,像幻觉一样。事务前后读的数据集合不同,导致出现“幻像”行。仅串行化能解决幻读问题。
Spring的事务隔离级别
Spring的事务隔离级别(Transaction Isolation Level)是指当多个事务同时操作同一个数据库时,一个事务读取到其他事务已经提交但未持久化到数据库中的数据所带来的影响。Spring提供了五种标准的事务隔离级别:
TRANSACTION_READ_UNCOMMITTED(读取未提交内容):隔离级别最低,事务可以读取到其他未提交事务的数据,可能出现脏读、不可重复读和幻读等问题。
TRANSACTION_READ_COMMITTED(读取已提交内容):保证其他事务提交后才能读取数据,解决了脏读问题,但依然存在不可重复读和幻读问题。
TRANSACTION_REPEATABLE_READ(可重复读):保证在一个事务内多次查询相同数据时,结果是一致的。解决了脏读、不可重复读问题,但仍会出现幻读问题。
TRANSACTION_SERIALIZABLE(序列化):隔离级别最高,在整个事务过程中将所有操作串行化执行,解决了以上所有问题,但对性能有较大影响。
TRANSACTION_DEFAULT(与数据库默认设置保持一致):使用数据库的默认隔离级别,该级别完全取决于数据库的实现。
选择相应的隔离级别需要考虑多方面的因素,包括应用程序的访问模式、数据的重要性、并发度等。默认情况下,Spring使用数据库默认的隔离级别设置。在编程或声明式事务管理时,可以通过@Transactional注解或相关配置来指定所需的事务隔离级别。
事务的传播行为:多个事务方法互相调用时,事务如何在这些方法之间传播。
事务的传播行为
事务的传播行为(Transaction Propagation Behavior)是指在多个事务处理过程中,各个事务之间获取资源、释放资源和执行操作的相关规则。Spring提供了七种标准的事务传播行为:
PROPAGATION_REQUIRED:默认传播行为,如果当前没有事务,则开启一个新的事务;如果已存在事务,就加入到该事务中。
PROPAGATION_SUPPORTS:支持当前事务。如果当前有事务,则使用该事务;没有则非事务执行。
PROPAGATION_MANDATORY:强制必须存在当前事务中,否则抛出异常。
PROPAGATION_REQUIRES_NEW:不管当前是否存在事务,都会新开一个事务执行当前方法,并挂起其他已存在的事务。
PROPAGATION_NOT_SUPPORTED:当前方法直接以非事务方式执行,如果当前存在事务,则先将事务挂起,待当前函数执行完毕后恢复已经挂起的事务。
PROPAGATION_NEVER:当前方法必须以非事务方式执行,如果当前有事务,则抛出异常。
PROPAGATION_NESTED:如果当前事务存在,则开启一个嵌套事务并执行每一个任务,如果嵌套事务成功,则与当前事务共同提交;嵌套事务失败时,仅回滚嵌套事务,而当前事务则继续运行。
以上所有传播行为,只要组成操作链时出现异常,并且该异常没有得到多层级捕获和处理,则整个过程会触发回滚操作。
选择哪种传播行为需要结合具体业务场景进行全面考虑,遵循最小化的管理原则,在保证数据正确性的前提下,尽可能地减少锁表、死锁、超时等因事务而带来的额外开销。默认情况下,Spring使用PROPAGATION_REQUIRED作为应用程序中所有有@Transactional注解的方法的传播行为设置。