如下面試題,基於網絡整理,和本身編輯。具體參考的文章,會在文末給出全部的連接。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 實戰》 。😈 我要買一本原版的!!!
參考與推薦以下文章: