Spring

什么是 Spring 框架?

Spring 是⼀种轻量级开发框架,旨在提⾼开发⼈员的开发效率以及系统的可维护性。Spring 官⽹:ht tps://spring.io/。

我们⼀般说 Spring 框架指的都是 Spring Framework,它是很多模块的集合,使⽤这些模块可以很⽅便地协助我们进⾏开发。这些模块是:核⼼容器、数据访问/集成,、Web、AOP(⾯向切⾯编程)、⼯ 具、消息和测试模块。⽐如:Core Container 中的 Core 组件是Spring 所有组件的核⼼,Beans 组件和 Context 组件是实现IOC和依赖注⼊的基础,AOP组件⽤来实现⾯向切⾯编程。 Spring 官⽹列出的 Spring 的 6 个特征:

  • 核⼼技术 :依赖注⼊(DI),AOP,事件(events),资源,i18n,验证,数据绑定,类型转换, SpEL。
  • 测试 :模拟对象,TestContext框架,Spring MVC 测试,WebTestClient。
  • 数据访问 :事务,DAO⽀持,JDBC,ORM,编组XML。
  • Web⽀持 : Spring MVC和Spring WebFlux Web框架。
  • 集成 :远程处理,JMS,JCA,JMX,电⼦邮件,任务,调度,缓存。
  • 语⾔ :Kotlin,Groovy,动态语⾔

列举⼀些重要的Spring模块?

下图对应的是 Spring4.x 版本。⽬前最新的5.x版本中 Web 模块的 Portlet 组件已经被废弃掉,同时增加了⽤于异步响应式处理的 WebFlux 组件。

image-20210504234632329

  • Spring Core: 基础,可以说 Spring 其他所有的功能都需要依赖于该类库。主要提供 IoC 依赖注⼊功能。
  • Spring Aspects : 该模块为与AspectJ的集成提供⽀持。
  • Spring AOP :提供了⾯向切⾯的编程实现。
  • Spring JDBC : Java数据库连接。
  • Spring JMS :Java消息服务。
  • Spring ORM : ⽤于⽀持Hibernate等ORM⼯具。
  • Spring Web : 为创建Web应⽤程序提供⽀持。
  • Spring Test : 提供了对 JUnit 和 TestNG 测试的⽀持。

@RestController vs @Controller

Controller返回⼀个⻚面

单独使⽤@Controller不加 @ResponseBody 的话⼀般使⽤在要返回⼀个视图的情况,这种情况属于⽐较传统的Spring MVC 的应⽤,对应于前后端不分离的情况。

image-20210504234812785

@RestController返回JSON 或 XML 形式数据

但 @RestController 只返回对象,对象数据直接以 JSON 或 XML 形式写⼊ HTTP 响应(Response) 中,这种情况属于 RESTful Web服务,这也是⽬前⽇常开发所接触的最常⽤的情况(前后端分离)。

image-20210504234846340

@Controller +@ResponseBody返回JSON 或 XML 形式数据 如果你需要在Spring4之前开发 RESTful Web服务的话,你需要使⽤ @Controller 并结合 @ResponseBody 注解,也就是说 @Controller + @ResponseBody = @RestController (Spring 4 之后新加的注解)。

image-20210504234926417

Spring中Autowired和Resource关键字的区别?

@Resource和@Autowired都是做bean的注入时使用,其实@Resource并不是Spring的注解,它的包 是javax.annotation.Resource,需要导入,但是Spring支持该注解的注入。

1、共同点

两者都可以写在字段和setter方法上。两者如果都写在字段上,那么就不需要再写setter方法。

2、不同点

(1) @Autowired

@Autowired为Spring提供的注解,需要导入包org.springframework.beans.factory.annotation.Autowired;只按照byType注入。

public class TestServiceImpl {
    // 下面两种@Autowired只要使用一种即可
    @Autowired
    private UserDao userDao; // 用于字段上

    @Autowired
    public void setUserDao(UserDao userDao) { // 用于属性的方法上
        this.userDao = userDao;
    }
}

@Autowired注解是按照类型(byType)装配依赖对象,默认情况下它要求依赖对象必须存在,如果允 许null值,可以设置它的required属性为false。如果我们想使用按照名称(byName)来装配,可以结合@Qualifier注解一起使用。如下:

public class TestServiceImpl { 
    @Autowired 
    @Qualifier("userDao")
    private UserDao userDao;
}

(2) @Resource

@Resource默认按照ByName自动注入,由J2EE提供,需要导入包javax.annotation.Resource。@Resource有两个重要的属性:name和type,而Spring将@Resource注解的name属性解析为bean的 名字,而type属性则解析为bean的类型。所以,如果使用name属性,则使用byName的自动注入策略,而使用type属性时则使用byType自动注入策略。如果既不制定name也不制定type属性,这时将通 过反射机制使用byName自动注入策略。

public class TestServiceImpl {
    // 下面两种@Resource只要使用一种即可
    @Resource(name="userDao")
    private UserDao userDao; // 用于字段上

    @Resource(name="userDao")
    public void setUserDao(UserDao userDao) { // 用于属性的setter方法上
        this.userDao = userDao;
    }
}

注:最好是将@Resource放在setter方法上,因为这样更符合面向对象的思想,通过set、get去操作属 性,而不是直接去操作属性。

@Resource装配顺序:

①如果同时指定了name和type,则从Spring上下文中找到唯一匹配的bean进行装配,找不到则抛出异 常。

②如果指定了name,则从上下文中查找名称(id)匹配的bean进行装配,找不到则抛出异常。

③如果指定了type,则从上下文中找到类似匹配的唯一bean进行装配,找不到或是找到多个,都会抛 出异常。

④如果既没有指定name,又没有指定type,则自动按照byName方式进行装配;如果没有匹配,则回 退为一个原始类型进行匹配,如果匹配则自动装配。

@Resource的作用相当于@Autowired,只不过@Autowired按照byType自动注入。

Spring的IOC和AOP机制?

我们是在使用Spring框架的过程中,其实就是为了使用IOC,依赖注入,和AOP,面向切面编程,这两 个是Spring的灵魂。

主要用到的设计模式有工厂模式和代理模式。

IoC(Inverse of Control:控制反转)是⼀种设计思想,就是 将原本在程序中⼿动创建对象的控制权,交由Spring框架来管理。 IoC 在其他语⾔中也有应⽤,并⾮ Spring 特有。 IoC 容器是 Spring⽤来实现 IoC 的载体, IoC 容器实际上就是个Map(key,value),Map 中存放的是各种对象。

IOC就是典型的工厂模式,通过sessionfactory去注入实例。AOP就是典型的代理模式的体现。

代理模式是常用的java设计模式,他的特征是代理类与委托类有同样的接口,代理类主要负责为委托类 预处理消息、过滤消息、把消息转发给委托类,以及事后处理消息等。代理类与委托类之间通常会存在 关联关系,一个代理类的对象与一个委托类的对象关联,代理类的对象本身并不真正实现服务,而是通 过调用委托类的对象的相关方法,来提供特定的服务。

spring的IoC容器是spring的核心,spring AOP是spring框架的重要组成部分。

在传统的程序设计中,当调用者需要被调用者的协助时,通常由调用者来创建被调用者的实例。但在spring里创建被调用者的工作不再由调用者来完成,因此控制反转(IoC);创建被调用者实例的工作通 常由spring容器来完成,然后注入调用者,因此也被称为依赖注入(DI),依赖注入和控制反转是同一 个概念。

面向方面编程(AOP)是以另一个角度来考虑程序结构,通过分析程序结构的关注点来完善面向对象编程(OOP)。OOP将应用程序分解成各个层次的对象,而AOP将程序分解成多个切面。spring AOP 只实现了方法级别的连接点,在J2EE应用中,AOP拦截到方法级别的操作就已经足够。在spring中,未来使IoC方便地使用健壮、灵活的企业服务,需要利用spring AOP实现为IoC和企业服务之间建立联系。

IOC:控制反转也叫依赖注入。利用了工厂模式

将对象交给容器管理,你只需要在spring配置文件总配置相应的bean,以及设置相关的属性,让spring容器来生成类的实例对象以及管理对象。在spring容器启动的时候,spring会把你在配置文件 中配置的bean都初始化好,然后在你需要调用的时候,就把它已经初始化好的那些bean分配给你需要 调用这些bean的类(假设这个类名是A),分配的方法就是调用A的setter方法来注入,而不需要你在 A里面new这些bean了。

注意:面试的时候,如果有条件,画图,这样更加显得你懂了.

spring ioc初始化流程

image-20210502152220139

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

AOP可以说是对OOP的补充和完善。OOP引入封装、继承和多态性等概念来建立一种对象层次结构, 用以模拟公共行为的一个集合。当我们需要为分散的对象引入公共行为的时候,OOP则显得无能为力。 也就是说,OOP允许你定义从上到下的关系,但并不适合定义从左到右的关系。例如日志功能。日志代 码往往水平地散布在所有对象层次中,而与它所散布到的对象的核心功能毫无关系。在OOP设计中,它 导致了大量代码的重复,而不利于各个模块的重用。

将程序中的交叉业务逻辑(比如安全,日志,事务等),封装成一个切面,然后注入到目标对象(具体 业务逻辑)中去。

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

简单点解释,比方说你想在你的biz层所有类中都加上一个打印‘你好’的功能,这时就可以用aop思想来做. 你先写个类写个类方法,方法经实现打印‘你好’,然后Ioc这个类 ref=“biz.”让每个类都注入即可实现。

AOP(Aspect-Oriented Programming:⾯向切⾯编程)能够将那些与业务⽆关,却为业务模块所共同调⽤的逻辑或责任(例如事务处理、⽇志管理、权限控制等)封装起来,便于减少系统的重复代码,降低模 块间的耦合度,并有利于未来的可拓展性和可维护性。

Spring AOP就是基于动态代理的,如果要代理的对象,实现了某个接⼝,那么Spring AOP会使⽤JDK Proxy,去创建代理对象,⽽对于没有实现接⼝的对象,就⽆法使⽤ JDK Proxy 去进⾏代理了,这时候Spring AOP会使⽤Cglib ,这时候Spring AOP会使⽤ Cglib ⽣成⼀个被代理对象的⼦类来作为代理, 如下图所示:

image-20210504235254736

当然你也可以使⽤ AspectJ ,Spring AOP 已经集成了AspectJ ,AspectJ 应该算的上是 Java ⽣态系统中最完整的 AOP 框架了。

使⽤ AOP 之后我们可以把⼀些通⽤功能抽象出来,在需要⽤到的地⽅直接使⽤即可,这样⼤⼤简化了代码量。我们需要增加新功能时也⽅便,这样也提⾼了系统扩展性。⽇志功能、事务管理等等场景都⽤ 到了 AOP 。

Spring AOP 和 AspectJ AOP 有什么区别?

Spring AOP 属于运⾏时增强,⽽ AspectJ 是编译时增强。 Spring AOP 基于代理(Proxying),⽽AspectJ 基于字节码操作(Bytecode Manipulation)。Spring AOP 已经集成了 AspectJ ,AspectJ 应该算的上是 Java ⽣态系统中最完整的 AOP 框架了。

AspectJ 相⽐于 Spring AOP 功能更加强⼤,但是 Spring AOP 相对来说更简单,如果我们的切⾯⽐较少,那么两者性能差异不⼤。但是,当切⾯太多的话,最好选择 AspectJ ,它⽐Spring AOP 快很多

依赖注入的方式有几种,各是什么?

一、构造器注入

将被依赖对象通过构造函数的参数注入给依赖对象,并且在初始化对象的时候注入。

优点:

对象初始化完成后便可获得可使用的对象。

缺点:

当需要注入的对象很多时,构造器参数列表将会很长;

不够灵活。若有多种注入方式,每种方式只需注入指定几个依赖,那么就需要提供多个重载的构造函 数,麻烦。

二、setter方法注入

IoC Service Provider通过调用成员变量提供的setter函数将被依赖对象注入给依赖类。

优点:

灵活。可以选择性地注入需要的对象。

缺点:

依赖对象初始化完成后由于尚未注入被依赖对象,因此还不能使用。

三、接口注入

依赖类必须要实现指定的接口,然后实现该接口中的一个函数,该函数就是用于依赖注入。该函数的参 数就是要注入的对象。

优点

接口注入中,接口的名字、函数的名字都不重要,只要保证函数的参数是要注入的对象类型即可。

缺点:

侵入行太强,不建议使用。

PS:什么是侵入行?

如果类A要使用别人提供的一个功能,若为了使用这功能,需要在自己的类中增加额外的代码,这就是 侵入性。

讲一下什么是Spring

Spring是一个轻量级的IoC和AOP容器框架。是为Java应用程序提供基础性服务的一套框架,目的是用 于简化企业应用程序的开发,它使得开发者只需要关心业务需求。常见的配置方式有三种:基于XML的 配置、基于注解的配置、基于Java的配置。

主要由以下几个模块组成:

Spring Core:核心类库,提供IOC服务;

Spring Context:提供框架式的Bean访问方式,以及企业级功能(JNDI、定时任务等); Spring AOP:AOP服务;

Spring DAO:对JDBC的抽象,简化了数据访问异常的处理; Spring ORM:对现有的ORM框架的支持;

Spring Web:提供了基本的面向Web的综合特性,例如多方文件上传; Spring MVC:提供面向Web应用的Model-View-Controller实现。

Spring的IOC理解

(1) IOC就是控制反转,是指创建对象的控制权的转移,以前创建对象的主动权和时机是由自己把控 的,而现在这种权力转移到Spring容器中,并由容器根据配置文件去创建实例和管理各个实例之间的依 赖关系,对象与对象之间松散耦合,也利于功能的复用。DI依赖注入,和控制反转是同一个概念的不同 角度的描述,即 应用程序在运行时依赖IoC容器来动态注入对象需要的外部资源。

(2) 最直观的表达就是,IOC让对象的创建不用去new了,可以由spring自动生产,使用java的反射机 制,根据配置文件在运行时动态的去创建对象以及管理对象,并调用对象的方法的。

(3) Spring的IOC有三种注入方式 :构造器注入、setter方法注入、根据注解注入。

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

Spring的AOP理解:

OOP面向对象,允许开发者定义纵向的关系,但并适用于定义横向的关系,导致了大量代码的重复,而 不利于各个模块的重用。

AOP,一般称为面向切面,作为面向对象的一种补充,用于将那些与业务无关,但却对多个对象产生影 响的公共行为和逻辑,抽取并封装为一个可重用的模块,这个模块被命名为“切面”(Aspect),减少系 统中的重复代码,降低了模块间的耦合度,同时提高了系统的可维护性。可用于权限认证、日志、事务 处理。

AOP实现的关键在于 代理模式,AOP代理主要分为静态代理和动态代理。静态代理的代表为AspectJ; 动态代理则以Spring AOP为代表。

(1) AspectJ是静态代理的增强,所谓静态代理,就是AOP框架会在编译阶段生成AOP代理类,因此也称为编译时增强,他会在编译阶段将AspectJ(切面)织入到Java字节码中,运行的时候就是增强之后的AOP对象。

(2) Spring AOP使用的动态代理,所谓的动态代理就是说AOP框架不会去修改字节码,而是每次运行时在内存中临时为方法生成一个AOP对象,这个AOP对象包含了目标对象的全部方法,并且在特定的切点做了增强处理,并回调原对象的方法。

Spring AOP中的动态代理主要有两种方式,JDK动态代理和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则无需特定的编译器处理。

解释一下spring bean的生命周期

首先说一下Servlet的生命周期:实例化,初始init,接收请求service,销毁destroy;

Spring上下文中的Bean生命周期也类似,如下:

(1) 实例化Bean:

对于BeanFactory容器,当客户向容器请求一个尚未初始化的bean时,或初始化bean的时候需要注入 另一个尚未初始化的依赖时,容器就会调用createBean进行实例化。对于ApplicationContext容器,当 容器启动结束后,通过获取BeanDefinition对象中的信息,实例化所有的bean。

(2) 设置对象属性(依赖注入):

实例化后的对象被封装在BeanWrapper对象中,紧接着,Spring根据BeanDefinition中的信息 以及 通过BeanWrapper提供的设置属性的接口完成依赖注入。

(3) 处理Aware接口:

接着,Spring会检测该对象是否实现了xxxAware接口,并将相关的xxxAware实例注入给Bean:

①如果这个Bean已经实现了BeanNameAware接口,会调用它实现的setBeanName(String beanId)方法,此处传递的就是Spring配置文件中Bean的id值;

②如果这个Bean已经实现了BeanFactoryAware接口,会调用它实现的setBeanFactory()方法,传递的 是Spring工厂自身。

③如果这个Bean已经实现了ApplicationContextAware接口,会调用

setApplicationContext(ApplicationContext)方法,传入Spring上下文;

(4) BeanPostProcessor:

如果想对Bean进行一些自定义的处理,那么可以让Bean实现了BeanPostProcessor接口,那将会调用

postProcessBeforeInitialization(Object obj, String s)方法。

(5) InitializingBean 与 init-method:

如果Bean在Spring配置文件中配置了 init-method 属性,则会自动调用其配置的初始化方法。

(6) 如果这个Bean实现了BeanPostProcessor接口,将会调用postProcessAfterInitialization(Object obj, String s)方法;由于这个方法是在Bean初始化结束时调用的,所以可以被应用于内存或缓存技术;

以上几个步骤完成后,Bean就已经被正确创建了,之后就可以使用这个Bean了。

(7) DisposableBean:

当Bean不再需要时,会经过清理阶段,如果Bean实现了DisposableBean这个接口,会调用其实现的

destroy()方法;

(8) destroy-method:

最后,如果这个Bean的Spring配置中配置了destroy-method属性,会自动调用其配置的销毁方法。

解释Spring支持的几种bean的作用域。

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应用相关,Spring5已经没有了。当你的应用部署在Portlet容器中工作时,它包含很多portlet。如果你想要声明让所有的portlet共用全局的存储变量的话,那么这全局 变量需要存储在global-session中。全局作用域与Servlet中的session作用域效果相同。

Spring基于xml注入bean的几种方式:

(1) Set方法注入;

(2) 构造器注入:①通过index设置参数的位置;②通过type设置参数类型;

(3) 静态工厂注入;

(4) 实例工厂;

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

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

常⻅的有两种解决办法:

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

  2. 在类中定义⼀个ThreadLocal成员变量,将需要的可变成员变量保存在 ThreadLocal 中(推荐的⼀种⽅式)。

@Component 和 @Bean 的区别是什么?

  1. 作⽤对象不同:@Component注解作⽤于类,⽽ @Bean 注解作⽤于⽅法。

  2. @Component 通常是通过类路径扫描来⾃动侦测以及⾃动装配到Spring容器中(我们可以使⽤@ComponentScan 注解定义要扫描的路径从中找出标识了需要装配的类⾃动装配到 Spring 的bean 容器中)。 @Bean 注解通常是我们在标有该注解的⽅法中定义产⽣这个 bean, @Bean 告诉了Spring这是某个类的示例,当我需要⽤它的时候还给我。

  3. @Bea注解⽐@Component 注解的⾃定义性更强,⽽且很多地⽅我们只能通过@Bean注解来注册bean。⽐如当我们引⽤第三⽅库中的类需要装配到 Spring 容器时,则只能通过@Bean 来实现。

将⼀个类声明为Spring的 bean 的注解有哪些?

我们⼀般使⽤@Autowired注解⾃动装配 bean,要想把类标识成可⽤于@Autowired注解⾃动装配的 bean 的类,采⽤以下注解可实现: @Component :通⽤的注解,可标注任意类为Spring组件。如果⼀个Bean不知道属于哪个层,可以使⽤ @Component 注解标注。 @Repository : 对应持久层即 Dao 层,主要⽤于数据库相关操作。 @Service : 对应服务层,主要涉及⼀些复杂的逻辑,需要⽤到 Dao层。 @Controller : 对应 Spring MVC 控制层,主要⽤户接受⽤户请求并调⽤ Service 层返回数据给前端⻚⾯。

Spring 中的 bean ⽣命周期?

  1. Bean 容器找到配置⽂件中 Spring Bean 的定义。
  2. Bean 容器利⽤ Java Reflection API 创建⼀个Bean的实例。
  3. 如果涉及到⼀些属性值 利⽤ set() ⽅法设置⼀些属性值。
  4. 如果 Bean 实现了 BeanNameAware 接⼝,调⽤ setBeanName() ⽅法,传⼊Bean的名字。
  5. 如果 Bean 实现了 BeanClassLoaderAware 接⼝,调⽤ setBeanClassLoader() ⽅法, 传⼊ ClassLoader 对象的实例。
  6. 与上⾯的类似,如果实现了其他 .Aware 接⼝,就调⽤相应的⽅法。
  7. 如果有和加载这个 Bean 的 Spring 容器相关的对象,执⾏ postProcessBeforeInitialization() ⽅法
  8. 如果Bean实现了 InitializingBean 接⼝,执⾏ afterPropertiesSet() ⽅法。
  9. 如果 Bean 在配置⽂件中的定义包含 init-method 属性,执⾏指定的⽅法。
  10. 如果有和加载这个 Bean的 Spring 容器相关的对象,执⾏ postProcessAfterInitialization() ⽅法
  11. 当要销毁 Bean 的时候,如果 Bean 实现了 DisposableBean 接⼝,执⾏destory()⽅法。
  12. 当要销毁 Bean 的时候,如果 Bean 在配置⽂件中的定义包含 destroy-method 属性,执⾏指定的⽅法。

图示:

image-20210505000723856

与之⽐较类似的中⽂版本:

image-20210505000738738

Spring框架中都用到了哪些设计模式?

(1) 工厂模式:BeanFactory就是简单工厂模式的体现,用来创建对象的实例;

(2) 单例模式:Bean默认为单例模式。

(3) 代理模式:Spring的AOP功能用到了JDK的动态代理和CGLIB字节码生成技术;

(4) 模板方法:用来解决代码重复的问题。比如. RestTemplate, JmsTemplate, JpaTemplate。

(5) 观察者模式:定义对象键一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它 的对象都会得到通知被制动更新,如Spring中listener的实现--ApplicationListener。

(6) 使⽤到了适配器模式、spring MVC 中也是⽤到了适配器模式适配 Controller 。

Spring 管理事务的⽅式有⼏种?

  1. 编程式事务,在代码中硬编码。(不推荐使⽤)

  2. 声明式事务,在配置⽂件中配置(推荐使⽤)

声明式事务⼜分为两种:
  1. 基于XML的声明式事务

  2. 基于注解的声明式事务

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。

@Transactional(rollbackFor = Exception.class)注解了解吗?

我们知道:Exception分为运⾏时异常RuntimeException和⾮运⾏时异常。事务管理对于企业应⽤来说是⾄关重要的,即使出现异常情况,它也可以保证数据的⼀致性。

当 @Transactional 注解作⽤于类上时,该类的所有 public ⽅法将都具有该类型的事务属性,同时,我们也可以在⽅法级别使⽤该标注来覆盖类级别的定义。如果类或者⽅法加了这个注解,那么这个 类⾥⾯的⽅法抛出异常,就会回滚,数据库⾥⾯的数据也会回滚。

在 @Transactional 注解中如果不配置 rollbackFor 属性,那么事物只会在遇到 RuntimeException 的时候才会回滚,加上 rollbackFor=Exception.class ,可以让事物在遇到⾮运⾏时异常时也回滚。

如何使⽤JPA在数据库中⾮持久化⼀个字段?

Entity(name="USER") 
public class User {

    @Id
    @GeneratedValue(strategy  =  GenerationType.AUTO) 
    @Column(name = "ID")
    private Long id;

    @Column(name="USER_NAME") 
    private String userName;

    @Column(name="PASSWORD") 
    private  String  password;

    private String secrect;

}

如果我们想让 secrect 这个字段不被持久化,也就是不被数据库存储怎么办?我们可以采⽤下⾯⼏种⽅法:

static String transient1; // not persistent because of static
final String transient2 = “Satish”; // not persistent because of final transient String transient3; // not persistent because of transient @Transient
String transient4; // not persistent because of @Transient

⼀般使⽤后⾯两种⽅式⽐较多,我个⼈使⽤注解的⽅式⽐较多。

results matching ""

    No results matching ""