Spring IOC、AOP与事务

是什么、为什么、怎么样这大概是所有问题必经之路,也是我们解决问题的思路。

  • 什么是 IoC?
  • IoC 解决了什么问题?
  • IoC 和 DI 的区别?
  • 什么是 AOP?
  • AOP 解决了什么问题?
  • AOP 为什么叫做切面编程?在这里插入图片描述

什么是IOC?
IoC (Inversion of control )控制反转/反转控制。它是一种思想不是一个技术实现。描述的是:Java 开发领域对象的创建以及管理的问题。
ioc的思想最核心的地方在于,资源不由使用资源的双方管理,而由不使用资源的第三方管理,Spring IOC则是第三方,用于对象的创建以及管理。

要了解控制反转IOC我觉得有必要先了解软件设计的一个重要思想:依赖倒置原则——把原本的高层建筑依赖底层建筑“倒置”过来,变成底层建筑依赖高层建筑。高层建筑决定需要什么,底层去实现这样的需求,但是高层并不用管底层是怎么实现的。这样就不会出现前面的“牵一发动全身”的情况
在这里插入图片描述

IoC 最常见以及最合理的实现方式叫做依赖注入(Dependency Injection,简称 DI)。

控制反转是目的,依赖注入是实现控制反转的手段。

何为依赖,依赖什么?

程序运行需要依赖外部的资源,提供程序内对象的所需要的数据、资源。应用程序依赖于IoC容器。

何为注入,注入什么?

配置文件把资源从外部注入到内部,容器加载了外部的文件、对象、数据,然后把这些资源注入给程序内的对象,维护了程序内外对象之间的依赖关系。

所以说,控制反转是通过依赖注入实现的。像是「从不同角度描述的同一件事」:

  • IoC 是设计思想,DI 是具体的实现方式;
  • IoC 是理论,DI 是实践;

从而实现对象之间的解藕。

当然,IoC 也可以通过其他的方式来实现,而 DI 只是 Spring 的选择。
在这里插入图片描述
为什么叫控制反转
控制 :指的是对象创建(实例化、管理)的权力
反转 :控制权交给外部环境(Spring 框架、IoC 容器)
谁控制谁,控制什么:传统Java SE程序设计,我们直接在对象内部通过new进行创建对象,是程序主动去创建依赖对象;而IoC是有专门一个容器来创建这些对象,即由Ioc容器来控制对象的创建;谁控制谁?当然是IoC 容器控制了对象;控制什么?那就是主要控制了外部资源获取(不只是对象包括比如文件等)。

●为何是反转,哪些方面反转了:有反转就有正转,传统应用程序是由我们自己在对象中主动控制去直接获取依赖对象,也就是正转;而反转则是由容器来帮忙创建及注入依赖对象;为何是反转?因为由容器帮我们查找及注入依赖对象,对象只是被动的接受依赖对象,所以是反转;哪些方面反转了?依赖对象的获取被反转了。

IoC 解决了什么问题?
IoC 的思想就是两方之间不互相依赖,由第三方容器来管理相关资源。这样有什么好处呢?

解耦:对象之间的耦合度或者说依赖程度降低;
管理:资源变的容易管理;比如你用 Spring 容器提供的话很容易就可以实现一个单例。

将对象之间的相互依赖关系交给 IoC 容器来管理,并由 IoC 容器完成对象的注入。这样可以很大程度上简化应用的开发,把应用从复杂的依赖关系中解放出来。 IoC 容器就像是一个工厂一样,当我们需要创建一个对象的时候,只需要配置好配置文件/注解即可,完全不用考虑对象是如何被创建出来的。
推荐阅读:https://www.zhihu.com/question/23277575/answer/169698662

Spring的IOC理解
(1)IOC就是控制反转,是指创建对象的控制权的转移,以前创建对象的主动权和时机是由自己把控
的,而现在这种权力转移到Spring容器中,并由容器根据配置文件去创建实例和管理各个实例之间的依
赖关系,对象与对象之间松散耦合,也利于功能的复用。DI依赖注入,和控制反转是同一个概念的不同
角度的描述,即 应用程序在运行时依赖IoC容器来动态注入对象需要的外部资源。
(2)最直观的表达就是,IOC让对象的创建不用去new了,可以由spring自动生产,使用java的反射机
制,根据配置文件在运行时动态的去创建对象以及管理对象,并调用对象的方法的。
(3)Spring的IOC有三种注入方式 :构造器注入、setter方法注入、根据注解注入。

IoC让相互协作的组件保持松散的耦合,而AOP编程允许你把遍布于应用各层的功能分离出来形成可重用的功能组件。

Spring IOC实现流程原理

在Spring中,最基本的IOC容器接口是BeanFactory - 这个接口为具体的IOC容器的实现作了最基本的功能规定,IOC 总体来说有两处地方最重要,一个是创建 Bean 容器,一个是初始化 Bean.
启动 Spring 容器

public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext(“classpath:applicationfile.xml”);
}

ApplicationContext 启动过程中,会负责创建实例 Bean,往各个 Bean 中注入依赖等.
在这里插入图片描述spring ioc初始化流程?
resource定位 即寻找⽤户定义的bean资源,由 ResourceLoader通过统⼀的接⼝Resource接⼝来完成beanDefinition载⼊ BeanDefinitionReader读取、解析Resource定位的资源 成BeanDefinition 载⼊到ioc中(通过HashMap进⾏维护BD) BeanDefinition注册 即向IOC容器注册这些BeanDefinition, 通过BeanDefinitionRegistery实现

BeanFactory,从名字上也很好理解,生产 bean 的工厂,它负责生产和管理各个 bean 实例。
流程:
启动容器->创建bean工厂(BeanFactory)->依靠BeanFactory来getBean->创建bean->创建bean实例->通过反射生成bean->填充属性,注入依赖->初始化bean

核心方法

@Override
public void refresh() throws BeansException, IllegalStateException {
   // 来个锁,不然 refresh() 还没结束,你又来个启动或销毁容器的操作,那不就乱套了嘛
   synchronized (this.startupShutdownMonitor) {

      // 准备工作,记录下容器的启动时间、标记“已启动”状态、处理配置文件中的占位符
      prepareRefresh();

      // 这步比较关键,这步完成后,配置文件就会解析成一个个 Bean 定义,注册到 BeanFactory 中,
      // 当然,这里说的 Bean 还没有初始化,只是配置信息都提取出来了,
      // 注册也只是将这些信息都保存到了注册中心(说到底核心是一个 beanName-> beanDefinition 的 map)
      ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();

      // 设置 BeanFactory 的类加载器,添加几个 BeanPostProcessor,手动注册几个特殊的 bean
      // 准备 Bean 容器: prepareBeanFactory,Spring 把我们在 xml 配置的 bean 都注册以后,会"手动"注册一些特殊的 bean。
      prepareBeanFactory(beanFactory);

      try {
         // 【这里需要知道 BeanFactoryPostProcessor 这个知识点,Bean 如果实现了此接口,
         // 那么在容器初始化以后,Spring 会负责调用里面的 postProcessBeanFactory 方法。】

         // 这里是提供给子类的扩展点,到这里的时候,所有的 Bean 都加载、注册完成了,但是都还没有初始化
         // 具体的子类可以在这步的时候添加一些特殊的 BeanFactoryPostProcessor 的实现类或做点什么事
         postProcessBeanFactory(beanFactory);
         // 调用 BeanFactoryPostProcessor 各个实现类的 postProcessBeanFactory(factory) 方法
         invokeBeanFactoryPostProcessors(beanFactory);

         // 注册 BeanPostProcessor 的实现类,注意看和 BeanFactoryPostProcessor 的区别
         // 此接口两个方法: postProcessBeforeInitialization 和 postProcessAfterInitialization
         // 两个方法分别在 Bean 初始化之前和初始化之后得到执行。注意,到这里 Bean 还没初始化
         registerBeanPostProcessors(beanFactory);

         // 初始化当前 ApplicationContext 的 MessageSource,国际化
         initMessageSource();

         // 初始化当前 ApplicationContext 的事件广播器
         initApplicationEventMulticaster();

         // 从方法名就可以知道,典型的模板方法(钩子方法),
         // 具体的子类可以在这里初始化一些特殊的 Bean(在初始化 singleton beans 之前)
         onRefresh();

         // 注册事件监听器,监听器需要实现 ApplicationListener 接口。
         registerListeners();

         // 重点,重点,重点
         // 初始化所有的 singleton beans
         //(lazy-init 的除外)
         finishBeanFactoryInitialization(beanFactory);

         // 最后,广播事件,ApplicationContext 初始化完成
         finishRefresh();
      }

      catch (BeansException ex) {
         if (logger.isWarnEnabled()) {
            logger.warn("Exception encountered during context initialization - " +
                  "cancelling refresh attempt: " + ex);
         }

         // Destroy already created singletons to avoid dangling resources.
         // 销毁已经初始化的 singleton 的 Beans,以免有些 bean 会一直占用资源
         destroyBeans();

         // Reset 'active' flag.
         cancelRefresh(ex);

         // 把异常往外抛
         throw ex;
      }

      finally {
         // Reset common introspection caches in Spring's core, since we
         // might not ever need metadata for singleton beans anymore...
         resetCommonCaches();
      }
   }
}

在这里插入图片描述

在这里插入图片描述在这里插入图片描述spring的类加载
在这里插入图片描述类从被加载到虚拟机内存中开始,直到卸载出内存为止,它的整个生命周期包括了:加载、验证、准备、解析、初始化、使用和卸载这7个阶段。其中,验证、准备和解析这三个部分统称为连接(linking)。
当然Spring中的加载过程中,其加载过程还是遵循JVM的加载过程。
JVM的类加载
在这里插入图片描述

DI依赖注⼊流程? (实例化,处理Bean之间的依赖关系)
过程在Ioc初始化后,依赖注⼊的过程是⽤户第⼀次向IoC容器索要Bean时触发

  • 如果设置lazy-init=true,会在第⼀次getBean的时候才初始化bean, lazy-init=false,会容器启动的时候直接初始化(singleton bean);
  • 调⽤BeanFactory.getBean()⽣成bean的;
  • ⽣成bean过程运⽤装饰器模式产⽣的bean都是beanWrapper(bean的增强);

依赖注⼊怎么处理bean之间的依赖关系?
其实就是通过在beanDefinition载⼊时,如果bean有依赖关系,通过占位符来代替,在调⽤getbean时候,如果遇到占位符,从ioc⾥获取bean注⼊到本实例来
Spring的IOC注⼊⽅式
一、构造器注入
将被依赖对象通过构造函数的参数注入给依赖对象,并且在初始化对象的时候注入。
优点:
对象初始化完成后便可获得可使用的对象。
缺点:
当需要注入的对象很多时,构造器参数列表将会很长;
不够灵活。若有多种注入方式,每种方式只需注入指定几个依赖,那么就需要提供多个重载的构造函数,麻烦。
二、setter方法注入
IoC Service Provider通过调用成员变量提供的setter函数将被依赖对象注入给依赖类。
优点:
灵活。可以选择性地注入需要的对象。
缺点:
依赖对象初始化完成后由于尚未注入被依赖对象,因此还不能使用。
三、接口注入
依赖类必须要实现指定的接口,然后实现该接口中的一个函数,该函数就是用于依赖注入。该函数的参数就是要注入的对象。
优点
接口注入中,接口的名字、函数的名字都不重要,只要保证函数的参数是要注入的对象类型即可。
缺点:
侵入性太强,不建议使用。
PS:什么是侵入性?
如果类A要使用别人提供的一个功能,若为了使用这功能,需要在自己的类中增加额外的代码,这就是侵入性

Bean的⽣命周期?

  • 实例化Bean: Ioc容器通过获取BeanDefinition对象中的信息进⾏实例化,实例化对象被包装在BeanWrapper对象中
  • 设置对象属性(DI):通过BeanWrapper提供的设置属性的接⼝完成属性依赖注⼊;
  • 注⼊Aware接⼝(BeanFactoryAware, 可以⽤这个⽅式来获取其它 Bean,ApplicationContextAware):Spring会检测该对象是否实现了xxxAware接⼝,并将相关的xxxAware实例注⼊给bean
  • BeanPostProcessor:⾃定义的处理(分前置处理和后置处理)
  • InitializingBean和init-method:执⾏我们⾃⼰定义的初始化⽅法
  • 使⽤
  • destroy:bean的销毁

IOC:控制反转:将对象的创建权,由Spring管理. DI(依赖注⼊):在Spring创建对象的过程中,把对象依赖的属性注⼊到类中。

Bean定义5种作⽤域
Spring容器中的bean可以分为5个范围:
(1)singleton:默认,每个容器中只有一个bean的实例,单例的模式由BeanFactory自身来维护。
(2)prototype:为每一个bean请求提供一个实例。
(3)request:为每一个网络请求创建一个实例,在请求完成以后,bean会失效并被垃圾回收器回收。
(4)session:与request范围类似,确保每个session中有一个bean的实例,在session过期后,bean会随之失效。
(5)global-session:全局作用域,global-session和Portlet应用相关。当你的应用部署在Portlet容器中工作时,它包含很多portlet。如果你想要声明让所有的portlet共用全局的存储变量的话,那么这全局变量需要存储在global-session中。全局作用域与Servlet中的session作用域效果相同。

Spring 中的单例 bean 的线程安全问题了解吗?

大部分时候我们并没有在系统中使用多线程,所以很少有人会关注这个问题。单例 bean 存在线程问题,主要是因为当多个线程操作同一个对象的时候,对这个对象的非静态成员变量的写操作会存在线程安全问题。

常见的有两种解决办法:

  • 在Bean对象中尽量避免定义可变的成员变量(不太现实)。

  • 在类中定义一个ThreadLocal成员变量,将需要的可变成员变量保存在 ThreadLocal 中(推荐的一种方式)。

Spring如解决Bean循环依赖问题?
循环依赖说的是 A 依赖 B,而 B 又依赖 A。或者是 A 依赖 B,B 依赖 C,而 C 却依赖 A。
在这里插入图片描述
在这里插入图片描述

Spring中循环依赖场景有:构造器的循环依赖 属性的循环依赖

答:Spring通过三级缓存解决了循环依赖,其中一级缓存为单例池(singletonObjects),二级缓存为早期曝光对象earlySingletonObjects,三级缓存为早期曝光对象工厂(singletonFactories)。

当A、B两个类发生循环引用时,在A完成实例化后,就使用实例化后的对象去创建一个对象工厂,并添加到三级缓存中,如果A被AOP代理,那么通过这个工厂获取到的就是A代理后的对象,如果A没有被AOP代理,那么这个工厂获取到的就是A实例化的对象。

当A进行属性注入时,会去创建B,同时B又依赖了A,所以创建B的同时又会去调用getBean(a)来获取需要的依赖,此时的getBean(a)会从缓存中获取:

第一步,先获取到三级缓存中的工厂;

第二步,调用对象工工厂的getObject方法来获取到对应的对象,得到这个对象后将其注入到B中。紧接着B会走完它的生命周期流程,包括初始化、后置处理器等。

当B创建完后,会将B再注入到A中,此时A再完成它的整个生命周期。至此,循环依赖结束!

面试官:”为什么要使用三级缓存呢?二级缓存能解决循环依赖吗?“

答:如果要使用二级缓存解决循环依赖,意味着所有Bean在实例化后就要完成AOP代理,这样违背了Spring设计的原则,Spring在设计之初就是通过AnnotationAwareAspectJAutoProxyCreator这个后置处理器来在Bean生命周期的最后一步来完成AOP代理,而不是在实例化后就立马进行AOP代理。

AOP(Aspect-Oriented Programming:面向切面编程)

AOP 是 OOP(面向对象编程)的一种延续补充。传统的OOP开发中的代码逻辑是自上而下的,在这过程中会产生些横切面的问题,而这些横切面问题又与主业务逻辑关系不大,散落在代码中难以维护。

AOP就是把这些横切面问题和主业务逻辑进行分离,达到解耦的⽬的,提⾼代码重⽤性和开发效率;

AOP 解决了什么问题
通过上面的分析可以发现,AOP 主要用来解决:在不改变原有业务逻辑的情况下,增强横切逻辑代码,根本上解耦合,避免横切逻辑代码重复。

AOP 为什么叫面向切面编程
:指的是横切逻辑,原有业务逻辑代码不动,只能操作横切逻辑代码,所以面向横切逻辑
:横切逻辑代码往往要影响的是很多个方法,每个方法如同一个点,多个点构成一个面。这里有一个面的概念

AOP 主要应⽤场景有:

  • 记录⽇志
  • 监控性能
  • 权限控制
  • 事务管理

AOP 核⼼概念
1、切⾯(aspect):类是对物体特征的抽象,切⾯就是对横切关注点的抽象
2、横切关注点:对哪些⽅法进⾏拦截,拦截后怎么处理,这些关注点称之为横切关注点。
3、连接点(joinpoint):被拦截到的点,因为 Spring 只⽀持⽅法类型的连接点,所以在Spring 中连接点指的就是被拦截到的⽅法,实际上连接点还可以是字段或者构造器。
4、切⼊点(pointcut):对连接点进⾏拦截的定义
5、通知(advice):所谓通知指的就是指拦截到连接点之后要执⾏的代码,通知分为前置、后置、异常、最终、环绕通知五类。
6、⽬标对象:代理的⽬标对象
7、织⼊(weave):将切⾯应⽤到⽬标对象并导致代理对象创建的过程
8、引⼊(introduction):在不修改代码的前提下,引⼊可以在运⾏期为类动态地添加⽅法或字段。

AOP实现
AOP的实现方式基于IOC,通过beanPostProcessor来自定义bean的结构
主要分为两大类:
一是采用动态代理技术,利用截取消息的方式,对该消息进行装饰,以取代原有对象行为的执行;
二是采用静态织入的方式,引入特定的语法创建“方面”,从而使得编译器可以在编译期间织入有关“方面”的代码.

AOP实现的关键在于 代理模式,AOP代理主要分为静态代理和动态代理。静态代理的代表为AspectJ;动态代理则以Spring AOP为代表。
(1)AspectJ是静态代理的增强,所谓静态代理,就是AOP框架会在编译阶段生成AOP代理类,因此也称为编译时增强,他会在编译阶段将AspectJ(切面)织入到Java字节码中,运行的时候就是增强之后的AOP对象。因此也称为编译时增强;
(2)Spring AOP使用的动态代理,所谓的动态代理就是说AOP框架不会去修改字节码,而是每次运行时在内存中临时为方法生成一个AOP对象,这个AOP对象包含了目标对象的全部方法,并且在特定的切点做了增强处理,并回调原对象的方法。因此也被称为运行时增强。

Spring AOP就是基于动态代理的,如果要代理的对象,实现了某个接口,那么Spring AOP会使用JDK Proxy,去创建代理对象,而对于没有实现接口的对象,就无法使用 JDK Proxy 去进行代理了,这时候Spring AOP会使用Cglib ,这时候Spring AOP会使用 Cglib 生成一个被代理对象的子类来作为代理

①JDK动态代理只提供接口的代理,不支持类的代理。核心InvocationHandler接口和Proxy类, InvocationHandler 通过invoke()方法反射来调用目标类中的代码,动态地将横切逻辑和业务编织在一 起;接着,Proxy利用 InvocationHandler动态创建一个符合某一接口的的实例, 生成目标类的代理对 象。

②如果代理类没有实现 InvocationHandler 接口,那么Spring AOP会选择使用CGLIB来动态代理目 标类。CGLIB(Code Generation Library),是一个代码生成的类库,可以在运行时动态的生成指定类 的一个子类对象,并覆盖其中特定方法并添加增强代码,从而实现AOP。CGLIB是通过继承的方式做的动态代 理,因此如果某个类被标记为final,那么它是无法使用CGLIB做动态代理的。

如下图所示:在这里插入图片描述
在这里插入图片描述(3)静态代理与动态代理区别在于生成AOP代理对象的时机不同,相对来说AspectJ的静态代理方式具有更好的性能,但是AspectJ需要特定的编译器进行处理,而Spring AOP则无需特定的编译器处理。

jdk动态代理

  • 通过bind⽅法建⽴代理与真实对象关系,通过Proxy.newProxyInstance(target)⽣成代理对象
  • 代理对象通过反射invoke⽅法实现调⽤真实对象的⽅法

动态代理与静态代理区别

  • 静态代理,程序运⾏前代理类的.class⽂件就存在了;
  • 动态代理:在程序运⾏时利⽤反射动态创建代理对象<复⽤性,易⽤性,更加集中都调⽤invoke>

CGLIB与JDK动态代理区别

  • Jdk必须提供接⼝才能使⽤;
  • CGLIB不需要,只要⼀个⾮抽象类就能实现动态代理

Spring 中使⽤了哪些设计模式?
⼯⼚模式: spring中的BeanFactory就是简单⼯⼚模式的体现,根据传⼊唯⼀的标识来获得bean对象;
单例模式: 提供了全局的访问点BeanFactory;
代理模式: AOP功能的原理就使⽤代理模式(1、JDK动态代理。2、CGLib字节码⽣成技术代理。)
装饰器模式: 依赖注⼊就需要使⽤BeanWrapper;
观察者模式: spring中Observer模式常⽤的地⽅是listener的实现。如ApplicationListener。
策略模式: Bean的实例化的时候决定采⽤何种⽅式初始化bean实例(反射或者CGLIB动态字节码⽣成)

Spring设计模式推荐阅读:https://snailclimb.gitee.io/javaguide/#/docs/system-design/framework/spring/Spring-Design-Patterns?id=%e6%8e%a7%e5%88%b6%e5%8f%8d%e8%bd%acioc%e5%92%8c%e4%be%9d%e8%b5%96%e6%b3%a8%e5%85%a5di

Spring 事务(基于AOP实现)在这里插入图片描述

主流程介绍
众所周知,Spring事务采用AOP的方式实现,我们从TransactionAspectSupport这个类开始分析。

  1. 获取事务的属性(@Transactional注解中的配置)
  2. 加载配置中的TransactionManager.
  3. 获取收集事务信息TransactionInfo
  4. 执行目标方法
  5. 出现异常,尝试处理。
  6. 清理事务相关信息
  7. 提交事务
//1. 获取@Transactional注解的相关参数
TransactionAttributeSource tas = getTransactionAttributeSource();
// 2. 获取事务管理器
	final TransactionAttribute txAttr = (tas != null ? tas.getTransactionAttribute(method, targetClass) : null);
	final PlatformTransactionManager tm = determineTransactionManager(txAttr);
	final String joinpointIdentification = methodIdentification(method, targetClass, txAttr);

	if (txAttr == null || !(tm instanceof CallbackPreferringPlatformTransactionManager)) {
		// Standard transaction demarcation with getTransaction and commit/rollback calls.
		// 3. 获取TransactionInfo,包含了tm和TransactionStatus
		TransactionInfo txInfo = createTransactionIfNecessary(tm, txAttr, 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.
			// 4.执行目标方法
			retVal = invocation.proceedWithInvocation();
		}
		catch (Throwable ex) {
		   //5.回滚
			// target invocation exception
			completeTransactionAfterThrowing(txInfo, ex);
			throw ex;
		}
		finally {
		  // 6. 清理当前线程的事务相关信息
			cleanupTransactionInfo(txInfo);
		}
		// 提交事务
		commitTransactionAfterReturning(txInfo);
		return retVal;
	}

关键对象介绍PlatformTransactionManager

TransactionManager是做什么的?它保存着当前的数据源连接,对外提供对该数据源的事务提交回滚操作接口,同时实现了事务相关操作的方法。一个数据源DataSource需要一个事务管理器。
属性:DataSource
内部核心方法:
public commit 提交事务
public rollback 回滚事务
public getTransaction 获得当前事务状态
protected doSuspend 挂起事务
protected doBegin 开始事务,主要是执行了JDBC的con.setAutoCommit(false)方法。同时处理了很多和数据库连接相关的ThreadLocal变量。
protected doCommit 提交事务
protected doRollback 回滚事务
protected doGetTransaction() 获取事务信息
final getTransaction 获取事务状态

@Nullable
	protected PlatformTransactionManager determineTransactionManager(@Nullable TransactionAttribute txAttr) {
		// Do not attempt to lookup tx manager if no tx attributes are set
		if (txAttr == null || this.beanFactory == null) {
			return getTransactionManager();
		}

		String qualifier = txAttr.getQualifier();
		//如果指定了Bean则取指定的PlatformTransactionManager类型的Bean
		if (StringUtils.hasText(qualifier)) {
			return determineQualifiedTransactionManager(this.beanFactory, qualifier);
		}
		//如果指定了Bean的名称,则根据bean名称获取对应的bean
		else if (StringUtils.hasText(this.transactionManagerBeanName)) {
			return determineQualifiedTransactionManager(this.beanFactory, this.transactionManagerBeanName);
		}
		else {
		// 默认取一个PlatformTransactionManager类型的Bean
			PlatformTransactionManager defaultTransactionManager = getTransactionManager();
			if (defaultTransactionManager == null) {
				defaultTransactionManager = this.transactionManagerCache.get(DEFAULT_TRANSACTION_MANAGER_KEY);
				if (defaultTransactionManager == null) {
					defaultTransactionManager = this.beanFactory.getBean(PlatformTransactionManager.class);
					this.transactionManagerCache.putIfAbsent(
							DEFAULT_TRANSACTION_MANAGER_KEY, defaultTransactionManager);
				}
			}
			return defaultTransactionManager;
		}
	}

Spring 管理事务的方式有几种?

编程式事务,在代码中硬编码。(不推荐使用)
声明式事务,在配置文件中配置(推荐使用)

声明式事务又分为两种:

  • 基于XML的声明式事务
  • 基于注解的声明式事务

Spring 事务中的隔离级别有哪几种?

TransactionDefinition 接口中定义了五个表示隔离级别的常量:

✓TransactionDefinition.ISOLATION_DEFAULT: 使用后端数据库默认的隔离级别,Mysql 默认采用的 REPEATABLE_READ隔离级别 Oracle 默认采用的 READ_COMMITTED隔离级别.
✓TransactionDefinition.ISOLATION_READ_UNCOMMITTED: 最低的隔离级别,允许读取尚未提交的数据变更,可能会导致脏读、幻读或不可重复读
✓TransactionDefinition.ISOLATION_READ_COMMITTED: 允许读取并发事务已经提交的数据,可以阻止脏读,但是幻读或不可重复读仍有可能发生
✓TransactionDefinition.ISOLATION_REPEATABLE_READ: 对同一字段的多次读取结果都是一致的,除非数据是被本身事务自己所修改,可以阻止脏读和不可重复读,但幻读仍有可能发生。
✓TransactionDefinition.ISOLATION_SERIALIZABLE: 最高的隔离级别,完全服从ACID的隔离级别。所有的事务依次逐个执行,这样事务之间就完全不可能产生干扰,也就是说,该级别可以防止脏读、不可重复读以及幻读。但是这将严重影响程序的性能。通常情况下也不会用到该级别。

Spring 事务中哪几种事务传播行为?

支持当前事务的情况:

✓TransactionDefinition.PROPAGATION_REQUIRED: 如果当前存在事务,则加入该事务;如果当前没有事务,则创建一个新的事务。
✓TransactionDefinition.PROPAGATION_SUPPORTS: 如果当前存在事务,则加入该事务;如果当前没有事务,则以非事务的方式继续运行。
✓TransactionDefinition.PROPAGATION_MANDATORY: 如果当前存在事务,则加入该事务;如果当前没有事务,则抛出异常。(mandatory:强制性)

不支持当前事务的情况:

✓TransactionDefinition.PROPAGATION_REQUIRES_NEW: 创建一个新的事务,如果当前存在事务,则把当前事务挂起。
✓TransactionDefinition.PROPAGATION_NOT_SUPPORTED: 以非事务方式运行,如果当前存在事务,则把当前事务挂起。
✓TransactionDefinition.PROPAGATION_NEVER: 以非事务方式运行,如果当前存在事务,则抛出异常。

其他情况:

✓TransactionDefinition.PROPAGATION_NESTED: 如果当前存在事务,则创建一个事务作为当前事务的嵌套事务来运行;如果当前没有事务,则该取值等价于TransactionDefinition.PROPAGATION_REQUIRED。

猜你喜欢

转载自blog.csdn.net/qq_44961149/article/details/108489649