如下面试题,基于网络整理,和本身编辑。具体参考的文章,会在文末给出全部的连接。php
若是胖友有本身的疑问,欢迎在星球提问,咱们一块儿整理吊吊的 Spring 面试题的大保健。html
而题目的难度,艿艿尽可能按照从容易到困难的顺序,逐步下去。前端
注意 1 ,这个面试题,暂时不包括 Spring MVC 部分。主要以 Spring IoC、Spring AOP、Spring Transaction 为主。java
注意 2 ,T T Spring 怎么会有这么多问题,艿艿本身面试不少都不太问,跟背书同样。因此整理的过程,真的是痛苦万分。react
Spring 是一个很庞大的技术体系,能够说包容一切,因此本文咱们按照下面的顺序,罗列各类面试题:git
Spring 是一个开源应用框架,旨在下降应用程序开发的复杂度。github
它是轻量级、松散耦合的。web
它的轻量级主要是相对于 EJB 。随着 Spring 的体系愈来愈庞大,你们被 Spring 的配置搞懵逼了,因此后来出了 Spring Boot 。面试
它具备分层体系结构,容许用户选择组件,同时还为 J2EE 应用程序开发提供了一个有凝聚力的框架。spring
以下是一张比较早期版本的 Spring Framework 的模块图:
艿艿:由于它的配色比较好看,哈哈哈哈。因此,没本身画一个最新的。
咱们按照一个一个分块来讲。
Spring 核心容器
对应图中,Core Container 。
该层基本上是 Spring Framework 的核心。它包含如下模块:
Spring Bean
核心容器提供 Spring 框架的基本功能。核心容器的主要组件是 BeanFactory,它是工厂模式的实现。BeanFactory 使用控制反转 (IOC)模式将应用程序的配置和依赖性规范与实际的应用程序代码分开。
Spring Context
Spring 上下文是一个配置文件,向 Spring 框架提供上下文信息。Spring 上下文包括企业服务,例如 JNDI、EJB、电子邮件、国际化、事件机制、校验和调度功能。
SpEL (Spring Expression Language)
Spring 表达式语言全称为 “Spring Expression Language”,缩写为 “SpEL” ,相似于 Struts2 中使用的 OGNL 表达式语言,能在运行时构建复杂表达式、存取对象图属性、对象方法调用等等,而且能与 Spring 功能完美整合,如能用来配置 Bean 定义。
或者说,这块就是 Spring IoC 。
数据访问
对应图中,Data Access 。
该层提供与数据库交互的支持。它包含如下模块:
JDBC (Java DataBase Connectivity)
Spring 对 JDBC 的封装模块,提供了对关系数据库的访问。
ORM (Object Relational Mapping)
Spring ORM 模块,提供了对 hibernate5 和 JPA 的集成。
- hibernate5 是一个 ORM 框架。
- JPA 是一个 Java 持久化 API 。
OXM (Object XML Mappers)
Spring 提供了一套相似 ORM 的映射机制,用来将 Java 对象和 XML 文件进行映射。这就是 Spring 的对象 XML 映射功能,有时候也成为 XML 的序列化和反序列化。
用的比较少,胖友了解下便可。
Transaction
Spring 简单而强大的事务管理功能,包括声明式事务和编程式事务。
Web
该层提供了建立 Web 应用程序的支持。它包含如下模块:
WebMVC
MVC 框架是一个全功能的构建 Web 应用程序的 MVC 实现。经过策略接口,MVC 框架变成为高度可配置的,MVC 容纳了大量视图技术,其中包括 JSP、Velocity、Tiles、iText 和 POI。
WebFlux
基于 Reactive 库的响应式的 Web 开发框架
不了解的胖友,能够看看 《使用 Spring 5 的 WebFlux 开发反应式 Web 应用》
WebSocket
Spring 4.0 的一个最大更新是增长了对 Websocket 的支持。
Websocket 提供了一个在 Web 应用中实现高效、双向通信,需考虑客户端(浏览器)和服务端之间高频和低延时消息交换的机制。
通常的应用场景有:在线交易、网页聊天、游戏、协做、数据可视化等。
Portlet 已经废弃
AOP
该层支持面向切面编程。它包含如下模块:
AOP
经过配置管理特性,Spring AOP 模块直接将面向方面的编程功能集成到了 Spring 框架中。因此,能够很容易地使 Spring 框架管理的任何对象支持 AOP。
Spring AOP 模块为基于 Spring 的应用程序中的对象提供了事务管理服务。经过使用 Spring AOP,不用依赖 EJB 组件,就能够将声明性事务管理集成到应用程序中。
Aspects
该模块为与 AspectJ 的集成提供支持。
Instrumentation
该层为类检测和类加载器实现提供支持。
用的比较少,胖友了解下便可。
其它
JMS (Java Messaging Service)
提供了一个 JMS 集成框架,简化了 JMS API 的使用。
可能有胖友不太了解 JMS ,能够看看 《JMS(Java消息服务)入门教程》 。
Test
该模块为使用 JUnit 和 TestNG 进行测试提供支持。
Messaging
该模块为 STOMP 提供支持。它还支持注解编程模型,该模型用于从 WebSocket 客户端路由和处理 STOMP 消息。
下面列举了一些使用 Spring 框架带来的主要好处:
艿艿:注意,下文中加粗的内容,这是本句话的关键。
固然,Spring 代码优势的同时,必定会带来相应的缺点:
把不少 JavaEE 的东西封装了,在知足快速开发高质量程序的同时,隐藏了实现细节。
这就致使,不少工程师,离开了 Spring 以后,就不知道怎么工做了。从 Java 工程师,变成了 Spring 工程师。对于有追求的咱们,仍是须要知其然,知其因此然。
Spring 框架中使用到了大量的设计模式,下面列举了比较有表明性的:
固然,感兴趣的胖友,以为不过瘾,能够看看艿艿基友知秋写的几篇文章:
下面,咱们会将分红 IoC 和 Bean 两部分来分享 Spring 容器的内容。
- IoC ,侧重在于容器。
- Bean ,侧重在于被容器管理的 Bean 。
注意,正确的拼写是 IoC 。
Spring 框架的核心是 Spring IoC 容器。容器建立 Bean 对象,将它们装配在一块儿,配置它们并管理它们的完整生命周期。
艿艿,注意上面三段段话的加粗部分的内容。
在依赖注入中,你没必要主动、手动建立对象,但必须描述如何建立它们。
另外,依赖注入的英文缩写是 Dependency Injection ,简称 DI 。
艿艿的吐槽,最怕这种概念题。下面引用知乎上的一个讨论:《IoC 和DI 有什么区别?》
IoC 是个更宽泛的概念,DI 是更具体的。引用郑烨的一篇博客,引用郑烨的一篇博客,我眼中的Spring
Dependency Injection
原来,它叫 IoC 。
Martin Flower 发话了,是个框架都有 IoC ,这不足以新生容器反转的“如何定位插件的具体实现”,因而,它有了个新名字,Dependency Injection 。其实,它就是一种将调用者与被调用者分离的思想,Uncle Bob 管它叫DIP(Dependency Inversion Principle),并把它纳入OO设计原则。
同 Spring 相比,它更早进入个人大脑。一切都是那么朦胧,直至 Spring 出现。慢慢的,我知道了它还分为三种:
- Interface Injection(type 1)
- Setter Injection(type 2)
- Constructor Injection(type 3)。
Martin Flower那篇为它改名的大做让我心目关于它的一切趋于完整。
在 Spring 中,它是一切的基础。Spring 的种种优点随之而来。
于我而言,它为我带来更多的是思惟方式的转变,恐怕之后我再也没法写出那种一大块的全功能程序了。
一般,依赖注入能够经过三种方式完成,即:
上面一个问题的三种方式的英文,下面是三种方式的中文。
目前,在 Spring Framework 中,仅使用构造函数和 setter 注入这两种方式。
那么这两种方式各有什么优缺点呢?胖友能够简单阅读 《Spring两种依赖注入方式的比较》,不用太较真。综述来讲:
构造函数注入 | setter 注入 |
---|---|
没有部分注入 | 有部分注入 |
不会覆盖 setter 属性 | 会覆盖 setter 属性 |
任意修改都会建立一个新实例 | 任意修改不会建立一个新实例 |
适用于设置不少属性 | 适用于设置少许属性 |
Spring 提供了两种( 不是“个” ) IoC 容器,分别是 BeanFactory、ApplicationContext 。
BeanFactory
BeanFactory 在
spring-beans
项目提供。
BeanFactory ,就像一个包含 Bean 集合的工厂类。它会在客户端要求时实例化 Bean 对象。
ApplicationContext
ApplicationContext 在
spring-context
项目提供。
ApplicationContext 接口扩展了 BeanFactory 接口,它在 BeanFactory 基础上提供了一些额外的功能。内置以下功能:
另外,ApplicationContext 会自动初始化非懒加载的 Bean 对象们。
详细的内容,感兴趣的胖友,能够看看 《【死磕 Spring】—— ApplicationContext 相关接口架构分析》 一文。源码以前无秘密。简单总结下 BeanFactory 与 ApplicationContext 二者的差别:
艿艿:可能不少胖友没看过源码,因此会比较难。
BeanFactory | ApplicationContext |
---|---|
它使用懒加载 | 它使用即时加载 |
它使用语法显式提供资源对象 | 它本身建立和管理资源对象 |
不支持国际化 | 支持国际化 |
不支持基于依赖的注解 | 支持基于依赖的注解 |
另外,BeanFactory 也被称为低级容器,而 ApplicationContext 被称为高级容器。
BeanFactory 最经常使用的是 XmlBeanFactory 。它能够根据 XML 文件中定义的内容,建立相应的 Bean。
如下是三种较常见的 ApplicationContext 实现方式:
一、ClassPathXmlApplicationContext :从 ClassPath 的 XML 配置文件中读取上下文,并生成上下文定义。应用程序上下文从程序环境变量中取得。示例代码以下:
ApplicationContext context = new ClassPathXmlApplicationContext(“bean.xml”); |
二、FileSystemXmlApplicationContext :由文件系统中的XML配置文件读取上下文。示例代码以下:
ApplicationContext context = new FileSystemXmlApplicationContext(“bean.xml”); |
三、XmlWebApplicationContext :由 Web 应用的XML文件读取上下文。例如咱们在 Spring MVC 使用的状况。
固然,目前咱们更多的是使用 Spring Boot 为主,因此使用的是第四种 ApplicationContext 容器,ConfigServletWebServerApplicationContext 。
简单来讲,Spring 中的 IoC 的实现原理,就是工厂模式加反射机制。代码以下:
interface Fruit { |
className
对应的 Fruit 对象。在基友 《面试问烂的 Spring IoC 过程》 的文章中,把 Spring IoC 相关的内容,讲的很是不错。
Spring 的 ApplicationContext 提供了支持事件和代码中监听器的功能。
咱们能够建立 Bean 用来监听在 ApplicationContext 中发布的事件。若是一个 Bean 实现了 ApplicationListener 接口,当一个ApplicationEvent 被发布之后,Bean 会自动被通知。示例代码以下:
public class AllApplicationEventListener implements ApplicationListener<ApplicationEvent> { |
Spring 提供了如下五种标准的事件:
#refresh()
方法时被触发。#start()
方法开始/从新开始容器时触发该事件。#stop()
方法中止容器时触发该事件。除了上面介绍的事件之外,还能够经过扩展 ApplicationEvent 类来开发自定义的事件。
① 示例自定义的事件的类,代码以下:
public class CustomApplicationEvent extends ApplicationEvent{ |
② 为了监听这个事件,还须要建立一个监听器。示例代码以下:
public class CustomEventListener implements ApplicationListener<CustomApplicationEvent> { |
③ 以后经过 ApplicationContext 接口的 #publishEvent(Object event)
方法,来发布自定义事件。示例代码以下:
// 建立 CustomApplicationEvent 事件 |
这个问题,胖友能够在回过头看 「什么是 Spring IoC 容器?」 问题,相互对照。
单纯从 Spring Framework 提供的方式,一共有三种:
一、XML 配置文件。
Bean 所需的依赖项和服务在 XML 格式的配置文件中指定。这些配置文件一般包含许多 bean 定义和特定于应用程序的配置选项。它们一般以 bean 标签开头。例如:
<bean id="studentBean" class="org.edureka.firstSpring.StudentBean"> |
二、注解配置。
您能够经过在相关的类,方法或字段声明上使用注解,将 Bean 配置为组件类自己,而不是使用 XML 来描述 Bean 装配。默认状况下,Spring 容器中未打开注解装配。所以,您须要在使用它以前在 Spring 配置文件中启用它。例如:
<beans> |
三、Java Config 配置。
Spring 的 Java 配置是经过使用 @Bean 和 @Configuration 来实现。
@Bean
注解扮演与 <bean />
元素相同的角色。@Configuration
类容许经过简单地调用同一个类中的其余 @Bean
方法来定义 Bean 间依赖关系。例如:
|
目前主要使用 Java Config 配置为主。固然,三种配置方式是能够混合使用的。例如说:
@RequestMapping
注解。另外,如今已是 Spring Boot 的天下,因此更加是 Java Config 配置为主。
艿艿,这个是一个比较小众的题目,简单了解便可。
Spring Bean 支持 5 种 Scope ,分别以下:
另外,网络上不少文章说有 Global-session 级别,它是 Portlet 模块独有,目前已经废弃,在 Spring5 中是找不到的。
仅当用户使用支持 Web 的 ApplicationContext 时,最后三个才可用。
再补充一点,开发者是能够自定义 Bean Scope ,具体可参见 《Spring(10)—— Bean 做用范围(二)—— 自定义 Scope》 。
不错呢,仍是那句话,这个题目简单了解下便可,实际经常使用的只有 Singleton 和 Prototype 两种级别,甚至说,只有 Singleton 级别。😈
艿艿说:这是一个比较高级的 Spring 的面试题,很是常见,而且答对比较加分。固然,若是实际真正弄懂,须要对 Spring Bean 的源码,有比较好的理解,因此 《精尽 Spring 源码》 系列,该读仍是读吧。
艿艿:要注意下面每段话,艿艿进行加粗的地方。
Spring Bean 的初始化流程以下:
实例化 Bean 对象
Spring 容器根据配置中的 Bean Definition(定义)中实例化 Bean 对象。
Bean Definition 能够经过 XML,Java 注解或 Java Config 代码提供。
Spring 使用依赖注入填充全部属性,如 Bean 中所定义的配置。
#setBeanName(String name)
方法。#setBeanFactory(BeanFactory beanFactory)
方法。#preProcessBeforeInitialization(Object bean, String beanName)
方法。#afterPropertiesSet()
方法。<bean />
的 init-method
属性),那么将调用该方法。#postProcessAfterInitialization(Object bean, String beanName)
方法。Spring Bean 的销毁流程以下:
#destroy()
方法。<bean />
的 destroy-method
属性),那么将调用该方法。总体以下图:
无心中,艿艿又翻到一张有趣的总体图,以下图:
只有将 Bean 仅用做另外一个 Bean 的属性时,才能将 Bean 声明为内部 Bean。
<property>
或 <constructor-arg>
中提供了 <bean>
元素的使用。例如,假设咱们有一个 Student 类,其中引用了 Person 类。这里咱们将只建立一个 Person 类实例并在 Student 中使用它。示例代码以下:
// Student.java |
<!-- bean.xml --> |
当 Bean 在 Spring 容器中组合在一块儿时,它被称为装配或 Bean 装配。Spring 容器须要知道须要什么 Bean 以及容器应该如何使用依赖注入来将 Bean 绑定在一块儿,同时装配 Bean 。
装配,和上文提到的 DI 依赖注入,实际是一个东西。
自动装配有哪些方式?
Spring 容器可以自动装配 Bean 。也就是说,能够经过检查 BeanFactory 的内容让 Spring 自动解析 Bean 的协做者。
自动装配的不一样模式:
自动装配有什么局限?
艿艿:这个题目,了解下便可,也不是很准确。
<constructor-arg>
和 <property>
设置指定依赖项,这将覆盖自动装配。基本元数据类型 - 简单属性(如原数据类型,字符串和类)没法自动装配。
这种,严格来讲,也不能称为局限。由于能够经过配置文件来解决。
使人困惑的性质 - 老是喜欢使用明确的装配,由于自动装配不太精确。
默认状况下,容器启动以后会将全部做用域为单例的 Bean 都建立好,可是有的业务场景咱们并不须要它提早都建立好。此时,咱们能够在Bean 中设置 lzay-init = "true"
。
Spring 框架并无对单例 Bean 进行任何多线程的封装处理。
固然,但实际上,大部分的 Spring Bean 并无可变的状态(好比Serview 类和 DAO 类),因此在某种程度上说 Spring 的单例 Bean 是线程安全的。
若是你的 Bean 有多种状态的话,就须要自行保证线程安全。最浅显的解决办法,就是将多态 Bean 的做用域( Scope )由 Singleton 变动为 Prototype 。
艿艿说:能回答出这个问题的,通常是比较厉害的。
这是个比较复杂的问题,有能力的胖友,建议看下 《【死磕 Spring】—— IoC 之加载 Bean:建立 Bean(五)之循环依赖处理》
感受,不经过源码,很难解释清楚这个问题。若是看不懂的胖友,能够在认真看完,在星球里,咱们一块儿多交流下。好玩的。
这块内容,实际写在 「Spring Bean」 中比较合适,考虑到后续的问题,都是关于注解的,因此单独起一个大的章节。
不使用 XML 来描述 Bean 装配,开发人员经过在相关的类,方法或字段声明上使用注解将配置移动到组件类自己。它能够做为 XML 设置的替代方案。例如:
Spring 的 Java 配置是经过使用 @Bean
和 @Configuration
来实现。
@Bean
注解,扮演与 <bean />
元素相同的角色。@Configuration
注解的类,容许经过简单地调用同一个类中的其余 @Bean
方法来定义 Bean 间依赖关系。示例以下:
|
默认状况下,Spring 容器中未打开注解装配。所以,要使用基于注解装配,咱们必须经过配置 <context:annotation-config />
元素在 Spring 配置文件中启用它。
固然,若是胖友是使用 Spring Boot ,默认状况下已经开启。
@Component
:它将 Java 类标记为 Bean 。它是任何 Spring 管理组件的通用构造型。@Controller
:它将一个类标记为 Spring Web MVC 控制器。@Service
:此注解是组件注解的特化。它不会对 @Component
注解提供任何其余行为。您能够在服务层类中使用 @Service 而不是 @Component
,由于它以更好的方式指定了意图。@Repository
:这个注解是具备相似用途和功能的 @Component
注解的特化。它为 DAO 提供了额外的好处。它将 DAO 导入 IoC 容器,并使未经检查的异常有资格转换为 Spring DataAccessException 。@Required
注解,应用于 Bean 属性 setter 方法。
示例代码以下:
public class Employee { |
@Autowired
注解,能够更准确地控制应该在何处以及如何进行自动装配。
示例代码以下:
public class EmpAccount { |
当你建立多个相同类型的 Bean ,并但愿仅使用属性装配其中一个 Bean 时,您可使用 @Qualifier
注解和 @Autowired
经过指定 ID 应该装配哪一个确切的 Bean 来消除歧义。
例如,应用中有两个类型为 Employee 的 Bean ID 为 "emp1"
和 "emp2"
,此处,咱们但愿 EmployeeAccount Bean 注入 "emp1"
对应的 Bean 对象。代码以下:
public class EmployeeAccount { |
Spring AOP 的面试题中,大多数都是概念题,主要是对切面的理解。概念点主要有:
- AOP
- Aspect
- JoinPoint
- PointCut
- Advice
- Target
- AOP Proxy
- Weaving
很是推荐阅读以下两篇文章:
AOP(Aspect-Oriented Programming),即面向切面编程, 它与 OOP( Object-Oriented Programming, 面向对象编程) 相辅相成, 提供了与 OOP 不一样的抽象软件结构的视角。
Aspect 由 PointCut 和 Advice 组成。
AOP 的工做重心在于如何将加强编织目标对象的链接点上, 这里包含两个工做:
能够简单地认为, 使用 @Aspect 注解的类就是切面
JoinPoint ,切点,程序运行中的一些时间点, 例如:
在 Spring AOP 中,JoinPoint 老是方法的执行点。
PointCut ,匹配 JoinPoint 的谓词(a predicate that matches join points)。
简单来讲,PointCut 是匹配 JoinPoint 的条件。
Advice => PointCut => JoinPoint
。😈 是否是以为有点绕,实际场景下,其实也不会弄的这么清楚~~
JoinPoint 和 PointCut 本质上就是两个不一样纬度上的东西。
或者,咱们在换一种说法:
Advice ,通知。
有哪些类型的 Advice?
@Before
注解标记进行配置。@AfterReturning
注解标记进行配置。@AfterThrowing
注解标记配置时执行。@After
注解标记进行配置。@Around
注解标记进行配置。😈 看起来,是否是和拦截器的执行时间,有几分类似。实际上,用于拦截效果的各类实现,大致都是相似的。
Target ,织入 Advice 的目标对象。目标对象也被称为 Advised Object 。
实现 AOP 的技术,主要分为两大类:
① 静态代理 - 指使用 AOP 框架提供的命令进行编译,从而在编译阶段就可生成 AOP 代理类,所以也称为编译时加强。
类加载时编织(特殊的类加载器实现)。
例如,SkyWalking 基于 Java Agent 机制,配置上 ByteBuddy 库,实现类加载时编织时加强,从而实现链路追踪的透明埋点。
感兴趣的胖友,能够看看 《SkyWalking 源码分析之 JavaAgent 工具 ByteBuddy 的应用》 。
② 动态代理 - 在运行时在内存中“临时”生成 AOP 动态代理类,所以也被称为运行时加强。目前 Spring 中使用了两种动态代理库:
那么 Spring 何时使用 JDK 动态代理,何时使用 CGLIB 呢?
// From 《Spring 源码深度解析》P172 |
或者,咱们来换一个解答答案:
Spring AOP 中的动态代理主要有两种方式,
JDK 动态代理
JDK 动态代理经过反射来接收被代理的类,而且要求被代理的类必须实现一个接口。JDK动态代理的核心是 InvocationHandler 接口和 Proxy 类。
CGLIB 动态代理
若是目标类没有实现接口,那么 Spring AOP 会选择使用 CGLIB 来动态代理目标类。固然,Spring 也支持配置,强制使用 CGLIB 动态代理。
CGLIB(Code Generation Library),是一个代码生成的类库,能够在运行时动态的生成某个类的子类,注意,CGLIB 是经过继承的方式作的动态代理,所以若是某个类被标记为 final
,那么它是没法使用 CGLIB 作动态代理的。
Weaving ,编织。
在 Spring AOP 中,有两种方式配置 AOP 切面:
目前,主流喜欢使用 注解 方式。胖友能够看看 《完全征服 Spring AOP 之实战篇》 。
很是推荐阅读以下文章:
事务就是对一系列的数据库操做(好比插入多条数据)进行统一的提交或回滚操做,若是插入成功,那么一块儿成功,若是中间有一条出现异常,那么回滚以前的全部操做。
这样能够防止出现脏数据,防止数据库数据出现问题。
指的是 ACID ,以下图所示:
目前 Spring 提供两种类型的事务管理:
实际场景下,咱们通常使用 Spring Boot + 注解的声明式事务。具体的示例,胖友能够看看 《Spring Boot 事务注解详解》 。
另外,也推荐看看 《Spring 事务管理 - 编程式事务、声明式事务》 一文。
① 首先,咱们先明确下,这里数据持久层框架,指的是 Spring JDBC、Hibernate、Spring JPA、MyBatis 等等。
② 而后,Spring 事务的管理,是经过 org.springframework.transaction.PlatformTransactionManager
进行管理,定义以下:
// PlatformTransactionManager.java |
#getTransaction(TransactionDefinition definition)
方法,根据事务定义 TransactionDefinition ,得到 TransactionStatus 。
#commit(TransactionStatus status)
方法,根据 TransactionStatus 状况,提交事务。
@Transactional
注解的的 A 方法,会调用 @Transactional
注解的的 B 方法。
PlatformTransactionManager#commit(TransactionStatus status)
方法,此处事务是不能、也不会提交的。PlatformTransactionManager#commit(TransactionStatus status)
方法,提交事务。#rollback(TransactionStatus status)
方法,根据 TransactionStatus 状况,回滚事务。
#commit(TransactionStatus status)
方法。③ 再以后,PlatformTransactionManager 有抽象子类 org.springframework.transaction.support.AbstractPlatformTransactionManager
,基于 模板方法模式 ,实现事务总体逻辑的骨架,而抽象 #doCommit(DefaultTransactionStatus status)
、#doRollback(DefaultTransactionStatus status)
等等方法,交由子类类来实现。
前方高能,即将进入关键的 ④ 步骤。
④ 最后,不一样的数据持久层框架,会有其对应的 PlatformTransactionManager 实现类,以下图所示:
以下,是一个比较常见的 XML 方式来配置的事务管理器,使用的是 DataSourceTransactionManager 。代码以下:
<!-- 事务管理器 --> |
😈 是否是颇有趣,更多详细的解析,可见以下几篇文章:
作过 Spring 多数据源的胖友,都会有个惨痛的经历,为何在开启事务的 Service 层的方法中,没法切换数据源呢?由于,在 Spring 的事务管理中,所使用的数据库链接会和当前线程所绑定,即便咱们设置了另一个数据源,使用的仍是当前的数据源链接。
另外,多个数据源且须要事务的场景,自己会带来多事务一致性的问题,暂时没有特别好的解决方案。
因此通常一个应用,推荐除非了读写分离所带来的多数据源,其它状况下,建议只有一个数据源。而且,随着微服务日益身形,一个服务对应一个 DB 是比较常见的架构选择。
@Transactional
注解的属性以下:
属性 | 类型 | 描述 |
---|---|---|
value | String | 可选的限定描述符,指定使用的事务管理器 |
propagation | enum: Propagation | 可选的事务传播行为设置 |
isolation | enum: Isolation | 可选的事务隔离级别设置 |
readOnly | boolean | 读写或只读事务,默认读写 |
timeout | int (in seconds granularity) | 事务超时时间设置 |
rollbackFor | Class对象数组,必须继承自Throwable | 致使事务回滚的异常类数组 |
rollbackForClassName | 类名数组,必须继承自Throwable | 致使事务回滚的异常类名字数组 |
noRollbackFor | Class对象数组,必须继承自Throwable | 不会致使事务回滚的异常类数组 |
noRollbackForClassName | 类名数组,必须继承自Throwable | 不会致使事务回滚的异常类名字数组 |
@Transactional
的全部属性默认值便可。具体用法以下:
@Transactional
能够做用于接口、接口方法、类以及类方法上。看成用于类上时,该类的全部 public
方法将都具备该类型的事务属性,同时,咱们也能够在方法级别使用该标注来覆盖类级别的定义。@Transactional
注解能够做用于接口、接口方法、类以及类方法上,可是 Spring 建议不要在接口或者接口方法上使用该注解,由于这只有在使用基于接口的代理时它才会生效。另外, @Transactional
注解应该只被应用到 public
方法上,这是由 Spring AOP 的本质决定的。若是你在 protected
、private
或者默承认见性的方法上使用 @Transactional
注解,这将被忽略,也不会抛出任何异常。这一点,很是须要注意。下面,咱们来简单说下源码相关的东西。
@Transactional
注解的属性,会解析成 org.springframework.transaction.TransactionDefinition
对象,即事务定义。TransactionDefinition 代码以下:
public interface TransactionDefinition { |
@Transactional
注解的 rollbackFor
、rollbackForClassName
、noRollbackFor
、noRollbackForClassName
属性貌似没体现出来?它们提如今 TransactionDefinition 的实现类 RuleBasedTransactionAttribute 中。#getPropagationBehavior()
方法,返回事务的传播行为,该值是个枚举,在下面来讲。#getIsolationLevel()
方法,返回事务的隔离级别,该值是个枚举,在下面来讲。关于这个问题,涉及的内容会比较多,胖友直接看以下两篇文章:
另外,有一点很是重要,不一样数据库对四个隔离级别的支持和实现略有不一样。由于咱们目前互联网主要使用 MySQL 为主,因此至少要搞懂 MySQL 对隔离级别的支持和实现状况。
在 TransactionDefinition 接口中,定义了“四种”的隔离级别枚举。代码以下:
// TransactionDefinition.java |
事务的传播行为,指的是当前带有事务配置的方法,须要怎么处理事务。
有一点须要注意,事务的传播级别,并非数据库事务规范中的名词,而是 Spring 自身所定义的。经过事务的传播级别,Spring 才知道如何处理事务,是建立一个新事务呢,仍是继续使用当前的事务。
艿艿的自我吐槽:是否是有种背概念背的想哭
在 TransactionDefinition 接口中,定义了三类七种传播级别。代码以下:
// TransactionDefinition.java |
PROPAGATION_REQUIRED
传播级别。PROPAGATION_NESTED
是 Spring 所特有的。
PROPAGATION_NESTED
启动的事务内嵌于外部事务中(若是存在外部事务的话),此时,内嵌事务并非一个独立的事务,它依赖于外部事务的存在,只有经过外部的事务提交,才能引发内部事务的提交,嵌套的子事务不能单独提交。若是熟悉 JDBC 中的保存点(SavePoint)的概念,那嵌套事务就很容易理解了,其实嵌套的子事务就是保存点的一个应用,一个事务中能够包括多个保存点,每个嵌套子事务。另外,外部事务的回滚也会致使嵌套子事务的回滚。PROPAGATION_NESTED
文字很长,实际咱们基本没用过。或者说,去掉基本,咱们根本没用过。所谓事务超时,就是指一个事务所容许执行的最长时间,若是超过该时间限制但事务尚未完成,则自动回滚事务。
在 TransactionDefinition 中以 int
的值来表示超时时间,其单位是秒。
固然,这个属性,貌似咱们基本也没用过。
事务的只读属性是指,对事务性资源进行只读操做或者是读写操做。
在 TransactionDefinition 中以 boolean
类型来表示该事务是否只读。
回滚规则,定义了哪些异常会致使事务回滚而哪些不会。
注意,事务的回滚规则,并非数据库事务规范中的名词,而是 Spring 自身所定义的。
艿艿:这个可能不是一个面试题,主要知足下你们的好奇心。
TransactionStatus 接口,记录事务的状态,不只仅包含事务自己,还包含事务的其它信息。代码以下:
// TransactionStatus.java |
Object transaction
属性,表示事务对象。#isNewTransaction()
方法,表示是不是新建立的事务。有什么用呢?答案结合 「Spring 事务如何和不一样的数据持久层框架作集成?」 问题,咱们对 #commit(TransactionStatus status)
方法的解释。经过该方法,咱们能够判断,当前事务是否当前方法所建立的,只有建立事务的方法,才能且应该真正的提交事务。从倾向上来讲,艿艿比较喜欢注解 + 声明式事务。
艿艿:这块的问题,感受面试问的很少,至少我不多问。哈哈哈。就当作下了解,万一问了呢。
可能会有胖友说,不是应该还有 Spring JDBC 吗。注意,Spring JDBC 不是 ORM 框架。
Spring 提供了 Spring JDBC 框架,方便咱们使用 JDBC 。
对于开发者,只须要使用 JdbcTemplate 类,它提供了不少便利的方法解决诸如把数据库数据转变成基本数据类型或对象,执行写好的或可调用的数据库操做语句,提供自定义的数据错误处理。
没有使用过的胖友,能够看看 《Spring JDBC 访问关系型数据库》 文章。
经过使用 Spring 数据数据访问层,它统一了各个数据持久层框架的不一样异常,统一进行提供 org.springframework.dao.DataAccessException
异常及其子类。以下图所示:
艿艿:这个问题很灵异,由于艿艿已经很久不使用 Hibernate 了,因此答案是直接复制的。
咱们能够经过两种方式使用 Spring 访问 Hibernate:
艿艿:不过我记得,12 年我用过 Spring JPA 的方式,操做 Hibernate 。具体可参考 《一块儿来学 SpringBoot 2.x | 第六篇:整合 Spring Data JPA》 。
固然,咱们能够再来看一道 《JPA 规范与 ORM 框架之间的关系是怎样的呢?》 。这个问题,我却是问过面试的候选人,哈哈哈哈。
整理 Spring 面试题的过程当中,又把 Spring 的知识点又复习了一遍。我忽然有点想念,那本被我翻烂的 《Spring 实战》 。😈 我要买一本原版的!!!
参考与推荐以下文章: