精盡 Spring 面試題

如下面試題,基於網絡整理,和本身編輯。具體參考的文章,會在文末給出全部的連接。php

若是胖友有本身的疑問,歡迎在星球提問,咱們一塊兒整理吊吊的 Spring 面試題的大保健。html

而題目的難度,艿艿儘可能按照從容易到困難的順序,逐步下去。前端

注意 1 ,這個面試題,暫時不包括 Spring MVC 部分。主要以 Spring IoC、Spring AOP、Spring Transaction 爲主。java

注意 2 ,T T Spring 怎麼會有這麼多問題,艿艿本身面試不少都不太問,跟背書同樣。因此整理的過程,真的是痛苦萬分。react

Spring 是一個很龐大的技術體系,能夠說包容一切,因此本文咱們按照下面的順序,羅列各類面試題:git

  • Spring 總體相關的面試
  • Spring IoC 相關的面試題
  • Spring AOP 相關的面試題
  • Spring Transaction 相關的面試題
  • Spring Data Access 相關的面試題

Spring 總體

什麼是 Spring Framework?

Spring 是一個開源應用框架,旨在下降應用程序開發的複雜度。github

  • 它是輕量級、鬆散耦合的。web

    它的輕量級主要是相對於 EJB 。隨着 Spring 的體系愈來愈龐大,你們被 Spring 的配置搞懵逼了,因此後來出了 Spring Boot 。面試

  • 它具備分層體系結構,容許用戶選擇組件,同時還爲 J2EE 應用程序開發提供了一個有凝聚力的框架。spring

  • 它能夠集成其餘框架,如 Spring MVC、Hibernate、MyBatis 等,因此又稱爲框架的框架( 粘合劑、腳手架 )。

Spring Framework 中有多少個模塊,它們分別是什麼?

以下是一張比較早期版本的 Spring Framework 的模塊圖:

艿艿:由於它的配色比較好看,哈哈哈哈。因此,沒本身畫一個最新的。

Spring Framework

咱們按照一個一個分塊來講。

Spring 核心容器

對應圖中,Core Container 。

該層基本上是 Spring Framework 的核心。它包含如下模塊:

  • Spring Core
  • 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 框架帶來的主要好處:

艿艿:注意,下文中加粗的內容,這是本句話的關鍵。

  • DI :Dependency Injection(DI) 方法,使得構造器和 JavaBean、properties 文件中的依賴關係一目瞭然。
  • 輕量級:與 EJB 容器相比較,IoC 容器更加趨向於輕量級。這樣一來 IoC 容器在有限的內存和 CPU 資源的狀況下,進行應用程序的開發和發佈就變得十分有利。
  • 面向切面編程(AOP): Spring 支持面向切面編程,同時把應用的業務邏輯與系統的服務分離開來。
  • 集成主流框架:Spring 並無閉門造車,Spring 集成了已有的技術棧,好比 ORM 框架、Logging 日期框架、J2EE、Quartz 和 JDK Timer ,以及其餘視圖技術。
  • 模塊化:Spring 框架是按照模塊的形式來組織的。由包和類的命名,就能夠看出其所屬的模塊,開發者僅僅須要選用他們須要的模塊便可。
  • 便捷的測試:要 測試一項用Spring開發的應用程序 十分簡單,由於測試相關的環境代碼都已經囊括在框架中了。更加簡單的是,利用 JavaBean 形式的 POJO 類,能夠很方便的利用依賴注入來寫入測試數據。
  • Web 框架:Spring 的 Web 框架亦是一個精心設計的 Web MVC 框架,爲開發者們在 Web 框架的選擇上提供了一個除了主流框架好比 Struts 、過分設計的、不流行 Web 框架的之外的有力選項。
  • 事務管理:Spring 提供了一個便捷的事務管理接口,適用於小型的本地事物處理(好比在單 DB 的環境下)和複雜的共同事物處理(好比利用 JTA 的複雜 DB 環境)。
  • 異常處理:Spring 提供一個方便的 API ,將特定技術的異常(由JDBC, Hibernate, 或 JDO 拋出)轉化爲一致的、Unchecked 異常。

固然,Spring 代碼優勢的同時,必定會帶來相應的缺點:

  • 每一個框架都有的問題,調試階段不直觀,後期的 bug 對應階段,不容易判斷問題所在。要花必定的時間去理解它。
  • 把不少 JavaEE 的東西封裝了,在知足快速開發高質量程序的同時,隱藏了實現細節。

    這就致使,不少工程師,離開了 Spring 以後,就不知道怎麼工做了。從 Java 工程師,變成了 Spring 工程師。對於有追求的咱們,仍是須要知其然,知其因此然。

Spring 框架中都用到了哪些設計模式?

Spring 框架中使用到了大量的設計模式,下面列舉了比較有表明性的:

  • 代理模式 — 在 AOP 和 remoting 中被用的比較多。
  • 單例模式 — 在 Spring 配置文件中定義的 Bean 默認爲單例模式。
  • 模板方法 — 用來解決代碼重複的問題。好比 RestTemplate、JmsTemplate、JdbcTemplate 。
  • 前端控制器 — Spring提供了 DispatcherServlet 來對請求進行分發。
  • 視圖幫助(View Helper) — Spring 提供了一系列的 JSP 標籤,高效宏來輔助將分散的代碼整合在視圖裏。
  • 依賴注入 — 貫穿於 BeanFactory / ApplicationContext 接口的核心理念。
  • 工廠模式 — BeanFactory 用來建立對象的實例。

固然,感興趣的胖友,以爲不過癮,能夠看看艿艿基友知秋寫的幾篇文章:

Spring IoC

下面,咱們會將分紅 IoC 和 Bean 兩部分來分享 Spring 容器的內容。

  • IoC ,側重在於容器。
  • Bean ,側重在於被容器管理的 Bean 。

什麼是 Spring IoC 容器?

注意,正確的拼寫是 IoC 。

Spring 框架的核心是 Spring IoC 容器。容器建立 Bean 對象,將它們裝配在一塊兒,配置它們並管理它們的完整生命週期。

  • Spring 容器使用依賴注入來管理組成應用程序的 Bean 對象。
  • 容器經過讀取提供的配置元數據 Bean Definition 來接收對象進行實例化,配置和組裝的指令。
  • 該配置元數據 Bean Definition 能夠經過 XML,Java 註解或 Java Config 代碼提供。

艿艿,注意上面三段段話的加粗部分的內容。

Spring IoC

什麼是依賴注入?

在依賴注入中,你沒必要主動、手動建立對象,但必須描述如何建立它們。

  • 你不是直接在代碼中將組件和服務鏈接在一塊兒,而是描述配置文件中哪些組件須要哪些服務。
  • 而後,再由 IoC 容器將它們裝配在一塊兒。

另外,依賴注入的英文縮寫是 Dependency Injection ,簡稱 DI 。

IoC 和 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 的種種優點隨之而來。
於我而言,它爲我帶來更多的是思惟方式的轉變,恐怕之後我再也沒法寫出那種一大塊的全功能程序了。

能夠經過多少種方式完成依賴注入?

一般,依賴注入能夠經過三種方式完成,即:

上面一個問題的三種方式的英文,下面是三種方式的中文。

  • 接口注入
  • 構造函數注入
  • setter 注入

目前,在 Spring Framework 中,僅使用構造函數和 setter 注入這兩種方式。

那麼這兩種方式各有什麼優缺點呢?胖友能夠簡單閱讀 《Spring兩種依賴注入方式的比較》,不用太較真。綜述來講:

構造函數注入 setter 注入
沒有部分注入 有部分注入
不會覆蓋 setter 屬性 會覆蓋 setter 屬性
任意修改都會建立一個新實例 任意修改不會建立一個新實例
適用於設置不少屬性 適用於設置少許屬性
  • 實際場景下,setting 注入使用的更多。

Spring 中有多少種 IoC 容器?

Spring 提供了兩種( 不是「個」 ) IoC 容器,分別是 BeanFactory、ApplicationContext 。

BeanFactory

BeanFactory 在 spring-beans 項目提供。

BeanFactory ,就像一個包含 Bean 集合的工廠類。它會在客戶端要求時實例化 Bean 對象。

ApplicationContext

ApplicationContext 在 spring-context 項目提供。

ApplicationContext 接口擴展了 BeanFactory 接口,它在 BeanFactory 基礎上提供了一些額外的功能。內置以下功能:

  • MessageSource :管理 message ,實現國際化等功能。
  • ApplicationEventPublisher :事件發佈。
  • ResourcePatternResolver :多資源加載。
  • EnvironmentCapable :系統 Environment(profile + Properties)相關。
  • Lifecycle :管理生命週期。
  • Closable :關閉,釋放資源
  • InitializingBean:自定義初始化。
  • BeanNameAware:設置 beanName 的 Aware 接口。

另外,ApplicationContext 會自動初始化非懶加載的 Bean 對象們。

詳細的內容,感興趣的胖友,能夠看看 《【死磕 Spring】—— ApplicationContext 相關接口架構分析》 一文。源碼以前無祕密。簡單總結下 BeanFactory 與 ApplicationContext 二者的差別:

艿艿:可能不少胖友沒看過源碼,因此會比較難。

BeanFactory ApplicationContext
它使用懶加載 它使用即時加載
它使用語法顯式提供資源對象 它本身建立和管理資源對象
不支持國際化 支持國際化
不支持基於依賴的註解 支持基於依賴的註解

另外,BeanFactory 也被稱爲低級容器,而 ApplicationContext 被稱爲高級容器。

請介紹下經常使用的 BeanFactory 容器?

BeanFactory 最經常使用的是 XmlBeanFactory 。它能夠根據 XML 文件中定義的內容,建立相應的 Bean。

請介紹下經常使用的 ApplicationContext 容器?

如下是三種較常見的 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 。

列舉一些 IoC 的一些好處?

  • 它將最小化應用程序中的代碼量。
  • 它以最小的影響和最少的侵入機制促進鬆耦合。
  • 它支持即時的實例化和延遲加載 Bean 對象。
  • 它將使您的應用程序易於測試,由於它不須要單元測試用例中的任何單例或 JNDI 查找機制。

簡述 Spring IoC 的實現機制?

簡單來講,Spring 中的 IoC 的實現原理,就是工廠模式加反射機制。代碼以下:

interface Fruit {

public abstract void eat();

}
class Apple implements Fruit {

public void eat(){
System.out.println("Apple");
}

}
class Orange implements Fruit {
public void eat(){
System.out.println("Orange");
}
}

class Factory {

public static Fruit getInstance(String className) {
Fruit f = null;
try {
f = (Fruit) Class.forName(className).newInstance();
} catch (Exception e) {
e.printStackTrace();
}
return f;
}

}

class Client {

public static void main(String[] args) {
Fruit f = Factory.getInstance("io.github.dunwu.spring.Apple");
if(f != null){
f.eat();
}
}

}
  • Fruit 接口,有 Apple 和 Orange 兩個實現類。
  • Factory 工廠,經過反射機制,建立 className 對應的 Fruit 對象。
  • Client 經過 Factory 工廠,得到對應的 Fruit 對象。
  • 😈 實際狀況下,Spring IoC 比這個複雜不少不少,例如單例 Bean 對象,Bean 的屬性注入,相互依賴的 Bean 的處理,以及等等。

在基友 《面試問爛的 Spring IoC 過程》 的文章中,把 Spring IoC 相關的內容,講的很是不錯。

Spring 框架中有哪些不一樣類型的事件?

Spring 的 ApplicationContext 提供了支持事件和代碼中監聽器的功能。

咱們能夠建立 Bean 用來監聽在 ApplicationContext 中發佈的事件。若是一個 Bean 實現了 ApplicationListener 接口,當一個ApplicationEvent 被髮布之後,Bean 會自動被通知。示例代碼以下:

public class AllApplicationEventListener implements ApplicationListener<ApplicationEvent> { 

@Override
public void onApplicationEvent(ApplicationEvent applicationEvent) {
// process event
}

}

Spring 提供瞭如下五種標準的事件:

  1. 上下文更新事件(ContextRefreshedEvent):該事件會在ApplicationContext 被初始化或者更新時發佈。也能夠在調用ConfigurableApplicationContext 接口中的 #refresh() 方法時被觸發。
  2. 上下文開始事件(ContextStartedEvent):當容器調用ConfigurableApplicationContext 的 #start() 方法開始/從新開始容器時觸發該事件。
  3. 上下文中止事件(ContextStoppedEvent):當容器調用 ConfigurableApplicationContext 的 #stop() 方法中止容器時觸發該事件。
  4. 上下文關閉事件(ContextClosedEvent):當ApplicationContext 被關閉時觸發該事件。容器被關閉時,其管理的全部單例 Bean 都被銷燬。
  5. 請求處理事件(RequestHandledEvent):在 We b應用中,當一個HTTP 請求(request)結束觸發該事件。

除了上面介紹的事件之外,還能夠經過擴展 ApplicationEvent 類來開發自定義的事件。

① 示例自定義的事件的類,代碼以下:

public class CustomApplicationEvent extends ApplicationEvent{ 

public CustomApplicationEvent(Object source, final String msg) {
super(source);
}

}

② 爲了監聽這個事件,還須要建立一個監聽器。示例代碼以下:

public class CustomEventListener implements ApplicationListener<CustomApplicationEvent> {

@Override
public void onApplicationEvent(CustomApplicationEvent applicationEvent) {
// handle event
}

}

③ 以後經過 ApplicationContext 接口的 #publishEvent(Object event) 方法,來發布自定義事件。示例代碼以下:

// 建立 CustomApplicationEvent 事件
CustomApplicationEvent customEvent = new CustomApplicationEvent(applicationContext, "Test message");
// 發佈事件
applicationContext.publishEvent(customEvent);

Spring Bean

什麼是 Spring Bean ?

  • Bean 由 Spring IoC 容器實例化,配置,裝配和管理。
  • Bean 是基於用戶提供給 IoC 容器的配置元數據 Bean Definition 建立。

這個問題,胖友能夠在回過頭看 「什麼是 Spring IoC 容器?」 問題,相互對照。

Spring 有哪些配置方式

單純從 Spring Framework 提供的方式,一共有三種:

  • 一、XML 配置文件。

    Bean 所需的依賴項和服務在 XML 格式的配置文件中指定。這些配置文件一般包含許多 bean 定義和特定於應用程序的配置選項。它們一般以 bean 標籤開頭。例如:

    <bean id="studentBean" class="org.edureka.firstSpring.StudentBean">
    <property name="name" value="Edureka"></property>
    </bean>
  • 二、註解配置。

    您能夠經過在相關的類,方法或字段聲明上使用註解,將 Bean 配置爲組件類自己,而不是使用 XML 來描述 Bean 裝配。默認狀況下,Spring 容器中未打開註解裝配。所以,您須要在使用它以前在 Spring 配置文件中啓用它。例如:

    <beans>
    <context:annotation-config/>
    <!-- bean definitions go here -->
    </beans>
  • 三、Java Config 配置。

    Spring 的 Java 配置是經過使用 @Bean 和 @Configuration 來實現。

    • @Bean 註解扮演與 <bean /> 元素相同的角色。
    • @Configuration 類容許經過簡單地調用同一個類中的其餘 @Bean 方法來定義 Bean 間依賴關係。
    • 例如:

      @Configuration
      public class StudentConfig {

      @Bean
      public StudentBean myStudent() {
      return new StudentBean();
      }

      }
      • 是否是很熟悉 😈

目前主要使用 Java Config 配置爲主。固然,三種配置方式是能夠混合使用的。例如說:

  • Dubbo 服務的配置,艿艿喜歡使用 XML 。
  • Spring MVC 請求的配置,艿艿喜歡使用 @RequestMapping 註解。
  • Spring MVC 攔截器的配置,艿艿喜歡 Java Config 配置。

另外,如今已是 Spring Boot 的天下,因此更加是 Java Config 配置爲主。

Spring 支持幾種 Bean Scope ?

艿艿,這個是一個比較小衆的題目,簡單瞭解便可。

Spring Bean 支持 5 種 Scope ,分別以下:

  • Singleton - 每一個 Spring IoC 容器僅有一個單 Bean 實例。默認
  • Prototype - 每次請求都會產生一個新的實例。
  • Request - 每一次 HTTP 請求都會產生一個新的 Bean 實例,而且該 Bean 僅在當前 HTTP 請求內有效。
  • Session - 每個的 Session 都會產生一個新的 Bean 實例,同時該 Bean 僅在當前 HTTP Session 內有效。
  • Application - 每個 Web Application 都會產生一個新的 Bean ,同時該 Bean 僅在當前 Web Application 內有效。

另外,網絡上不少文章說有 Global-session 級別,它是 Portlet 模塊獨有,目前已經廢棄,在 Spring5 中是找不到的。

僅當用戶使用支持 Web 的 ApplicationContext 時,最後三個纔可用。

再補充一點,開發者是能夠自定義 Bean Scope ,具體可參見 《Spring(10)—— Bean 做用範圍(二)—— 自定義 Scope》 。

不錯呢,仍是那句話,這個題目簡單瞭解下便可,實際經常使用的只有 Singleton 和 Prototype 兩種級別,甚至說,只有 Singleton 級別。😈

Spring Bean 在容器的生命週期是什麼樣的?

艿艿說:這是一個比較高級的 Spring 的面試題,很是常見,而且答對比較加分。固然,若是實際真正弄懂,須要對 Spring Bean 的源碼,有比較好的理解,因此 《精盡 Spring 源碼》 系列,該讀仍是讀吧。

艿艿:要注意下面每段話,艿艿進行加粗的地方。

Spring Bean 的初始化流程以下:

  • 實例化 Bean 對象

    • Spring 容器根據配置中的 Bean Definition(定義)中實例化 Bean 對象。

      Bean Definition 能夠經過 XML,Java 註解或 Java Config 代碼提供。

    • Spring 使用依賴注入填充全部屬性,如 Bean 中所定義的配置。

  • Aware 相關的屬性,注入到 Bean 對象
    • 若是 Bean 實現 BeanNameAware 接口,則工廠經過傳遞 Bean 的 beanName 來調用 #setBeanName(String name) 方法。
    • 若是 Bean 實現 BeanFactoryAware 接口,工廠經過傳遞自身的實例來調用 #setBeanFactory(BeanFactory beanFactory) 方法。
  • 調用相應的方法,進一步初始化 Bean 對象
    • 若是存在與 Bean 關聯的任何 BeanPostProcessor 們,則調用 #preProcessBeforeInitialization(Object bean, String beanName) 方法。
    • 若是 Bean 實現 InitializingBean 接口,則會調用 #afterPropertiesSet() 方法。
    • 若是爲 Bean 指定了 init 方法(例如 <bean /> 的 init-method 屬性),那麼將調用該方法。
    • 若是存在與 Bean 關聯的任何 BeanPostProcessor 們,則將調用 #postProcessAfterInitialization(Object bean, String beanName) 方法。

Spring Bean 的銷燬流程以下:

  • 若是 Bean 實現 DisposableBean 接口,當 spring 容器關閉時,會調用 #destroy() 方法。
  • 若是爲 bean 指定了 destroy 方法(例如 <bean /> 的 destroy-method 屬性),那麼將調用該方法。

總體以下圖:流程圖

無心中,艿艿又翻到一張有趣的總體圖,以下圖:

流程圖

什麼是 Spring 的內部 bean?

只有將 Bean 僅用做另外一個 Bean 的屬性時,才能將 Bean 聲明爲內部 Bean。

  • 爲了定義 Bean,Spring 提供基於 XML 的配置元數據在 <property>或 <constructor-arg> 中提供了 <bean>元素的使用。
  • 內部 Bean 老是匿名的,而且它們老是做爲原型 Prototype 。

例如,假設咱們有一個 Student 類,其中引用了 Person 類。這裏咱們將只建立一個 Person 類實例並在 Student 中使用它。示例代碼以下:

// Student.java

public class Student {

private Person person;

// ... Setters and Getters
}

// Person.java

public class Person {

private String name;
private String address;

// ... Setters and Getters
}
<!-- bean.xml -->

<bean id=「StudentBean" class="com.edureka.Student">
<property name="person">
<!--This is inner bean -->
<bean class="com.edureka.Person">
<property name="name" value=「Scott"></property>
<property name="address" value=「Bangalore"></property>
</bean>
</property>
</bean>

什麼是 Spring 裝配?

當 Bean 在 Spring 容器中組合在一塊兒時,它被稱爲裝配或 Bean 裝配。Spring 容器須要知道須要什麼 Bean 以及容器應該如何使用依賴注入來將 Bean 綁定在一塊兒,同時裝配 Bean 。

裝配,和上文提到的 DI 依賴注入,實際是一個東西。

自動裝配有哪些方式?

Spring 容器可以自動裝配 Bean 。也就是說,能夠經過檢查 BeanFactory 的內容讓 Spring 自動解析 Bean 的協做者。

自動裝配的不一樣模式:

  • no - 這是默認設置,表示沒有自動裝配。應使用顯式 Bean 引用進行裝配。
  • byName - 它根據 Bean 的名稱注入對象依賴項。它匹配並裝配其屬性與 XML 文件中由相同名稱定義的 Bean 。
  • 【最經常使用】byType - 它根據類型注入對象依賴項。若是屬性的類型與 XML 文件中的一個 Bean 類型匹配,則匹配並裝配屬性。
  • 構造函數 - 它經過調用類的構造函數來注入依賴項。它有大量的參數。
  • autodetect - 首先容器嘗試經過構造函數使用 autowire 裝配,若是不能,則嘗試經過 byType 自動裝配。

自動裝配有什麼侷限?

艿艿:這個題目,瞭解下便可,也不是很準確。

  • 覆蓋的可能性 - 您始終可使用 <constructor-arg> 和 <property> 設置指定依賴項,這將覆蓋自動裝配。
  • 基本元數據類型 - 簡單屬性(如原數據類型,字符串和類)沒法自動裝配。

    這種,嚴格來講,也不能稱爲侷限。由於能夠經過配置文件來解決。

  • 使人困惑的性質 - 老是喜歡使用明確的裝配,由於自動裝配不太精確。

解釋什麼叫延遲加載?

默認狀況下,容器啓動以後會將全部做用域爲單例的 Bean 都建立好,可是有的業務場景咱們並不須要它提早都建立好。此時,咱們能夠在Bean 中設置 lzay-init = "true" 。

  • 這樣,當容器啓動以後,做用域爲單例的 Bean ,就不在建立。
  • 而是在得到該 Bean 時,才真正在建立加載。

Spring 框架中的單例 Bean 是線程安全的麼?

Spring 框架並無對單例 Bean 進行任何多線程的封裝處理。

  • 關於單例 Bean 的線程安全和併發問題,須要開發者自行去搞定。
  • 而且,單例的線程安全問題,也不是 Spring 應該去關心的。Spring 應該作的是,提供根據配置,建立單例 Bean 或多例 Bean 的功能。

固然,但實際上,大部分的 Spring Bean 並無可變的狀態(好比Serview 類和 DAO 類),因此在某種程度上說 Spring 的單例 Bean 是線程安全的。

若是你的 Bean 有多種狀態的話,就須要自行保證線程安全。最淺顯的解決辦法,就是將多態 Bean 的做用域( Scope )由 Singleton 變動爲 Prototype 。

Spring Bean 怎麼解決循環依賴的問題?

艿艿說:能回答出這個問題的,通常是比較厲害的。

這是個比較複雜的問題,有能力的胖友,建議看下 《【死磕 Spring】—— IoC 之加載 Bean:建立 Bean(五)之循環依賴處理》

感受,不經過源碼,很難解釋清楚這個問題。若是看不懂的胖友,能夠在認真看完,在星球裏,咱們一塊兒多交流下。好玩的。

Spring 註解

這塊內容,實際寫在 「Spring Bean」 中比較合適,考慮到後續的問題,都是關於註解的,因此單獨起一個大的章節。

什麼是基於註解的容器配置?

不使用 XML 來描述 Bean 裝配,開發人員經過在相關的類,方法或字段聲明上使用註解將配置移動到組件類自己。它能夠做爲 XML 設置的替代方案。例如:

Spring 的 Java 配置是經過使用 @Bean 和 @Configuration 來實現。

  • @Bean 註解,扮演與 <bean /> 元素相同的角色。
  • @Configuration 註解的類,容許經過簡單地調用同一個類中的其餘 @Bean 方法來定義 Bean 間依賴關係。

示例以下:

@Configuration
public class StudentConfig {

@Bean
public StudentBean myStudent() {
return new StudentBean();
}

}

如何在 Spring 中啓動註解裝配?

默認狀況下,Spring 容器中未打開註解裝配。所以,要使用基於註解裝配,咱們必須經過配置 <context:annotation-config /> 元素在 Spring 配置文件中啓用它。

固然,若是胖友是使用 Spring Boot ,默認狀況下已經開啓。

@Component, @Controller, @Repository, @Service 有何區別?

  • @Component :它將 Java 類標記爲 Bean 。它是任何 Spring 管理組件的通用構造型。
  • @Controller :它將一個類標記爲 Spring Web MVC 控制器。
  • @Service :此註解是組件註解的特化。它不會對 @Component 註解提供任何其餘行爲。您能夠在服務層類中使用 @Service 而不是 @Component ,由於它以更好的方式指定了意圖。
  • @Repository :這個註解是具備相似用途和功能的 @Component 註解的特化。它爲 DAO 提供了額外的好處。它將 DAO 導入 IoC 容器,並使未經檢查的異常有資格轉換爲 Spring DataAccessException 。

@Required 註解有什麼用?

@Required 註解,應用於 Bean 屬性 setter 方法。

  • 此註解僅指示必須在配置時使用 Bean 定義中的顯式屬性值或使用自動裝配填充受影響的 Bean 屬性。
  • 若是還沒有填充受影響的 Bean 屬性,則容器將拋出 BeanInitializationException 異常。

示例代碼以下:

public class Employee {

private String name;

@Required
public void setName(String name){
this.name=name;
}

public string getName(){
return name;
}

}
  • T T 貌似平時不多用這個註解噢。

@Autowired 註解有什麼用?

@Autowired 註解,能夠更準確地控制應該在何處以及如何進行自動裝配。

  • 此註解用於在 setter 方法,構造函數,具備任意名稱或多個參數的屬性或方法上自動裝配 Bean。
  • 默認狀況下,它是類型驅動的注入。

示例代碼以下:

public class EmpAccount {

@Autowired
private Employee emp;

}

@Qualifier 註解有什麼用?

當你建立多個相同類型的 Bean ,並但願僅使用屬性裝配其中一個 Bean 時,您可使用 @Qualifier 註解和 @Autowired 經過指定 ID 應該裝配哪一個確切的 Bean 來消除歧義。

例如,應用中有兩個類型爲 Employee 的 Bean ID 爲 "emp1" 和 "emp2" ,此處,咱們但願 EmployeeAccount Bean 注入 "emp1" 對應的 Bean 對象。代碼以下:

public class EmployeeAccount {

@Autowired
@Qualifier(emp1)
private Employee emp;

}

Spring AOP

Spring AOP 的面試題中,大多數都是概念題,主要是對切面的理解。概念點主要有:

  • AOP
  • Aspect
  • JoinPoint
  • PointCut
  • Advice
  • Target
  • AOP Proxy
  • Weaving
  • 在閱讀完 「Spring AOP」 的面試題後,在回過頭思考下這些概念點,到底理解了多少。注意,不是背,理解!

很是推薦閱讀以下兩篇文章:

什麼是 AOP ?

AOP(Aspect-Oriented Programming),即面向切面編程, 它與 OOP( Object-Oriented Programming, 面向對象編程) 相輔相成, 提供了與 OOP 不一樣的抽象軟件結構的視角。

  • 在 OOP 中,以類( Class )做爲基本單元
  • 在 AOP 中,以切面( Aspect )做爲基本單元。

什麼是 Aspect ?

Aspect 由 PointCut 和 Advice 組成。

  • 它既包含了橫切邏輯的定義,也包括了鏈接點的定義。
  • Spring AOP 就是負責實施切面的框架,它將切面所定義的橫切邏輯編織到切面所指定的鏈接點中。

AOP 的工做重心在於如何將加強編織目標對象的鏈接點上, 這裏包含兩個工做:

  1. 如何經過 PointCut 和 Advice 定位到特定的 JoinPoint 上。
  2. 如何在 Advice 中編寫切面代碼。

能夠簡單地認爲, 使用 @Aspect 註解的類就是切面

流程圖

什麼是 JoinPoint ?

JoinPoint ,切點,程序運行中的一些時間點, 例如:

  • 一個方法的執行。
  • 或者是一個異常的處理。

在 Spring AOP 中,JoinPoint 老是方法的執行點。

什麼是 PointCut ?

PointCut ,匹配 JoinPoint 的謂詞(a predicate that matches join points)。

簡單來講,PointCut 是匹配 JoinPoint 的條件。

  • Advice 是和特定的 PointCut 關聯的,而且在 PointCut 相匹配的 JoinPoint 中執行。即 Advice => PointCut => JoinPoint 。
  • 在 Spring 中, 全部的方法均可以認爲是 JoinPoint ,可是咱們並不但願在全部的方法上都添加 Advice 。而 PointCut 的做用,就是提供一組規則(使用 AspectJ PointCut expression language 來描述) 來匹配 JoinPoint ,給知足規則的 JoinPoint 添加 Advice 。

😈 是否是以爲有點繞,實際場景下,其實也不會弄的這麼清楚~~

關於 JoinPoint 和 PointCut 的區別

JoinPoint 和 PointCut 本質上就是兩個不一樣緯度上的東西。

  • 在 Spring AOP 中,全部的方法執行都是 JoinPoint 。而 PointCut 是一個描述信息,它修飾的是 JoinPoint ,經過 PointCut ,咱們就能夠肯定哪些 JoinPoint 能夠被織入 Advice 。
  • Advice 是在 JoinPoint 上執行的,而 PointCut 規定了哪些 JoinPoint 能夠執行哪些 Advice 。

或者,咱們在換一種說法:

  1. 首先,Advice 經過 PointCut 查詢須要被織入的 JoinPoint 。
  2. 而後,Advice 在查詢到 JoinPoint 上執行邏輯。

什麼是 Advice ?

Advice ,通知。

  • 特定 JoinPoint 處的 Aspect 所採起的動做稱爲 Advice 。
  • Spring AOP 使用一個 Advice 做爲攔截器,在 JoinPoint 「周圍」維護一系列的攔截器。

有哪些類型的 Advice?

  • Before - 這些類型的 Advice 在 JoinPoint 方法以前執行,並使用 @Before 註解標記進行配置。
  • After Returning - 這些類型的 Advice 在鏈接點方法正常執行後執行,並使用 @AfterReturning 註解標記進行配置。
  • After Throwing - 這些類型的 Advice 僅在 JoinPoint 方法經過拋出異常退出並使用 @AfterThrowing 註解標記配置時執行。
  • After Finally - 這些類型的 Advice 在鏈接點方法以後執行,不管方法退出是正常仍是異常返回,並使用 @After 註解標記進行配置。
  • Around - 這些類型的 Advice 在鏈接點以前和以後執行,並使用 @Around 註解標記進行配置。

😈 看起來,是否是和攔截器的執行時間,有幾分類似。實際上,用於攔截效果的各類實現,大致都是相似的。

什麼是 Target ?

Target ,織入 Advice 的目標對象。目標對象也被稱爲 Advised Object 。

  • 由於 Spring AOP 使用運行時代理的方式來實現 Aspect ,所以 Advised Object 老是一個代理對象(Proxied Object) 。
  • 注意, Advised Object 指的不是原來的對象,而是織入 Advice 後所產生的代理對象。
  • Advice + Target Object = Advised Object = Proxy 。

AOP 有哪些實現方式?

實現 AOP 的技術,主要分爲兩大類:

  • ① 靜態代理 - 指使用 AOP 框架提供的命令進行編譯,從而在編譯階段就可生成 AOP 代理類,所以也稱爲編譯時加強。

    • 編譯時編織(特殊編譯器實現)
    • 類加載時編織(特殊的類加載器實現)。

      例如,SkyWalking 基於 Java Agent 機制,配置上 ByteBuddy 庫,實現類加載時編織時加強,從而實現鏈路追蹤的透明埋點。

      感興趣的胖友,能夠看看 《SkyWalking 源碼分析之 JavaAgent 工具 ByteBuddy 的應用》 。

  • ② 動態代理 - 在運行時在內存中「臨時」生成 AOP 動態代理類,所以也被稱爲運行時加強。目前 Spring 中使用了兩種動態代理庫:

    • JDK 動態代理
    • CGLIB

那麼 Spring 何時使用 JDK 動態代理,何時使用 CGLIB 呢?

// From 《Spring 源碼深度解析》P172
// Spring AOP 部分使用 JDK 動態代理或者 CGLIB 來爲目標對象建立代理。(建議儘可能使用 JDK 的動態代理)
// 若是被代理的目標對象實現了至少一個接口,則會使用 JDK 動態代理。全部該目標類型實現的接口都講被代理。
// 若該目標對象沒有實現任何接口,則建立一個 CGLIB 代理。
// 若是你但願強制使用 CGLIB 代理,(例如但願代理目標對象的全部方法,而不僅是實現自接口的方法)那也能夠。可是須要考慮如下兩個方法:
// 1> 沒法通知(advise) Final 方法,由於它們不能被覆蓋。
// 2> 你須要將 CGLIB 二進制髮型包放在 classpath 下面。
// 爲何 Spring 默認使用 JDK 的動態代理呢?筆者猜想緣由以下:
// 1> 使用 JDK 原生支持,減小三方依賴
// 2> JDK8 開始後,JDK 代理的性能差距 CGLIB 的性能不會太多。可參見:https://www.cnblogs.com/haiq/p/4304615.html

或者,咱們來換一個解答答案:

Spring AOP 中的動態代理主要有兩種方式,

  • JDK 動態代理

    JDK 動態代理經過反射來接收被代理的類,而且要求被代理的類必須實現一個接口。JDK動態代理的核心是 InvocationHandler 接口和 Proxy 類。

  • CGLIB 動態代理

    若是目標類沒有實現接口,那麼 Spring AOP 會選擇使用 CGLIB 來動態代理目標類。固然,Spring 也支持配置,強制使用 CGLIB 動態代理。
    CGLIB(Code Generation Library),是一個代碼生成的類庫,能夠在運行時動態的生成某個類的子類,注意,CGLIB 是經過繼承的方式作的動態代理,所以若是某個類被標記爲 final ,那麼它是沒法使用 CGLIB 作動態代理的。

Spring AOP and AspectJ AOP 有什麼區別?

  • 代理方式不一樣
    • Spring AOP 基於動態代理方式實現。
    • AspectJ AOP 基於靜態代理方式實現。
  • PointCut 支持力度不一樣
    • Spring AOP 僅支持方法級別的 PointCut 。
    • AspectJ AOP 提供了徹底的 AOP 支持,它還支持屬性級別的 PointCut 。

什麼是編織(Weaving)?

Weaving ,編織。

  • 爲了建立一個 Advice 對象而連接一個 Aspect 和其它應用類型或對象,稱爲編織(Weaving)。
  • 在 Spring AOP 中,編織在運行時執行,即動態代理。請參考下圖:Proxy

Spring 如何使用 AOP 切面?

在 Spring AOP 中,有兩種方式配置 AOP 切面:

  • 基於 XML 方式的切面實現。
  • 基於 註解 方式的切面實現。

目前,主流喜歡使用 註解 方式。胖友能夠看看 《完全征服 Spring AOP 之實戰篇》 。

Spring Transaction

很是推薦閱讀以下文章:

什麼是事務?

事務就是對一系列的數據庫操做(好比插入多條數據)進行統一的提交或回滾操做,若是插入成功,那麼一塊兒成功,若是中間有一條出現異常,那麼回滾以前的全部操做。

這樣能夠防止出現髒數據,防止數據庫數據出現問題。

事務的特性指的是?

指的是 ACID ,以下圖所示:

事務的特性

  1. 原子性 Atomicity :一個事務(transaction)中的全部操做,或者所有完成,或者所有不完成,不會結束在中間某個環節。事務在執行過程當中發生錯誤,會被恢復(Rollback)到事務開始前的狀態,就像這個事務歷來沒有執行過同樣。即,事務不可分割、不可約簡。
  2. 一致性 Consistency :在事務開始以前和事務結束之後,數據庫的完整性沒有被破壞。這表示寫入的資料必須徹底符合全部的預設約束觸發器)、級聯回滾等。
  3. 隔離性 Isolation :數據庫容許多個併發事務同時對其數據進行讀寫和修改的能力,隔離性能夠防止多個事務併發執行時因爲交叉執行而致使數據的不一致。事務隔離分爲不一樣級別,包括讀未提交(Read uncommitted)、讀提交(read committed)、可重複讀(repeatable read)和串行化(Serializable)。
  4. 持久性 Durability :事務處理結束後,對數據的修改就是永久的,即使系統故障也不會丟失。

列舉 Spring 支持的事務管理類型?

目前 Spring 提供兩種類型的事務管理:

  • 聲明式事務:經過使用註解或基於 XML 的配置事務,從而事務管理與業務代碼分離。
  • 編程式事務:經過編碼的方式實現事務管理,須要在代碼中顯式的調用事務的得到、提交、回滾。它爲您提供極大的靈活性,但維護起來很是困難。

實際場景下,咱們通常使用 Spring Boot + 註解的聲明式事務。具體的示例,胖友能夠看看 《Spring Boot 事務註解詳解》 。

另外,也推薦看看 《Spring 事務管理 - 編程式事務、聲明式事務》 一文。

Spring 事務如何和不一樣的數據持久層框架作集成?

① 首先,咱們先明確下,這裏數據持久層框架,指的是 Spring JDBC、Hibernate、Spring JPA、MyBatis 等等。

② 而後,Spring 事務的管理,是經過 org.springframework.transaction.PlatformTransactionManager 進行管理,定義以下:

// PlatformTransactionManager.java

public interface PlatformTransactionManager {

// 根據事務定義 TransactionDefinition ,得到 TransactionStatus 。
TransactionStatus getTransaction(@Nullable TransactionDefinition definition) throws TransactionException;

// 根據狀況,提交事務
void commit(TransactionStatus status) throws TransactionException;

// 根據狀況,回滾事務
void rollback(TransactionStatus status) throws TransactionException;

}
  • PlatformTransactionManager 是負責事務管理的接口,一共有三個接口方法,分別負責事務的得到、提交、回滾。
  • #getTransaction(TransactionDefinition definition) 方法,根據事務定義 TransactionDefinition ,得到 TransactionStatus 。
    • 爲何不是建立事務呢?由於若是當前若是已經有事務,則不會進行建立,通常來講會跟當前線程進行綁定。若是不存在事務,則進行建立。
    • 爲何返回的是 TransactionStatus 對象?在 TransactionStatus 中,不只僅包含事務屬性,還包含事務的其它信息,例如是否只讀、是否爲新建立的事務等等。😈 下面,也會詳細解析 TransactionStatus 。
    • 事務 TransactionDefinition 是什麼?😈 下面,也會詳細解析 TransactionStatus 。
  • #commit(TransactionStatus status) 方法,根據 TransactionStatus 狀況,提交事務。
    • 爲何根據 TransactionStatus 狀況,進行提交?例如說,帶@Transactional 註解的的 A 方法,會調用 @Transactional 註解的的 B 方法。
      • 在 B 方法結束調用後,會執行 PlatformTransactionManager#commit(TransactionStatus status) 方法,此處事務是不能、也不會提交的。
      • 而是在 A 方法結束調用後,執行 PlatformTransactionManager#commit(TransactionStatus status) 方法,提交事務。
  • #rollback(TransactionStatus status) 方法,根據 TransactionStatus 狀況,回滾事務。
    • 爲何根據 TransactionStatus 狀況,進行回滾?緣由同 #commit(TransactionStatus status) 方法。

③ 再以後,PlatformTransactionManager 有抽象子類 org.springframework.transaction.support.AbstractPlatformTransactionManager ,基於 模板方法模式 ,實現事務總體邏輯的骨架,而抽象 #doCommit(DefaultTransactionStatus status)#doRollback(DefaultTransactionStatus status) 等等方法,交由子類類來實現。

前方高能,即將進入關鍵的 ④ 步驟。

④ 最後,不一樣的數據持久層框架,會有其對應的 PlatformTransactionManager 實現類,以下圖所示:事務的特性

  • 全部的實現類,都基於 AbstractPlatformTransactionManager 這個骨架類。
  • HibernateTransactionManager ,和 Hibernate5 的事務管理作集成。
  • DataSourceTransactionManager ,和 JDBC 的事務管理作集成。因此,它也適用於 MyBatis、Spring JDBC 等等。
  • JpaTransactionManager ,和 JPA 的事務管理作集成。

以下,是一個比較常見的 XML 方式來配置的事務管理器,使用的是 DataSourceTransactionManager 。代碼以下:

<!-- 事務管理器 -->
<bean id="transactionManager"
class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<!-- 數據源 -->
<property name="dataSource" ref="dataSource" />
</bean>
  • 正如上文所說,它適用於 MyBatis、Spring JDBC 等等。

😈 是否是頗有趣,更多詳細的解析,可見以下幾篇文章:

爲何在 Spring 事務中不能切換數據源?

作過 Spring 多數據源的胖友,都會有個慘痛的經歷,爲何在開啓事務的 Service 層的方法中,沒法切換數據源呢?由於,在 Spring 的事務管理中,所使用的數據庫鏈接會和當前線程所綁定,即便咱們設置了另一個數據源,使用的仍是當前的數據源鏈接。

另外,多個數據源且須要事務的場景,自己會帶來多事務一致性的問題,暫時沒有特別好的解決方案。

因此通常一個應用,推薦除非了讀寫分離所帶來的多數據源,其它狀況下,建議只有一個數據源。而且,隨着微服務日益身形,一個服務對應一個 DB 是比較常見的架構選擇。

@Transactional 註解有哪些屬性?如何使用?

@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 的本質決定的。若是你在 protectedprivate 或者默承認見性的方法上使用 @Transactional 註解,這將被忽略,也不會拋出任何異常。這一點,很是須要注意。

下面,咱們來簡單說下源碼相關的東西。

@Transactional 註解的屬性,會解析成 org.springframework.transaction.TransactionDefinition 對象,即事務定義。TransactionDefinition 代碼以下:

public interface TransactionDefinition {

int getPropagationBehavior(); // 事務的傳播行爲
int getIsolationLevel(); // 事務的隔離級別
int getTimeout(); // 事務的超時時間
boolean isReadOnly(); // 事務是否只讀
@Nullable
String getName(); // 事務的名字

}
  • 可能會胖友有之後,@Transactional 註解的 rollbackForrollbackForClassNamenoRollbackFornoRollbackForClassName 屬性貌似沒體現出來?它們提如今 TransactionDefinition 的實現類 RuleBasedTransactionAttribute 中。
  • #getPropagationBehavior() 方法,返回事務的傳播行爲,該值是個枚舉,在下面來講。
  • #getIsolationLevel() 方法,返回事務的隔離級別,該值是個枚舉,在下面來講。

什麼是事務的隔離級別?分紅哪些隔離級別?

關於這個問題,涉及的內容會比較多,胖友直接看以下兩篇文章:

另外,有一點很是重要,不一樣數據庫對四個隔離級別的支持和實現略有不一樣。由於咱們目前互聯網主要使用 MySQL 爲主,因此至少要搞懂 MySQL 對隔離級別的支持和實現狀況。

在 TransactionDefinition 接口中,定義了「四種」的隔離級別枚舉。代碼以下:

// TransactionDefinition.java

/**
* 【Spring 獨有】使用後端數據庫默認的隔離級別
*
* MySQL 默認採用的 REPEATABLE_READ隔離級別
* Oracle 默認採用的 READ_COMMITTED隔離級別
*/
int ISOLATION_DEFAULT = -1;

/**
* 最低的隔離級別,容許讀取還沒有提交的數據變動,可能會致使髒讀、幻讀或不可重複讀
*/
int ISOLATION_READ_UNCOMMITTED = Connection.TRANSACTION_READ_UNCOMMITTED;

/**
* 容許讀取併發事務已經提交的數據,能夠阻止髒讀,可是幻讀或不可重複讀仍有可能發生
*/
int ISOLATION_READ_COMMITTED = Connection.TRANSACTION_READ_COMMITTED;
/**
* 對同一字段的屢次讀取結果都是一致的,除非數據是被自己事務本身所修改,能夠阻止髒讀和不可重複讀,但幻讀仍有可能發生。
*/
int ISOLATION_REPEATABLE_READ = Connection.TRANSACTION_REPEATABLE_READ;
/**
* 最高的隔離級別,徹底服從ACID的隔離級別。全部的事務依次逐個執行,這樣事務之間就徹底不可能產生干擾,也就是說,該級別能夠防止髒讀、不可重複讀以及幻讀。
*
* 可是這將嚴重影響程序的性能。一般狀況下也不會用到該級別。
*/
int ISOLATION_SERIALIZABLE = Connection.TRANSACTION_SERIALIZABLE;

什麼是事務的傳播級別?分紅哪些傳播級別?

事務的傳播行爲,指的是當前帶有事務配置的方法,須要怎麼處理事務。

  • 例如:方法可能繼續在現有事務中運行,也可能開啓一個新事務,並在本身的事務中運行。
  • 有一點須要注意,事務的傳播級別,並非數據庫事務規範中的名詞,而是 Spring 自身所定義的。經過事務的傳播級別,Spring 才知道如何處理事務,是建立一個新事務呢,仍是繼續使用當前的事務。

    艿艿的自我吐槽:是否是有種背概念背的想哭

在 TransactionDefinition 接口中,定義了三類七種傳播級別。代碼以下:

// TransactionDefinition.java

// ========== 支持當前事務的狀況 ==========

/**
* 若是當前存在事務,則使用該事務。
* 若是當前沒有事務,則建立一個新的事務。
*/
int PROPAGATION_REQUIRED = 0;
/**
* 若是當前存在事務,則使用該事務。
* 若是當前沒有事務,則以非事務的方式繼續運行。
*/
int PROPAGATION_SUPPORTS = 1;
/**
* 若是當前存在事務,則使用該事務。
* 若是當前沒有事務,則拋出異常。
*/
int PROPAGATION_MANDATORY = 2;

// ========== 不支持當前事務的狀況 ==========

/**
* 建立一個新的事務。
* 若是當前存在事務,則把當前事務掛起。
*/
int PROPAGATION_REQUIRES_NEW = 3;
/**
* 以非事務方式運行。
* 若是當前存在事務,則把當前事務掛起。
*/
int PROPAGATION_NOT_SUPPORTED = 4;
/**
* 以非事務方式運行。
* 若是當前存在事務,則拋出異常。
*/
int PROPAGATION_NEVER = 5;

// ========== 其餘狀況 ==========

/**
* 若是當前存在事務,則建立一個事務做爲當前事務的嵌套事務來運行。
* 若是當前沒有事務,則等價於 {@link TransactionDefinition#PROPAGATION_REQUIRED}
*/
int PROPAGATION_NESTED = 6;
  • 分類以後,其實仍是比較好記的。固然,絕大數場景,咱們只用 PROPAGATION_REQUIRED 傳播級別。
  • 這裏須要指出的是,前面的六種事務傳播行爲是 Spring 從 EJB 中引入的,他們共享相同的概念。而 PROPAGATION_NESTED 是 Spring 所特有的。
    • 以 PROPAGATION_NESTED 啓動的事務內嵌於外部事務中(若是存在外部事務的話),此時,內嵌事務並非一個獨立的事務,它依賴於外部事務的存在,只有經過外部的事務提交,才能引發內部事務的提交,嵌套的子事務不能單獨提交。若是熟悉 JDBC 中的保存點(SavePoint)的概念,那嵌套事務就很容易理解了,其實嵌套的子事務就是保存點的一個應用,一個事務中能夠包括多個保存點,每個嵌套子事務。另外,外部事務的回滾也會致使嵌套子事務的回滾。
    • 😈 固然,雖然上面 PROPAGATION_NESTED 文字很長,實際咱們基本沒用過。或者說,去掉基本,咱們根本沒用過。

什麼是事務的超時屬性?

所謂事務超時,就是指一個事務所容許執行的最長時間,若是超過該時間限制但事務尚未完成,則自動回滾事務。

在 TransactionDefinition 中以 int 的值來表示超時時間,其單位是秒。

固然,這個屬性,貌似咱們基本也沒用過。

什麼是事務的只讀屬性?

事務的只讀屬性是指,對事務性資源進行只讀操做或者是讀寫操做。

  • 所謂事務性資源就是指那些被事務管理的資源,好比數據源、JMS 資源,以及自定義的事務性資源等等。
  • 若是肯定只對事務性資源進行只讀操做,那麼咱們能夠將事務標誌爲只讀的,以提升事務處理的性能。感興趣的胖友,能夠看看 《不使用事務和使用只讀事務的區別 》 。

在 TransactionDefinition 中以 boolean 類型來表示該事務是否只讀。

什麼是事務的回滾規則?

回滾規則,定義了哪些異常會致使事務回滾而哪些不會。

  • 默認狀況下,事務只有遇到運行期異常時纔會回滾,而在遇到檢查型異常時不會回滾(這一行爲與EJB的回滾行爲是一致的)。
  • 可是你能夠聲明事務在遇到特定的檢查型異常時像遇到運行期異常那樣回滾。一樣,你還能夠聲明事務遇到特定的異常不回滾,即便這些異常是運行期異常。

注意,事務的回滾規則,並非數據庫事務規範中的名詞,而是 Spring 自身所定義的。

簡單介紹 TransactionStatus ?

艿艿:這個可能不是一個面試題,主要知足下你們的好奇心。

TransactionStatus 接口,記錄事務的狀態,不只僅包含事務自己,還包含事務的其它信息。代碼以下:

// TransactionStatus.java

public interface TransactionStatus extends SavepointManager, Flushable {

/**
* 是不是新建立的事務
*/
boolean isNewTransaction();

/**
* 是否有 Savepoint
*
* 在 {@link TransactionDefinition#PROPAGATION_NESTED} 傳播級別使用。
*/
boolean hasSavepoint();

/**
* 設置爲只回滾
*/
void setRollbackOnly();
/**
* 是否爲只回滾
*/
boolean isRollbackOnly();

/**
* 執行 flush 操做
*/
@Override
void flush();

/**
* 是否事務已經完成
*/
boolean isCompleted();

}
  • 爲何沒有事務對象呢?在 TransactionStatus 的實現類 DefaultTransactionStatus 中,有個 Object transaction 屬性,表示事務對象。
  • #isNewTransaction() 方法,表示是不是新建立的事務。有什麼用呢?答案結合 「Spring 事務如何和不一樣的數據持久層框架作集成?」 問題,咱們對 #commit(TransactionStatus status) 方法的解釋。經過該方法,咱們能夠判斷,當前事務是否當前方法所建立的,只有建立事務的方法,才能且應該真正的提交事務。

使用 Spring 事務有什麼優勢?

  1. 經過 PlatformTransactionManager ,爲不一樣的數據層持久框架提供統一的 API ,無需關心究竟是原生 JDBC、Spring JDBC、JPA、Hibernate 仍是 MyBatis 。
  2. 經過使用聲明式事務,使業務代碼和事務管理的邏輯分離,更加清晰。

從傾向上來講,艿艿比較喜歡註解 + 聲明式事務。

Spring Data Access

艿艿:這塊的問題,感受面試問的很少,至少我不多問。哈哈哈。就當作下了解,萬一問了呢。

Spring 支持哪些 ORM 框架?

  • Hibernate
  • JPA
  • MyBatis
  • JDO
  • OJB

可能會有胖友說,不是應該還有 Spring JDBC 嗎。注意,Spring JDBC 不是 ORM 框架。

在 Spring 框架中如何更有效地使用 JDBC ?

Spring 提供了 Spring JDBC 框架,方便咱們使用 JDBC 。

對於開發者,只須要使用 JdbcTemplate 類,它提供了不少便利的方法解決諸如把數據庫數據轉變成基本數據類型或對象,執行寫好的或可調用的數據庫操做語句,提供自定義的數據錯誤處理。

沒有使用過的胖友,能夠看看 《Spring JDBC 訪問關係型數據庫》 文章。

Spring 數據數據訪問層有哪些異常?

經過使用 Spring 數據數據訪問層,它統一了各個數據持久層框架的不一樣異常,統一進行提供 org.springframework.dao.DataAccessException 異常及其子類。以下圖所示:

流程圖

使用 Spring 訪問 Hibernate 的方法有哪些?

艿艿:這個問題很靈異,由於艿艿已經很久不使用 Hibernate 了,因此答案是直接複製的。

咱們能夠經過兩種方式使用 Spring 訪問 Hibernate:

  • 使用 Hibernate 模板和回調進行控制反轉。
  • 擴展 HibernateDAOSupport 並應用 AOP 攔截器節點。

艿艿:不過我記得,12 年我用過 Spring JPA 的方式,操做 Hibernate 。具體可參考 《一塊兒來學 SpringBoot 2.x | 第六篇:整合 Spring Data JPA》 。

固然,咱們能夠再來看一道 《JPA 規範與 ORM 框架之間的關係是怎樣的呢?》 。這個問題,我卻是問過面試的候選人,哈哈哈哈。

666. 彩蛋

整理 Spring 面試題的過程當中,又把 Spring 的知識點又複習了一遍。我忽然有點想念,那本被我翻爛的 《Spring 實戰》 。😈 我要買一本原版的!!!

參考與推薦以下文章:

相關文章
相關標籤/搜索