介绍下什么是Spring?
Spring是一个轻量级Java开发框架,最早有Rod Johnson创建,目的是为了解决企业级应用开发的业务逻辑层和其他各层的耦合问题。它是一个分层的JavaSE/JavaEE full-stack (一站式)轻量级开源框架,为开发Java应用程序提供全面的基础架构支持。Spring负责基础架构,因此Java开发者可以专注于应用程序的开发。
Spring最根本的使命是解决企业级应用开发的复杂性,即简化Java开发。
Spring可以做很多事情,它为企业级开发提供给了丰富的功能,但是这些功能的底层都依赖于它的两个核心特性,也就是依赖注入(dependency injection,DI) *和 *面向切面编程(aspect-oriented programming,AOP)。
为了降低Java开发的复杂性,Spring采取了以下4种关键策略·
基于POJO的轻量级和最小侵入性编程;
通过依赖注入和面向接口实现松耦合;
基于切面和惯例进行声明式编程;
通过切面和模板减少样板式代码。
Spring框架都有哪些特点?
轻量级
从大小与开销两方面而言Spring都是轻量的。完整的Spring框架可以在一个大小只有1M多的JAR文件里发布,并且Spring所需的处理开销也是微不足道的。
此外,Spring是非侵入式的∶典型的,Spring应用中的对象不依赖于Spring的特定类。
控制反转
Spring通过一种称作控制反转IOC的技术促进了低耦合。
当应用了IOC,一个对象依赖的其他对象会通过被动的方式传递进来,而不是这个对象自己创建或者查找依赖对象。
面向切面
Spring支持面向切面的编程,并且把应用业务逻辑和系统服务分开
容器
Spring包含并管理应用对象的配置和生命周期,在这个意义上它是一种容器,你可以配置你的每个bean如何被创建(基于一个可配置原型,你的bean可以创建一个单独的实例或者每次需要时都生成—个新的实例)以及它们是如何相互关联的。
框架集合
Spring可以将简单的组件配置、组合成为复杂的应用。
在Spring中,应用对象被声明式地组合,典型的是在一个XML文件里。
Spring也提供了很多基础功能(事务管理、持久化框架集成等),将应用逻辑的开发留给开发者。
Spring由哪些模块组成?(列举⼀些重要的Spring模块?)
Spring总共大约有20个模块,由1300多个不同的文件构成。而这些组件被分别整合在核心容器(Core container) 、AOP(Aspect oriented Programming)和设备支持(Instrmentation).数据访问与集成(Data Access/Integeration)、web、消息(Messaging)、Test等6个模块中。以下是Spring 4的模块结构图:
常用模块(重要模块):
Spring Core:提供了框架的基本组成部分,包括控制反转(Inversion of Control,IOC)和依赖注入(Dependency Injection,DI)功能。
Spring Beans:提供了BeanFactory,是工厂模式的一个经典实现,Spring将管理对象称为Bean。
Spring Context:构建于core封装包基础上的context封装包,提供了一种框架式的对象访问方法。
SpringJDBC:提供了一个JDBC的抽象层,消除了烦琐的JDBC编码和数据库厂商特有的错误代码解析,用于简化JDBC。
Spring ROM: Spring框架插入了若千个 ORM框架,从而提供了ORM的对象关系工具,其中包括JDO、 Hibernate和Batis SQL Map。所有这些都遭从Spring 的通用事务和DAO异棠层次结构。
SpringAOP:提供了面向切面的编程实现,让你可以自定义拦截器、切点等。
SpringWeb:提供了针对Web开发的集成特性,例如文件上传,利用servlet listeners进行ioc容器初始化和针对Web的ApplicationContext。
SpringTest:主要为测试提供支持的,支持使用JUnit或TestNG对Spring组件进行单元测试和集成测试。
Spring框架用到了哪些设计模式?
-
工厂模式: BeanFactory就是简单工厂模式的体现,用来创建对象的实例;
-
单例模式: Bean默认为单例模式。
-
代理模式: Spring的AOP功能用到了JDK的动态代理和CGLIB字节码生成技术;
-
适配器模式: Spring AOP 的增强或通知(Advice)使⽤到了适配器模式、spring MVC 中也是⽤到了适配器模式适配 Controller 。
-
模板方法: 用来解决代码重复的问题。比如.RestTemplate, JmsTemplate, JpaTemplate。
-
观察者模式:定义对象键一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都会得到通知被制动更新,如Spring中listener的实现-ApplicationListener。
-
包装器设计模式 : 我们的项⽬需要连接多个数据库,⽽且不同的客户在每次访问中根据需要会去访问不同的数据库。这种模式让我们可以根据客户的需求能够动态切换不同的数据源。
Spring IOC & AOP
什么是IOC容器?
IOC(Inversion of Control)即控制反转是一种设计思想,它把传统上由程序代码直接操控的对象的调用权交给容器,通过容器来实现对象组件的装配和管理。所谓的”控制反转”概念就是对组件对象控制权的转移,从程序代码本身转移到了外部容器。
Spring lOC负责创建对象,管理对象(通过依赖注入(DI),装配对象,配置对象,并且管理这些对象的整个生命周期。
IoC 容器实际上就是个Map(key, value) ,Map 中存放的是各种对象。
什么是依赖注入?
依赖注入:相对于loC而言,依赖注入(DI)更加准确地描述了loC的设计理念。所谓依赖注入
(Dependency Injection),即组件之间的依赖关系由容器在应用系统运行期来决定,也就是由容器动态地将某种依赖关系的目标对象实例注入到应用系统中的各个关联的组件之中。组件不做定位查询,只提供普通的Java方法让容器去决定依赖关系。总结:依赖于容器的注入对象
SpringBean的作用域?
Spring应用上下文中所有bean默认都是作为singleton(单例),不管bean被注入多少次都是同一个实例。
Spring的Bean 定义了 5 种作用域 : 分别为 singleton(单例)、prototype(原型)、
request、session 和 global session
Singleton:单例模式(多线程下不安全)
Spring IoC 容器中只会存在一个共享的 Bean 实例,无论有多少个
Bean 引用它,始终指向同一对象。该模式在多线程下是不安全的。Singleton 作用域是Spring 中的缺省作用域,也可以显示的将 Bean 定义为 singleton 模式,配置为:
|
<bean id="userDao" class="com.ioc.UserDaoImpl" scope="singleton"/>
|
Prototype:原型模式(每次使用时创建)
每次通过 Spring 容器获取 prototype 定义的 bean 时,容器都将创建一个新的 Bean 实例,每个 Bean 实例都有自己的属性和状态,而 singleton 全局只有一个对象。根据经验,对有状态的 bean 使用 prototype 作用域,而对无状态的 bean 使用 singleton作用域。
Request(一个request一个实例):
在一次 Http 请求中,容器会返回该 Bean 的同一实例。而对不同的 Http 请求则会产生新的 Bean,而且该 bean 仅在当前 Http Request 内有效,当前 Http 请求结束,该 bean实例也将会被销毁。
|
<bean id="loginAction" class="com.cnblogs.Login" scope="request"/>
|
Session:
在一次 Http Session 中,容器会返回该 Bean 的同一实例。而对不同的 Session 请
求则会创建新的实例,该 bean 实例仅在当前 Session 内有效。同 Http 请求相同,每一次session 请求创建新的实例,而不同的实例之间不共享属性,且实例仅在自己的 session 请求内有效,请求结束,则实例将被销毁。
|
<bean id="userPreference" class="com.ioc.UserPreference" scope="session"/>
|
Global Session
在一个全局的 Http Session 中,容器会返回该 Bean 的同一个实例,仅在
使用 portlet context 时有效。
注意:缺省的Spring bean的作用域是Singleton。使用prototype作用域需要慎重的思考,因为频繁创建和销毁bean 会带来很大的性能开销。
你怎么定义Bean(类)的作用域?
当定义一个在Spring里,我们还能给这个bean声明一个作用域。它可以通过bean定义中的scope属性来定义。如,当Spring要在需要的时候每次生产一个新的bean实例,bean的scope属性被指定为prototype。另一方面,一个bean每次使用的时候必须返回同一个实例,这个bean的scope属性必须设为singleton。
Spring 中的单例 bean 的线程安全问题了解吗?(为什么单例模式线程不安全?)
单例 bean 存在线程问题,主要是因为当多个线程操作同⼀个对象的时候,对这个对象的⾮静态成员变量的写操作会存在线程安全问题。
实际上大部分时候spring bean无状态的(比如dao类),所有某种程度上来说bean也是安全的,但如果bean 有状态的话(比如view model对象),那就要开发者自己去保证线程安全了,最简单的就是改变bean 的作用域,把"singleton"变更为"prototype”,这样请求bean相当于new Bean()了,所以就可以保证线程安全了。
·有状态就是有数据存储功能。·无状态就是不会保存数据。
常见的两种解决方式:
1. 在Bean对象中尽量避免定义可变的成员变量(不太现实)。
2. 在类中定义⼀个ThreadLocal成员变量,将需要的可变成员变量保存在 ThreadLocal 中(推荐的⼀种⽅式)。
Spring Bean 生命周期
实例化
- 实例化一个Bean,就是new 一个对象
IOC依赖注入
- 按照Spring上下文对实例化的Bean进行配置,也就是IOC注入。
setBeanName
- 如果 Bean 实现了 BeanNameAware 接⼝,调⽤ setBeanName() ⽅法,传⼊Bean的名字。
BeanFactoryAware 实现
- 如果Bean 实现了 BeanFactoryAware 接口,会调用它实现的 setBeanFactory()方法。
ApplicationContextAware 实现
- 如果Bean实现了ApplicationContextAware接口,会调用setApplicationContext(ApplicationContext)方法,传入 Spring 上下文(同样这个方式也可以实现步骤 4 的内容,但比 4 更好,因为 ApplicationContext 是 BeanFactory 的子接口,有更多的实现方法)
postProcessBeforelnitialization接口实现-初始化预处理
- 如果Bean关联了BeanPostProcessor接口,将会调用postProcessBeforeInitialization(Object obj, String s)方法,BeanPostProcessor 经常被用作是 Bean 内容的更改,并且由于这个是在 Bean 初始化结束时调用那个的方法,也可以被应用于内存或缓存技术。
init-method
- 如果 Bean 在 Spring 配置文件中配置了 init-method 属性会自动调用其配置的初始化方法。
postProcessAfterInitialization
- 如果Bean关联了BeanPostProcessor接口,将会调用postProcessAfterInitialization(Object obj, String s)方法。
注:以上工作完成以后就可以应用这个 Bean 了,那这个 Bean 是一个 Singleton 的,所以一般情况下我们调用同一个 id 的 Bean 会是在内容地址相同的实例,当然在 Spring 配置文件中也可以配置非 Singleton。
Destroy过期自动清理阶段
- 当要销毁 Bean 的时候,如果 Bean 实现了 DisposableBean 接⼝,执⾏ destroy() ⽅法
destroy-method自配置清理
- 当要销毁 Bean 的时候,如果 Bean 在配置⽂件中的定义包含 destroy-method 属性,执⾏指定的⽅法。
BeanFactory和FactoryBean的区别?
BeanFactory:是一个工厂,IOC的顶级接口(其实是构建了一个spring上下文的环境,容器),用来管理和获取Bean对象。
FactoryBean:是一个Bean生成工具,是用来获取一种类型对象的Bean,它是构造Bean实例的一种方式。用户可以通过实现该接口定制实例化Bean的逻辑。
Spring 依赖注入的方式?
4种:构造器注入、setter方法注入、静态工厂注入、实例工厂注入
构造器依赖注入:构造器依赖注入通过容器触发一个类的构造器来实现的,该类有一系列参数,每个参数代表一个对其他类的依赖。
Setter方法注入: Setter方法注入是容器通过调用无参构造器或无参static工厂方法实例化bean之后,调用该bean的setter方法,即实现了基于setter的依赖注入。
**静态工厂注入: **通过调用静态工厂的方法来获取自己需要的对象,为了让 spring 管理所有对象,我们不能直接通过"工程类.静态方法()"来获取对象,而是依然通过 spring 注入的形式获取。
实例工厂注入: 获取对象实例的方法不是静态的,所以你需要首先 new 工厂类,再调用普通的实例方法。
最常用的是构造器和setter方式的注入。
两者区别:
两种依赖方式都可以使用,构造器注入和Setter方法注入。最好的解决方案是用构造器参数实现强制依赖,setter方法实现可选依赖。
什么是Bean的装配?
装配,或bean装配是指在Spring容器中把bean组装到一起,前提是容器需要知道bean的依赖关系,如何通过依赖注入来把它们装配到一起。
什么是bean的自动装配?
在Spring框架中,在配置文件中设定bean的依赖关系是一个很好的机制,Spring容器能够自动装配相互合作的bean,这意味着容器不需要和配置,能通过Bean工厂自动处理bean之间的协作。这意味着Spring可以通过向Bean Factory中注入的方式自动搞定bean之间的依赖关系。自动装配可以设置在每个bean上,也可以设定在特定的bean上。
解释不同方式的自动装配,spring自动装配bean有哪些方式?
在spring中,对象无需自己查找或创建与其关联的其他对象,由容器负责把需要相互协作的对象引用赋予各个对象,使用autowire来配置自动装载模式。
在Spring框架xml配置中共有5种自动装配:
no: 默认的方式是不进行自动装配的,通过手工设置ref属性来进行装配bean。
byName:通过bean的名称进行自动装配,如果一个bean的property与另一bean的name相同,就进行自动装配。
byType:通过参数的数据类型进行自动装配。
constructor:利用构造函数进行装配,并且构造函数的参数通过byType进行装配。
autodetect:自动探测,如果有构造方法,通过construct的方式自动装配,否则使用byType的方式自动装配。
使用@Autowired注解自动装配的过程是怎样的?
使用@Autowired注解来自动装配指定的bean。
在使用@Autowired注解之前需要在Spring配置文件进行配置,<context:annotation-config />。
在启动spring loC时,容器自动装载了一个AutowiredAnnotationBeanPostProcessor后置处理器,当容器扫描到@Autowied、@Resource或@Inject时,就会在IOC容器自动查找需要的bean,并装配给该对象的属性。在使用@Autowired时,首先在容器中查询对应类型的bean:
如果查询结果刚好为一个,就将该bean装配给@Autowired指定的数据;·
如果查询的结果不止一个,那么@Autowired会根据名称来查找;
如果上述查找的结果为空,那么会抛出异常。解决方法时,使用required=false。
将⼀个类声明为Spring的 bean 的注解有哪些?
@Component :通⽤的注解,可标注任意类为 Spring 组件。如果⼀个Bean不知道属于哪个层,可以使⽤ @Component 注解标注。
@Repository : 对应持久层即 Dao 层,主要⽤于数据库相关操作。
@Service : 对应服务层,主要涉及⼀些复杂的逻辑,需要⽤到 Dao层。
@Controller : 对应 Spring MVC 控制层,主要⽤户接受⽤户请求并调⽤ Service 层返回数
据给前端⻚⾯。
@Component 和 @Bean 的区别是什么?
-
作⽤对象不同: @Component 注解作⽤于类,⽽ @Bean 注解作⽤于⽅法。
-
@Component 通常是通过类路径扫描来⾃动侦测以及⾃动装配到Spring容器中(我们可以使⽤ @ComponentScan 注解定义要扫描的路径从中找出标识了需要装配的类⾃动装配到Spring 的 bean 容器中)。
@Bean 注解通常是我们在标有该注解的⽅法中定义产⽣这个bean, @Bean 告诉了Spring这是某个类的示例,当我需要⽤它的时候还给我。
- @Bean 注解⽐ Component 注解的⾃定义性更强,⽽且很多地⽅我们只能通过 @Bean 注解来注册bean。⽐如当我们引⽤第三⽅库中的类需要装配到 Spring 容器时,则只能通过
@Bean 来实现。
@component, @Controller,@Repository,@Service有何区别?
@Component:这将java类标记为bean。它是任何Spring管理组件的通用构造型。spring的组件扫描机制现在可以将其拾取并将其拉入应用程序环境中。
@Controller:这将一个类标记为Spring Web MVC控制器。标有它的Bean 会自动导入到IOC容器中。
@Service:此注解是组件注解的特化。它不会对@Component注解提供任何其他行为。您可以在服务层类中使用@Service而不是@Component,因为它以更好的方式指定了意图。
@Repository:这个注解是具有类似用途和功能的@Component注解的特化。它为DAO提供了额外的好处。它将DAO导入IOC容器,并使未经检查的异常有资格转换为Spring DataAccessException。
@Autowired注解有什么作用
@Autowired默认是按照类型装配注入的,默认情况下它要求依赖对象必须存在(可以设置它required属性为false)。@Autowired 注解提供了更细粒度的控制,包括在何处以及如何完成自动装配。它的用法和@Required一样,修饰setter方法、构造器、属性或者具有任意名称和/或多个参数的PN方法。
@Autowired和@Resource之间的区别
@Autowired可用于: 构造函数、成员变量、Setter方法
@Autowired和@Resource之间的区别
相同点:
1,@Autowired和@Resource功能相同,都可以用来装配bean;
2,两个注解可以加载属性字段或写在setter方法上;
不同点:
1,提供方不同:@Autowired是Spring框架提供,@Resource是Java自带,jdk1.6版本开始支持;
2,装配方式不同:
@Autowired默认按照byType装配;
@Resource默认按照byName装配,如果匹配不到,则继续使用byType装配;
@Qualifier注解有什么作用
当您创建多个相同类型的bean并希望仅使用属性装配其中一个bean时,您可以使用@Qualifier注解和@Autowired通过指定应该装配哪个确切的bean来消除歧义。
@RequestMapping 注解有什么用?
@RequestMapping注解用于将特定HTTP请求方法映射到将处理相应请求的控制器中的特定类/方法。此注释可应用于两个级别:
类级别:注解写在类上,映射请求的URL
方法级别:注解写在对应的方法上,映射URL以及HTTP请求方法
@RestController vs @Controller
Controller 返回⼀个⻚⾯
单独使⽤ @Controller 不加 @ResponseBody 的话⼀般使⽤在要返回⼀个视图的情况,这种情况属于⽐传统的Spring MVC 的应⽤,对应于前后端不分离的情况。
@RestController 返回JSON 或 XML 形式数据
但 @RestController 只返回对象,对象数据直接以 JSON 或 XML 形式写⼊ HTTP 响应(Response)中,这种情况属于 RESTful Web服务,这也是⽬前⽇常开发所接触的最常⽤的情况(前后端分离)。
@Controller +@ResponseBody 返回JSON 或 XML 形式数据
如果你需要在Spring4之前开发 RESTful Web服务的话,你需要使⽤ @Controller 并结合 @ResponseBody 注解,也就是说 @Controller + @ResponseBody = @RestController (Spring 4之后新加的注解)。
注意部分解释下什么是AOP?你的理解是什么?
AOP(Aspect-Oriented Programming:⾯向切⾯编程)能够将那些与业务⽆关,却为业务模块所共同调⽤的逻辑或责任(例如事务处理、⽇志管理、权限控制等)封装起来,便于减少系统的重复代码,降低模块间的耦合度,并有利于未来的可拓展性和可维护性。
Spring AOP就是基于动态代理的,如果要代理的对象,实现了某个接⼝,那么Spring AOP会使⽤JDK Proxy,去创建代理对象,⽽对于没有实现接⼝的对象,就⽆法使⽤ JDK Proxy 去进⾏代理了,这时候Spring AOP会使⽤Cglib ,这时候Spring AOP会使⽤ Cglib ⽣成⼀个被代理对象的⼦类来作为代理,使⽤ AOP 之后我们可以把⼀些通⽤功能抽象出来,在需要⽤到的地⽅直接使⽤即可,这样⼤⼤简化了代码量。我们需要增加新功能时也⽅便,这样也提⾼了系统扩展性。⽇志功能、事务管理等等场景都⽤到了 AOP 。
JDK动态代理和CGLIB动态代理的区别
Spring AOP中的动态代理主要有两种方式,JDK动态代理和CGLIB动态代理:
JDK动态代理只提供接口的代理,不支持类的代理。核心InvocationHandler接口和Proxy类,lnvocationHandler通过invoke()方法反射来调用目标类中的代码,动态地将横切逻辑和业务编织在一起;接着,Proxy利用InvocationHandler动态创建一个符合某一接口的的实例,生成目标类的代理对象。
如果代理类没有实现InvocationHandler接口,那么Spring AOP会选择使用CGLIB来动态代理目标类。CGLIB (Code Generation Library),是一个代码生成的类库,可以在运行时动态的生成指定类的一个子类对象,并覆盖其中特定方法并添加增强代码,从而实现AOP。CGLIB是通过继承的方式做的动态代理,因此如果某个类被标记为final,那么它是无法使用CGLIB做动态代理的。
静态代理与动态代理区别在于生成AOP代理对象的时机不同,相对来说AspectJ的静态代理方式具有更好的性能,但是AspectJ需要特定的编译器进行处理,而Spring AOP则无需特定的编译器处理。
说明图片解释下SpringAOP里面的几个名词
切面(Aspect)
切面是通知和切点的结合。通知和切点共同定义了切面的全部内容。在SpringAOP中,切面可以使用通用类(基于模式的风格)或者在普通类中以@AspectJ注解来实现。
连接点(Join point)
指方法,在Spring AOP中,一个连接点总是代表一个方法的执行。应用可能有数以千计的时机应用通知。这些时机被称为连接点。连接点是在应用执行过程中能够插入切面的一个点。这个点可以是调用方法时、抛出异常时、甚至修改一个字段时。切面代码可以利用这些点插入到应用的正常流程之中,并添加新的行为。
通知(Advice)
在AOP术语中,切面的工作被称为通知。
切入点(Pointcut)
切点的定义会匹配通知所要织入的一个或多个连接点。我们通常使用明确的类和方法名称,或是利用正则表达式定义所匹配的类和方法名称来指定这些切点。
引入(lntroduction)
引入允许我们向现有类添加新方法或属性。
目标对象(Target Object)
被一个或者多个切面(aspect)所通知(advise)的对象。它通常是一个代理对象。也有人把它叫做被通知(adviced)对象。既然Spring AOP是通过运行时代理实现的,这个对象永远是一个被代理(proxied)对象。
织入(Weaving)
织入是把切面应用到目标对象并创建新的代理对象的过程。在目标对象的生命周期里有多少个点可以进行织入:
编译期:切面在目标类编译时被织入。AspectJ的织入编译器是以这种方式织入切面的。
类加载期:切面在目标类加载到VM时被织入。需要特殊的类加载器,它可以在目标类被引入应用之前增强该目标类的字节码。AspectJ5的加载时织入就支持以这种方式织入切面。
运行期:切面在应用运行的某个时刻被织入。一般情况下,在织入切面时,AOP容器会为目标对象动态地创建一个代理对象。SpringAOP就是以这种方式织入切面。
spring通知有哪些类型?
在AOP术语中,切面的工作被称为通知,实际上是程序执行时要通过SpringAOP框架触发的代码段。Spring切面可以应用5种类型的通知:
-
前置通知(Before) : 在目标方法被调用之前调用通知功能;
-
后置通知(After)∶在目标方法完成之后调用通知,此时不会关心方法的输出是什么;
-
返回通知(After-returning ) : 在目标方法成功执行之后调用通知;
-
异常通知(After-throwing): 在目标方法抛出异常后调用通知;
-
环绕通知(Around): 通知包裹了被通知的方法,在被通知的方法调用之前和调用之后执行自定义的行为。
什么是切面Aspect?
aspect由pointcount和advice组成,切面是通知和切点的结合。
它既包含了横切逻辑的定义,也包括了连接点的定义.
Spring AOP就是负责实施切面的框架,它将切面所定义的横切逻辑编织到切面所指定的连接点中.
AOP的工作重心在于如何将增强编织目标对象的连接点上,这里包含两个工作:
如何通过pointcut和advice定位到特定的joinpoint 上
如何在advice中编写切面代码.
可以简单地认为,使用@Aspect注解的类就是切面.
切面示意图Spring支持的事务管理类型,spring事务实现方式有哪些?
Spring支持两种类型的事务管理:
编程式事务管理:通过编程的方式管理事务,给你带来极大的灵活性,但是难维护。
编程式事务管理通过TransactionTemplate手动管理事务,硬编码方式(不推荐使用)
声明式事务管理:你可以将业务代码和事务管理分离,你只需用注解和XML配置来管理事务。(在配置文件中配置,推荐使用)
声明式事务管理有三种实现方式:
基于TransactionProxyFactoryBean的方式。
基于AspectJ的XML方式。
基于注解的方式大部分。service类名上或方法上添加@Transactional 注解
你更倾向用那种事务管理类型?
我会选择声明式事务管理,因为它对应用代码的影响最小,因此更符合一个无侵入的轻量级容器的思想。声明式事务管理要优于编程式事务管理,虽然比编程式事务管理(这种方式允许你通过代码控制事务)少了一点灵活性。唯一不足地方是,最细粒度只能作用到方法级别,无法做到像编程式事务那样可以作用到代码块级别。
Spring事务的实现方式和实现原理
Spring事务的本质其实就是数据库对事务的支持,没有数据库的事务支持,spring是无法提供事务功能的。真正的数据库层的事务提交和回滚是通过binlog或者redo log实现的。
说一下Spring的事务传播行为
spring事务的传播行为说的是,当多个事务同时存在的时候,spring如何处理这些事务的行为。一共七种传播行为:
PROPAGATION_REQUIRED(required):如果当前没有事务,就创建一个新事务,如果当前存在事务,就加入该事务,该设置是最常用的设置。
PROPAGATION_SUPPORTS(supports): 支持当前事务,如果当前存在事务,就加入该事务,如果当前不存在事务,就以非事务执行。
PROPAGATION_MANDATORY(mandatory):支持当前事务,如果当前存在事务,就加入该事务,如果当前不存在事务,就抛出异常。
PROPAGATION_REQUIRES_NEW(required_new):创建新事务,无论当前存不存在事务,都创建新事务。
PROPAGATION_NOT_SUPPORTED(not_supported): 以非事务方式执行操作,如果当前存在事务,就把当前事务挂起。
PROPAGATION_NEVER(never):以非事务方式执行,如果当前存在事务,则抛出异常。
PROPAGATION_NESTED(nested): 如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,则按REQUIRED属性执行。
说一下Spring的事务隔离级别?
spring有五大隔离级别,默认值为ISOLATION_DEFAULT(使用数据库的设置),其他四个隔离级别和数据库的隔离级别一致:
-
lSOLATION_DEFAULT(default): 用底层数据库的设置隔离级别,数据库设置的是什么我就用什么;
-
ISOLATION_READ_UNCOMMITTED(read_uncommitted): 未提交读,最低隔离级别、事务未提交前,就可被其他事务读取(会出现幻读、脏读、不可重复读);
-
ISOLATION_READ_COMMITTED(read_committed): 提交读,一个事务提交后才能被其他事务读取到(会造成幻读、不可重复读),SQL server的默认级别;
-
ISOLATION_REPEATABLE_READ(repeatable_read): 可重复读,保证多次读取同一个数据时,其值都和事务开始时候的内容是一致,禁止读取到别的事务未提交的数据(会造成幻读),MySQL的默认级别;
-
ISOLATION_SERIALIZABLE(serializable):序列化,代价最高最可靠的隔离级别,该隔离级别能防止脏读、不可重复读、幻读。
什么是脏读、幻读、不可重复读?
脏读∶表示一个事务能够读取另一个事务中还未提交的数据。比如,某个事务尝试插入记录A,此时该事务还未提交,然后另一个事务尝试读取到了记录A。
不可重复读︰是指在一个事务内,多次读同一数据。
幻读:指同一个事务内多次查询返回的结果集不一样。比如同一个事务A第一次查询时候有n条记录,但是第二次同等条件下查询却有n+1条记录,这就好像产生了幻觉。发生幻读的原因也是另外一个事务新增或者删除或者修改了第一个事务结果集里面的数据,同一个记录的数据内容被修改了,所有数据行的记录就变多或者变少了。
@Transactional(rollbackFor = Exception.class)注解了解吗?
Exception分为运⾏时异常RuntimeException和⾮运⾏时异常。事务管理对于企业应
⽤来说是⾄关重要的,即使出现异常情况,它也可以保证数据的⼀致性。
当 @Transactional 注解作⽤于类上时,该类的所有 public ⽅法将都具有该类型的事务属性,同时,我们也可以在⽅法级别使⽤该标注来覆盖类级别的定义。如果类或者⽅法加了这个注解,那么这个类⾥⾯的⽅法抛出异常,就会回滚,数据库⾥⾯的数据也会回滚。
在 @Transactional 注解中如果不配置 rollbackFor 属性,那么事物只会在遇到 RuntimeException 的时候才会回滚,加上 rollbackFor=Exception.class ,可以让事物在遇到⾮运⾏时异常时也回滚。
网友评论