32 道 Spring 常見面試總結(附詳細參考答案),我經常拿來面試別人

語言: CN / TW / HK

最近在對 JavaGuide 的內容進行重構完善,同步一下最新更新,希望能夠幫助你。

Spring 基礎

什麼是 Spring 框架?

Spring 是一款開源的輕量級 Java 開發框架,旨在提高開發人員的開發效率以及系統的可維護性。

我們一般說 Spring 框架指的都是 Spring Framework,它是很多模組的集合,使用這些模組可以很方便地協助我們進行開發,比如說 Spring 支援 IoC(Inverse of Control:控制反轉) 和 AOP(Aspect-Oriented Programming:面向切面程式設計)、可以很方便地對資料庫進行訪問、可以很方便地整合第三方元件(電子郵件,任務,排程,快取等等)、對單元測試支援比較好、支援 RESTful Java 應用程式的開發。

Spring 最核心的思想就是不重新造輪子,開箱即用,提高開發效率。

Spring 翻譯過來就是春天的意思,可見其目標和使命就是為 Java 程式設計師帶來春天啊!感動!

多提一嘴 : 語言的流行通常需要一個殺手級的應用,Spring 就是 Java 生態的一個殺手級的應用框架。

Spring 提供的核心功能主要是 IoC 和 AOP。學習 Spring ,一定要把 IoC 和 AOP 的核心思想搞懂!

Spring 包含的模組有哪些?

Spring4.x 版本

Spring4.x主要模組

Spring5.x 版本

Spring5.x主要模組

Spring5.x 版本中 Web 模組的 Portlet 元件已經被廢棄掉,同時增加了用於非同步響應式處理的 WebFlux 元件。

Spring 各個模組的依賴關係如下:

Spring 各個模組的依賴關係

Core Container

Spring 框架的核心模組,也可以說是基礎模組,主要提供 IoC 依賴注入功能的支援。Spring 其他所有的功能基本都需要依賴於該模組,我們從上面那張 Spring 各個模組的依賴關係圖就可以看出來。

  • spring-core :Spring 框架基本的核心工具類。
  • spring-beans :提供對 bean 的建立、配置和管理等功能的支援。
  • spring-context :提供對國際化、事件傳播、資源載入等功能的支援。
  • spring-expression :提供對錶達式語言(Spring Expression Language) SpEL 的支援,只依賴於 core 模組,不依賴於其他模組,可以單獨使用。

AOP

  • spring-aspects :該模組為與 AspectJ 的整合提供支援。
  • spring-aop :提供了面向切面的程式設計實現。
  • spring-instrument :提供了為 JVM 新增代理(agent)的功能。 具體來講,它為 Tomcat 提供了一個織入代理,能夠為 Tomcat 傳遞類文 件,就像這些檔案是被類載入器載入的一樣。沒有理解也沒關係,這個模組的使用場景非常有限。

Data Access/Integration

  • spring-jdbc :提供了對資料庫訪問的抽象 JDBC。不同的資料庫都有自己獨立的 API 用於操作資料庫,而 Java 程式只需要和 JDBC API 互動,這樣就遮蔽了資料庫的影響。
  • spring-tx :提供對事務的支援。
  • spring-orm : 提供對 Hibernate、JPA 、iBatis 等 ORM 框架的支援。
  • spring-oxm :提供一個抽象層支撐 OXM(Object-to-XML-Mapping),例如:JAXB、Castor、XMLBeans、JiBX 和 XStream 等。
  • spring-jms : 訊息服務。自 Spring Framework 4.1 以後,它還提供了對 spring-messaging 模組的繼承。

Spring Web

  • spring-web :對 Web 功能的實現提供一些最基礎的支援。
  • spring-webmvc : 提供對 Spring MVC 的實現。
  • spring-websocket : 提供了對 WebSocket 的支援,WebSocket 可以讓客戶端和服務端進行雙向通訊。
  • spring-webflux :提供對 WebFlux 的支援。WebFlux 是 Spring Framework 5.0 中引入的新的響應式框架。與 Spring MVC 不同,它不需要 Servlet API,是完全非同步。

Messaging

spring-messaging 是從 Spring4.0 開始新加入的一個模組,主要職責是為 Spring 框架整合一些基礎的報文傳送應用。

Spring Test

Spring 團隊提倡測試驅動開發(TDD)。有了控制反轉 (IoC)的幫助,單元測試和整合測試變得更簡單。

Spring 的測試模組對 JUnit(單元測試框架)、TestNG(類似 JUnit)、Mockito(主要用來 Mock 物件)、PowerMock(解決 Mockito 的問題比如無法模擬 final, static, private 方法)等等常用的測試框架支援的都比較好。

Spring,Spring MVC,Spring Boot 之間什麼關係?

很多人對 Spring,Spring MVC,Spring Boot 這三者傻傻分不清楚!這裡簡單介紹一下這三者,其實很簡單,沒有什麼高深的東西。

Spring 包含了多個功能模組(上面剛剛提高過),其中最重要的是 Spring-Core(主要提供 IoC 依賴注入功能的支援) 模組, Spring 中的其他模組(比如 Spring MVC)的功能實現基本都需要依賴於該模組。

下圖對應的是 Spring4.x 版本。目前最新的 5.x 版本中 Web 模組的 Portlet 元件已經被廢棄掉,同時增加了用於非同步響應式處理的 WebFlux 元件。

Spring主要模組

Spring MVC 是 Spring 中的一個很重要的模組,主要賦予 Spring 快速構建 MVC 架構的 Web 程式的能力。MVC 是模型(Model)、檢視(View)、控制器(Controller)的簡寫,其核心思想是通過將業務邏輯、資料、顯示分離來組織程式碼。

使用 Spring 進行開發各種配置過於麻煩比如開啟某些 Spring 特性時,需要用 XML 或 Java 進行顯式配置。於是,Spring Boot 誕生了!

Spring 旨在簡化 J2EE 企業應用程式開發。Spring Boot 旨在簡化 Spring 開發(減少配置檔案,開箱即用!)。

Spring Boot 只是簡化了配置,如果你需要構建 MVC 架構的 Web 程式,你還是需要使用 Spring MVC 作為 MVC 框架,只是說 Spring Boot 幫你簡化了 Spring MVC 的很多配置,真正做到開箱即用!

Spring IoC

談談自己對於 Spring IoC 的瞭解

IoC(Inverse of Control:控制反轉) 是一種設計思想,而不是一個具體的技術實現。IoC 的思想就是將原本在程式中手動建立物件的控制權,交由 Spring 框架來管理。不過, IoC 並非 Spring 特有,在其他語言中也有應用。

為什麼叫控制反轉?

  • 控制 :指的是物件建立(例項化、管理)的權力
  • 反轉 :控制權交給外部環境(Spring 框架、IoC 容器)

將物件之間的相互依賴關係交給 IoC 容器來管理,並由 IoC 容器完成物件的注入。這樣可以很大程度上簡化應用的開發,把應用從複雜的依賴關係中解放出來。 IoC 容器就像是一個工廠一樣,當我們需要建立一個物件的時候,只需要配置好配置檔案/註解即可,完全不用考慮物件是如何被創建出來的。

在實際專案中一個 Service 類可能依賴了很多其他的類,假如我們需要例項化這個 Service,你可能要每次都要搞清這個 Service 所有底層類的建構函式,這可能會把人逼瘋。如果利用 IoC 的話,你只需要配置好,然後在需要的地方引用就行了,這大大增加了專案的可維護性且降低了開發難度。

在 Spring 中, IoC 容器是 Spring 用來實現 IoC 的載體, IoC 容器實際上就是個 Map(key,value),Map 中存放的是各種物件。

Spring 時代我們一般通過 XML 檔案來配置 Bean,後來開發人員覺得 XML 檔案來配置不太好,於是 SpringBoot 註解配置就慢慢開始流行起來。

相關閱讀:

什麼是 Spring Bean?

簡單來說,Bean 代指的就是那些被 IoC 容器所管理的物件。

我們需要告訴 IoC 容器幫助我們管理哪些物件,這個是通過配置元資料來定義的。配置元資料可以是 XML 檔案、註解或者 Java 配置類。

<!-- Constructor-arg with 'value' attribute -->
<bean id="..." class="...">
   <constructor-arg value="..."/>
</bean>

下圖簡單地展示了 IoC 容器如何使用配置元資料來管理物件。

org.springframework.beans和 org.springframework.context 這兩個包是 IoC 實現的基礎,如果想要研究 IoC 相關的原始碼的話,可以去看看

將一個類宣告為 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來實現。

@Bean註解使用示例:

@Configuration
public class AppConfig {
    @Bean
    public TransferService transferService() {
        return new TransferServiceImpl();
    }

}

上面的程式碼相當於下面的 xml 配置

<beans>
    <bean id="transferService" class="com.acme.TransferServiceImpl"/>
</beans>

下面這個例子是通過 @Component 無法實現的。

@Bean
public OneService getService(status) {
    case (status)  {
        when 1:
                return new serviceImpl1();
        when 2:
                return new serviceImpl2();
        when 3:
                return new serviceImpl3();
    }
}

注入 Bean 的註解有哪些?

Spring 內建的 @Autowired 以及 JDK 內建的 @Resource 和 @Inject 都可以用於注入 Bean。

Annotaion Package Source
@Autowired org.springframework.bean.factory Spring 2.5+
@Resource javax.annotation Java JSR-250
@Inject javax.inject Java JSR-330

@Autowired 和@Resource使用的比較多一些。

@Autowired 和 @Resource 的區別是什麼?

Autowired 屬於 Spring 內建的註解,預設的注入方式為byType(根據型別進行匹配),也就是說會優先根據介面型別去匹配並注入 Bean (介面的實現類)。

這會有什麼問題呢? 當一個介面存在多個實現類的話,byType這種方式就無法正確注入物件了,因為這個時候 Spring 會同時找到多個滿足條件的選擇,預設情況下它自己不知道選擇哪一個。

這種情況下,注入方式會變為 byName(根據名稱進行匹配),這個名稱通常就是類名(首字母小寫)。就比如說下面程式碼中的 smsService 就是我這裡所說的名稱,這樣應該比較好理解了吧。

// smsService 就是我們上面所說的名稱
@Autowired
private SmsService smsService;

舉個例子,SmsService 介面有兩個實現類: SmsServiceImpl1和 SmsServiceImpl2,且它們都已經被 Spring 容器所管理。

// 報錯,byName 和 byType 都無法匹配到 bean
@Autowired
private SmsService smsService;
// 正確注入 SmsServiceImpl1 物件對應的 bean
@Autowired
private SmsService smsServiceImpl1;
// 正確注入  SmsServiceImpl1 物件對應的 bean
// smsServiceImpl1 就是我們上面所說的名稱
@Autowired
@Qualifier(value = "smsServiceImpl1")
private SmsService smsService;

我們還是建議通過 @Qualifier 註解來顯示指定名稱而不是依賴變數的名稱。

@Resource屬於 JDK 提供的註解,預設注入方式為 byName。如果無法通過名稱匹配到對應的 Bean 的話,注入方式會變為byType。

@Resource 有兩個比較重要且日常開發常用的屬性:name(名稱)、type(型別)。

public @interface Resource {
    String name() default "";
    Class<?> type() default Object.class;
}

如果僅指定 name 屬性則注入方式為byName,如果僅指定type屬性則注入方式為byType,如果同時指定name 和type屬性(不建議這麼做)則注入方式為byType+byName。

// 報錯,byName 和 byType 都無法匹配到 bean
@Resource
private SmsService smsService;
// 正確注入 SmsServiceImpl1 物件對應的 bean
@Resource
private SmsService smsServiceImpl1;
// 正確注入 SmsServiceImpl1 物件對應的 bean(比較推薦這種方式)
@Resource(name = "smsServiceImpl1")
private SmsService smsService;

簡單總結一下:

  • @Autowired 是 Spring 提供的註解,@Resource 是 JDK 提供的註解。
  • Autowired 預設的注入方式為byType(根據型別進行匹配),@Resource預設注入方式為 byName(根據名稱進行匹配)。
  • 當一個介面存在多個實現類的情況下,@Autowired 和@Resource都需要通過名稱才能正確匹配到對應的 Bean。Autowired 可以通過 @Qualifier 註解來顯示指定名稱,@Resource可以通過 name 屬性來顯示指定名稱。

Bean 的作用域有哪些?

Spring 中 Bean 的作用域通常有下面幾種:

  • singleton : IoC 容器中只有唯一的 bean 例項。Spring 中的 bean 預設都是單例的,是對單例設計模式的應用。
  • prototype : 每次獲取都會建立一個新的 bean 例項。也就是說,連續 getBean() 兩次,得到的是不同的 Bean 例項。
  • request (僅 Web 應用可用): 每一次 HTTP 請求都會產生一個新的 bean(請求 bean),該 bean 僅在當前 HTTP request 內有效。
  • session (僅 Web 應用可用) : 每一次來自新 session 的 HTTP 請求都會產生一個新的 bean(會話 bean),該 bean 僅在當前 HTTP session 內有效。
  • application/global-session (僅 Web 應用可用): 每個 Web 應用在啟動時建立一個 Bean(應用 Bean),,該 bean 僅在當前應用啟動時間內有效。
  • websocket (僅 Web 應用可用):每一次 WebSocket 會話產生一個新的 bean。

如何配置 bean 的作用域呢?

xml 方式:

<bean id="..." class="..." scope="singleton"></bean>

註解方式:

@Bean
@Scope(value = ConfigurableBeanFactory.SCOPE_PROTOTYPE)
public Person personPrototype() {
    return new Person();
}

單例 Bean 的執行緒安全問題了解嗎?

大部分時候我們並沒有在專案中使用多執行緒,所以很少有人會關注這個問題。單例 Bean 存線上程問題,主要是因為當多個執行緒操作同一個物件的時候是存在資源競爭的。

常見的有兩種解決辦法:

  1. 在 Bean 中儘量避免定義可變的成員變數。
  2. 在類中定義一個 ThreadLocal 成員變數,將需要的可變成員變數儲存在 ThreadLocal 中(推薦的一種方式)。

不過,大部分 Bean 實際都是無狀態(沒有例項變數)的(比如 Dao、Service),這種情況下, Bean 是執行緒安全的。

Bean 的生命週期瞭解麼?

下面的內容整理自: https:// yemengying.com/2016/07/ 14/spring-bean-life-cycle/ ,除了這篇文章,再推薦一篇很不錯的文章 : https://www. cnblogs.com/zrtqsk/p/37 35273.html

  • Bean 容器找到配置檔案中 Spring Bean 的定義。
  • Bean 容器利用 Java Reflection API 建立一個 Bean 的例項。
  • 如果涉及到一些屬性值 利用 set()方法設定一些屬性值。
  • 如果 Bean 實現了 BeanNameAware 介面,呼叫 setBeanName()方法,傳入 Bean 的名字。
  • 如果 Bean 實現了 BeanClassLoaderAware 介面,呼叫 setBeanClassLoader()方法,傳入 ClassLoader物件的例項。
  • 如果 Bean 實現了 BeanFactoryAware 介面,呼叫 setBeanFactory()方法,傳入 BeanFactory物件的例項。
  • 與上面的類似,如果實現了其他 *.Aware介面,就呼叫相應的方法。
  • 如果有和載入這個 Bean 的 Spring 容器相關的 BeanPostProcessor 物件,執行postProcessBeforeInitialization() 方法
  • 如果 Bean 實現了InitializingBean介面,執行afterPropertiesSet()方法。
  • 如果 Bean 在配置檔案中的定義包含 init-method 屬性,執行指定的方法。
  • 如果有和載入這個 Bean 的 Spring 容器相關的 BeanPostProcessor 物件,執行postProcessAfterInitialization() 方法
  • 當要銷燬 Bean 的時候,如果 Bean 實現了 DisposableBean 介面,執行 destroy() 方法。
  • 當要銷燬 Bean 的時候,如果 Bean 在配置檔案中的定義包含 destroy-method 屬性,執行指定的方法。

圖示:

Spring Bean 生命週期

與之比較類似的中文版本:

Spring Bean 生命週期

Spring AoP

談談自己對於 AOP 的瞭解

AOP(Aspect-Oriented Programming:面向切面程式設計)能夠將那些與業務無關,卻為業務模組所共同呼叫的邏輯或責任(例如事務處理、日誌管理、許可權控制等)封裝起來,便於減少系統的重複程式碼,降低模組間的耦合度,並有利於未來的可拓展性和可維護性。

Spring AOP 就是基於動態代理的,如果要代理的物件,實現了某個介面,那麼 Spring AOP 會使用 JDK Proxy ,去建立代理物件,而對於沒有實現介面的物件,就無法使用 JDK Proxy 去進行代理了,這時候 Spring AOP 會使用 Cglib 生成一個被代理物件的子類來作為代理,如下圖所示:

SpringAOPProcess

當然你也可以使用 AspectJ !Spring AOP 已經集成了 AspectJ ,AspectJ 應該算的上是 Java 生態系統中最完整的 AOP 框架了。

AOP 切面程式設計設計到的一些專業術語:

術語 含義
目標(Target) 被通知的物件
代理(Proxy) 向目標物件應用通知之後建立的代理物件
連線點(JoinPoint) 目標物件的所屬類中,定義的所有方法均為連線點
切入點(Pointcut) 被切面攔截 / 增強的連線點(切入點一定是連線點,連線點不一定是切入點)
通知(Advice) 增強的邏輯 / 程式碼,也即攔截到目標物件的連線點之後要做的事情
切面(Aspect) 切入點(Pointcut)+通知(Advice)
Weaving(織入) 將通知應用到目標物件,進而生成代理物件的過程動作

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 快很多。

AspectJ 定義的通知型別有哪些?

  • Before (前置通知):目標物件的方法呼叫之前觸發
  • After (後置通知):目標物件的方法呼叫之後觸發
  • AfterReturning (返回通知):目標物件的方法呼叫完成,在返回結果值之後觸發
  • AfterThrowing (異常通知) :目標物件的方法執行中丟擲 / 觸發異常後觸發。AfterReturning 和 AfterThrowing 兩者互斥。如果方法呼叫成功無異常,則會有返回值;如果方法丟擲了異常,則不會有返回值。
  • Around : (環繞通知)編程式控制目標物件的方法呼叫。環繞通知是所有通知型別中可操作範圍最大的一種,因為它可以直接拿到目標物件,以及要執行的方法,所以環繞通知可以任意的在目標物件的方法呼叫前後搞事,甚至不呼叫目標物件的方法

多個切面的執行順序如何控制?

1、通常使用@Order 註解直接定義切面順序

// 值越小優先順序越高
@Order(3)
@Component
@Aspect
public class LoggingAspect implements Ordered {

2、實現Ordered 介面重寫 getOrder 方法。

@Component
@Aspect
public class LoggingAspect implements Ordered {

    // ....

    @Override
    public int getOrder() {
        // 返回值越小優先順序越高
        return 1;
    }
}

Spring MVC

說說自己對於 Spring MVC 瞭解?

MVC 是模型(Model)、檢視(View)、控制器(Controller)的簡寫,其核心思想是通過將業務邏輯、資料、顯示分離來組織程式碼。

網上有很多人說 MVC 不是設計模式,只是軟體設計規範,我個人更傾向於 MVC 同樣是眾多設計模式中的一種。** java-design-patterns ** 專案中就有關於 MVC 的相關介紹。

想要真正理解 Spring MVC,我們先來看看 Model 1 和 Model 2 這兩個沒有 Spring MVC 的時代。

Model 1 時代

很多學 Java 後端比較晚的朋友可能並沒有接觸過 Model 1 時代下的 JavaWeb 應用開發。在 Model1 模式下,整個 Web 應用幾乎全部用 JSP 頁面組成,只用少量的 JavaBean 來處理資料庫連線、訪問等操作。

這個模式下 JSP 即是控制層(Controller)又是表現層(View)。顯而易見,這種模式存在很多問題。比如控制邏輯和表現邏輯混雜在一起,導致程式碼重用率極低;再比如前端和後端相互依賴,難以進行測試維護並且開發效率極低。

mvc-mode1

Model 2 時代

學過 Servlet 並做過相關 Demo 的朋友應該瞭解“Java Bean(Model)+ JSP(View)+Servlet(Controller) ”這種開發模式,這就是早期的 JavaWeb MVC 開發模式。

  • Model:系統涉及的資料,也就是 dao 和 bean。
  • View:展示模型中的資料,只是用來展示。
  • Controller:處理使用者請求都發送給 ,返回資料給 JSP 並展示給使用者。

Model2 模式下還存在很多問題,Model2 的抽象和封裝程度還遠遠不夠,使用 Model2 進行開發時不可避免地會重複造輪子,這就大大降低了程式的可維護性和複用性。

於是,很多 JavaWeb 開發相關的 MVC 框架應運而生比如 Struts2,但是 Struts2 比較笨重。

Spring MVC 時代

隨著 Spring 輕量級開發框架的流行,Spring 生態圈出現了 Spring MVC 框架, Spring MVC 是當前最優秀的 MVC 框架。相比於 Struts2 , Spring MVC 使用更加簡單和方便,開發效率更高,並且 Spring MVC 執行速度更快。

MVC 是一種設計模式,Spring MVC 是一款很優秀的 MVC 框架。Spring MVC 可以幫助我們進行更簡潔的 Web 層的開發,並且它天生與 Spring 框架整合。Spring MVC 下我們一般把後端專案分為 Service 層(處理業務)、Dao 層(資料庫操作)、Entity 層(實體類)、Controller 層(控制層,返回資料給前臺頁面)。

Spring MVC 的核心元件有哪些?

記住了下面這些元件,也就記住了 SpringMVC 的工作原理。

  • DispatcherServlet核心的中央處理器 ,負責接收請求、分發,並給予客戶端響應。
  • HandlerMapping處理器對映器 ,根據 uri 去匹配查詢能處理的 Handler ,並會將請求涉及到的攔截器和 Handler 一起封裝。
  • HandlerAdapter處理器介面卡 ,根據 HandlerMapping 找到的 Handler ,適配執行對應的 Handler;
  • Handler請求處理器 ,處理實際請求的處理器。
  • ViewResolver檢視解析器 ,根據 Handler 返回的邏輯檢視 / 檢視,解析並渲染真正的檢視,並傳遞給 DispatcherServlet 響應客戶端

SpringMVC 工作原理了解嗎?

Spring MVC 原理如下圖所示:

SpringMVC 工作原理的圖解我沒有自己畫,直接圖省事在網上找了一個非常清晰直觀的,原出處不明。

流程說明(重要):

  1. 客戶端(瀏覽器)傳送請求, DispatcherServlet攔截請求。
  2. DispatcherServlet 根據請求資訊呼叫 HandlerMapping 。HandlerMapping 根據 uri 去匹配查詢能處理的 Handler(也就是我們平常說的 Controller 控制器) ,並會將請求涉及到的攔截器和 Handler 一起封裝。
  3. DispatcherServlet 呼叫 HandlerAdapter適配執行 Handler 。
  4. Handler 完成對使用者請求的處理後,會返回一個 ModelAndView 物件給DispatcherServlet,ModelAndView 顧名思義,包含了資料模型以及相應的檢視的資訊。Model 是返回的資料物件,View 是個邏輯上的 View。
  5. ViewResolver 會根據邏輯 View 查詢實際的 View。
  6. DispaterServlet 把返回的 Model 傳給 View(檢視渲染)。
  7. 把 View 返回給請求者(瀏覽器)

統一異常處理怎麼做?

推薦使用註解的方式統一異常處理,具體會使用到 @ControllerAdvice + @ExceptionHandler 這兩個註解 。

@ControllerAdvice
@ResponseBody
public class GlobalExceptionHandler {

    @ExceptionHandler(BaseException.class)
    public ResponseEntity<?> handleAppException(BaseException ex, HttpServletRequest request) {
      //......
    }

    @ExceptionHandler(value = ResourceNotFoundException.class)
    public ResponseEntity<ErrorReponse> handleResourceNotFoundException(ResourceNotFoundException ex, HttpServletRequest request) {
      //......
    }
}

這種異常處理方式下,會給所有或者指定的 Controller 織入異常處理的邏輯(AOP),當 Controller 中的方法丟擲異常的時候,由被@ExceptionHandler 註解修飾的方法進行處理。

ExceptionHandlerMethodResolver 中 getMappedMethod 方法決定了異常具體被哪個被 @ExceptionHandler 註解修飾的方法處理異常。

@Nullable
 private Method getMappedMethod(Class<? extends Throwable> exceptionType) {
  List<Class<? extends Throwable>> matches = new ArrayList<>();
    //找到可以處理的所有異常資訊。mappedMethods 中存放了異常和處理異常的方法的對應關係
  for (Class<? extends Throwable> mappedException : this.mappedMethods.keySet()) {
   if (mappedException.isAssignableFrom(exceptionType)) {
    matches.add(mappedException);
   }
  }
    // 不為空說明有方法處理異常
  if (!matches.isEmpty()) {
      // 按照匹配程度從小到大排序
   matches.sort(new ExceptionDepthComparator(exceptionType));
      // 返回處理異常的方法
   return this.mappedMethods.get(matches.get(0));
  }
  else {
   return null;
  }
 }

從原始碼看出:getMappedMethod()會首先找到可以匹配處理異常的所有方法資訊,然後對其進行從小到大的排序,最後取最小的那一個匹配的方法(即匹配度最高的那個)。

由於篇幅過多,部分內容沒有放出來,你可以點選下面的連結進行線上閱讀,體驗更好一些。

參考

自薦一個非常不錯的 Java 教程類開源專案:JavaGuide 。這是我在大三開始準備秋招面試的時候建立的,已經維護了 4 年多了,目前這個專案在 Github 上收到了 125k+ 的 star。

並且,這個專案還推出了一個PDF版本: 完結撒花!JavaGuide面試突擊版來啦!

《Java 面試指北 》 是一份教你如何更高效地準備面試的小冊,涵蓋常見八股文(系統設計、常見框架、分散式、高併發 ......)、優質面經等內容。

img

為了幫助更多同學準備 Java 面試以及學習 Java ,我建立了一個純粹的知識星球。雖然收費只有培訓班/訓練營的百分之一,但是知識星球裡的內容質量更高,提供的服務也更全面。

歡迎準備 Java 面試以及學習 Java 的同學加入我的知識星球,乾貨非常多!收費雖然是白菜價,但星球裡的內容或許比你參加上萬的培訓班質量還要高。

我有自己的原則,不割韭菜,用心做內容,真心希望幫助到你!