原始碼下面無秘密,這是程式設計師的口頭禪。對於強大而且設計優秀的Spring框架也是這樣的,在基礎程式碼層層堆疊之下,Spring成為了一個非常流行的框架。
Spring6框架的開發者們透過層層設計和封裝打造了一個功能如此之多而相容性非常好的框架。這也是解構這個框架難點,而透過理解整個框架功能的實現也差不多瞭解了整個Spring的生態,甚至是整個java開發生態的大部分知識。
Spring6框架包含哪些內容
透過SpringFramework6的原始碼可以看到,Spring包含如下幾個模組:
spring-aop
:提供了面向切面程式設計(AOP)的支援,允許在程式碼中分離橫切關注點。spring-aspects
:包含了用於實現 AOP 的AspectJ 整合和 weaving 功能。spring-beans
:是 Spring 框架的核心模組之一,提供了對 JavaBean 的配置和管理。spring-context
:提供了應用程式上下文的管理和配置,包括依賴注入和上下文的生命週期。spring-context-indexer
:用於支援 Spring Context 的索引和搜尋功能。spring-context-support
:提供了一些額外的上下文支援,如快取、事件釋出和監聽器等。spring-core
:是 Spring 框架的基礎模組,包含了一些核心的工具和類。spring-core-test
:是 Spring 核心模組的測試支援。spring-expression
:提供了一種強大的表示式語言,用於在配置檔案和註解中解析和操作物件。spring-instrument
:用於在應用程式執行期間動態檢測和修改類的行為。spring-jcl
:提供了 JCL(Jakarta Commons Logging)的整合和日誌管理。spring-jdbc
:提供了對 JDBC 資料庫操作的簡化和封裝。spring-jms
:用於與 JMS(Java Message Service)訊息佇列的整合。spring-messaging
:是一個通用的訊息傳遞抽象和實現。spring-orm
:提供了對 ORM(Object Relational Mapping)框架的整合支援。spring-oxm
:是 Spring Object/Relational Mapping(ORM)模組的一部分。spring-r2dbc
:用於與 Reactive Relational Database Connectivity(反應式關係型資料庫連線)的支援。spring-test
:提供了用於測試 Spring 應用程式的工具和類。spring-tx
:提供了對事務管理的支援。spring-web
:是 Spring 框架的 Web 模組,提供了 Web 應用程式的開發支援。spring-webflux
:用於構建反應式 Web 應用程式。spring-webmvc
:是傳統的 Spring MVC 框架,用於構建 Web 應用程式。spring-websocket
:用於實現 WebSocket 通訊。
這些模組基本就是Spring6框架的全部核心了。
Spring6框架如何深入瞭解
如何深入瞭解Spring框架呢?
一個很常用的辦法就是透過使用到的api結合原始碼來分析和理解。api給程式設計師一個直觀的體驗,這個api是什麼,就有什麼功能。
當想要理解原理的時候,就可以結合原始碼來來了解原理和實現方式。原始碼下面無秘密。
另外一方面就是透過實現介面或者仿寫介面來更加深入的理解原始碼當中的原理。為什麼要這樣寫而不是其它方式呢?效能還是複用?透過實現介面肯定能學到更多東西。
這裡就有一個大概的學習Spring6框架的模板了。
- 編寫demo使用api
- 閱讀api的原始碼和實現
- 編寫api的實現
- 編寫單元測試
以Sping6框架中核心類org.springframework.beans.factory.BeanFactory
為例說明整個研究和學習過程。
介面org.springframework.beans.factory.BeanFactory#getBean
,它的主要功能如下:
-
返回指定 bean 的一個例項,該例項可以是共享的或獨立的。
-
這種方法允許 Spring BeanFactory 用作 Singleton 或 Prototype 設計模式的替代品。在 Singleton bean 的情況下,呼叫者可能會保留對返回物件的引用。
-
將別名轉換回相應的規範 bean 名稱。
-
如果在該工廠例項中找不到該 bean,則將向父工廠詢問。
-
編寫使用demo
package io.yulin.learn.spring.s100;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.stereotype.Component;
/**
* BeanFactory獲取Bean使用demo
* @author r0ad
* @since 1.0
*/
@SpringBootApplication
@Slf4j
public class BeanFactoryGetBean {
public static void main(String[] args) {
ConfigurableApplicationContext context = SpringApplication.run(BeanFactoryGetBean.class, args);
log.info("當前上下文是BeanFactory {}",context instanceof BeanFactory);
ComponentDemo1 componentDemo1 = (ComponentDemo1) context.getBean("ComponentDemo1");
// 測試獲取到的bean的介面
componentDemo1.test();
// 關閉應用
context.close();
}
}
@Component("ComponentDemo1")
@Slf4j
class ComponentDemo1 {
public void test() {
log.info("ComponentDemo1 test");
}
}
輸出的結果如下。可以看到ConfigurableApplicationContext
實現了BeanFactory
的getBean
介面。透過context.getBean("ComponentDemo1")
獲取到的bean就是ComponentDemo1
。而且可以直接呼叫test
方法。
當前上下文是BeanFactory true
ComponentDemo1 test
我們可以透過一個ConcurrentHashMap來維護bean的例項。透過這個Map實現getBean
方法。
package io.yulin.learn.spring.s100;
import org.springframework.beans.factory.BeanCreationException;
import java.util.concurrent.ConcurrentHashMap;
/**
* 實現 BeanFactory 介面的getBean方法,由於BeanFactory有很多介面,此處演示就沒有直接implements BeanFactory
* @author nine
* @since 1.0
*/
public class BeanFactoryDemoImpl {
private ConcurrentHashMap<String, Object> beans = new ConcurrentHashMap<>();
public Object getBean(String name) throws BeanCreationException {
if (beans.containsKey(name)) {
return beans.get(name);
} else {
throw new BeanCreationException("Bean not found: " + name);
}
}
// 新增方法用於向 ConcurrentHashMap 中新增 bean
public void registerBean(String name, Object bean) {
beans.put(name, bean);
}
// 新增方法用於從 ConcurrentHashMap 中移除 bean
public void removeBean(String name) {
beans.remove(name);
}
}
編寫了一個單元測試並且測試透過。
package io.yulin.learn.spring.s100;
import org.junit.jupiter.api.Test;
import static org.assertj.core.api.Assertions.assertThat;
/**
* 自定義getBean實現的單元測試
* @author nine
*/
public class TestDemoImplGetBean {
@Test
public void testDemoImplGetBean() {
BeanFactoryDemoImpl beanFactoryDemo = new BeanFactoryDemoImpl();
beanFactoryDemo.registerBean("test", new ComponentDemo1());
Object test = beanFactoryDemo.getBean("test");
boolean instanceCheck = test instanceof ComponentDemo1;
assertThat(instanceCheck).isTrue();
((ComponentDemo1) test).test();
}
}
實際實現介面org.springframework.beans.factory.BeanFactory#getBean
要複雜的多。需要實現上文提到的全部功能。以下是一個Spring6框架中的實現。
/**
* 從IoC容器中獲取指定名稱的bean例項。
*
* @param name 要獲取的bean的名稱,可能包含工廠Bean引用字首
* @return 根據名稱建立或獲取的bean例項
* @throws BeansException 若在獲取、建立或初始化bean過程中發生異常時丟擲
*/
public Object getBean(String name) throws BeansException {
// 首先處理並轉換傳入的bean名稱(例如移除FactoryBean字首等)
String beanName = BeanFactoryUtils.transformedBeanName(name);
// 從容器內部儲存的bean定義集合中查詢指定名稱的bean例項
Object bean = this.beans.get(beanName);
// 如果未找到該bean,則丟擲 NoSuchBeanDefinitionException 異常
if (bean == null) {
throw new NoSuchBeanDefinitionException(
beanName,
"Defined beans are [" + StringUtils.collectionToCommaDelimitedString(this.beans.keySet()) + "]");
}
// 檢查是否是工廠Bean引用,並確保它實際上是FactoryBean型別
// 若不是工廠Bean卻被嘗試作為工廠Bean引用,丟擲異常
if (BeanFactoryUtils.isFactoryDereference(name) && !(bean instanceof FactoryBean)) {
throw new BeanIsNotAFactoryException(beanName, bean.getClass());
}
// 如果當前bean是一個FactoryBean,並且呼叫者沒有顯式要求獲得FactoryBean本身,
// 則透過呼叫FactoryBean.getObject()方法來獲取由其生成的bean例項
if (bean instanceof FactoryBean<?> factoryBean && !BeanFactoryUtils.isFactoryDereference(name)) {
try {
// 獲取FactoryBean所生產的物件例項
Object exposedObject = factoryBean.getObject();
// 如果FactoryBean返回null,則丟擲異常
if (exposedObject == null) {
throw new BeanCreationException(beanName, "FactoryBean exposed null object");
}
// 返回由FactoryBean生成的物件例項
return exposedObject;
}
catch (Exception ex) {
// 若在呼叫FactoryBean.getObject()過程中出現任何異常,包裝成BeanCreationException丟擲
throw new BeanCreationException(beanName, "FactoryBean threw exception on object creation", ex);
}
}
else {
// 如果bean不是FactoryBean,或者請求的是FactoryBean自身,直接返回bean例項
return bean;
}
}
透過上述整個過程的實戰和原始碼分析,可以對Spring6的核心功能有一個更加清晰的瞭解和感悟。
Spring6框架需要了解哪些內容
透過Spring6框架的官方文件可以找到大致的框架。
Spring的文件一直以來都是非常清晰的。尤其是在無數次迭代之後更能體現Spring的設計之美。
其中Spring6框架大致分為6個大類,每個大類對應很多小類和技術細節。
這6個大類分別是:
CORE 核心技術:
Spring 框架的核心技術包括 IoC 容器、依賴注入、AOP、事件機制等。IoC 容器提供了物件的生命週期管理和依賴關係的維護,依賴注入透過配置或註解的方式實現物件之間的解耦,AOP 支援面向切面程式設計,而事件機制則允許應用程式中的元件之間進行事件通知和處理。
TESTING 測試:
Spring 框架提供了豐富的測試支援,包括單元測試、整合測試、模擬物件、測試資料準備等功能。透過 Spring 的測試框架,開發人員可以方便地編寫和執行各種型別的測試,並且能夠與 Spring 應用程式的其他部分無縫整合。
Data Access 資料訪問:
Spring 的資料訪問層提供了對 JDBC、ORM 框架(如 Hibernate、MyBatis)、NoSQL 資料庫(如 MongoDB、Redis)等的支援,同時也提供了事務管理、資料來源管理、資料訪問模板等功能,簡化了資料訪問層的開發。
Web Servet:
在 Web Servlet 領域,Spring 框架提供了對 Servlet、JSP、WebSocket、Web MVC 等的支援,包括處理請求、檢視解析、控制器、攔截器等功能,同時也提供了 RESTful Web 服務的開發支援。
Web Reactive:
Spring Web Reactive 則是針對響應式程式設計模型提供支援,包括非阻塞 I/O、非同步處理、反應式流等特性,同時提供了 WebFlux、WebClient、WebSocket、RSocket 等元件,用於構建響應式的 Web 應用程式。
Integration 整合:
Spring 提供了對 REST Clients、JMS、JCA、JMX、Email、Tasks、Scheduling、Caching、Observability、JVM Checkpoint Restore 等整合功能的支援,使得應用程式可以方便地與外部系統整合,並且具備了更好的可觀察性和可擴充套件性。
CORE 核心技術:
- IoC Container(控制反轉容器):Spring 的核心功能之一,透過 IoC 容器管理和組織應用程式中的物件及其依賴關係。它負責例項化、配置和管理這些物件,以及處理它們之間的依賴注入。
- Events(事件):Spring 的事件機制允許應用程式中的元件傳送和接收事件。這種松耦合的通訊方式可以用於實現模組之間的互動和觸發非同步操作。
- Resources(資源載入):Spring 提供了統一的資源載入機制,使得應用程式可以輕鬆地訪問不同型別的資源,如檔案、類路徑資源、URL 等。
- i18n(國際化):Spring 提供了國際化支援,使得應用程式能夠根據使用者的語言環境展示不同的文字訊息和格式化資料。
- Validation(驗證):Spring 的驗證框架提供了一種方便的方式來驗證表單資料和其他輸入資料的有效性。它支援各種驗證規則和自定義驗證器。
- Data Binding(資料繫結):Spring 提供了強大的資料繫結機制,可以將請求引數、表單資料等與 Java 物件進行繫結,簡化了資料傳輸和處理的過程。
- Type Conversion(型別轉換):Spring 的型別轉換機制可以自動將一種型別的值轉換為另一種型別,使得應用程式在處理不同資料型別時更加靈活和方便。
- SpEL(Spring 表示式語言):SpEL 是一種強大的表示式語言,允許在執行時對物件圖進行查詢和操作。它可以在配置檔案中使用,也可以在執行時透過程式設計方式使用。
- AOP(面向切面程式設計):Spring 的 AOP 支援透過代理機制實現橫切關注點(如日誌、事務管理等)的模組化開發。它透過切面、連線點和通知來實現對目標物件的增強。
- AOT(Ahead of Time Compilation,預編譯):AOT 是 Spring 框架的一個最新功能,透過提前將 Spring 應用程式的位元組碼編譯成本地機器程式碼,以提高應用程式的效能和啟動速度。
以上是關於 Spring 框架中 CORE 核心技術的功能說明,它們共同構成了 Spring 的基礎,併為開發者提供了很多有用的特性和工具。
TESTING 測試:
- Mock Objects:Spring 允許建立和使用模擬物件(Mock Objects),這些物件可以替代真實的物件進行單元測試,以便更容易地隔離被測試的元件。
- TestContext Framework:Spring 的 TestContext 框架提供了一種統一的方式來載入和管理應用程式上下文,使得在測試中可以方便地使用 Spring 容器和其他功能。
- Spring MVC Test:Spring MVC Test 提供了對 Spring MVC 應用程式進行整合測試的支援,可以模擬 HTTP 請求和響應,驗證控制器的行為和檢視的渲染結果。
- WebTestClient:WebTestClient 是 Spring WebFlux 模組提供的用於測試 WebFlux 應用程式的客戶端工具,可以進行非同步、非阻塞的 Web 應用程式測試。
這些功能使得在 Spring 框架中進行單元測試、整合測試和端到端測試變得更加簡單和高效。
Data Access 資料訪問:
- Transactions(事務):Spring 框架提供了強大的事務管理功能,支援宣告式事務和程式設計式事務,以確保資料庫操作的一致性和可靠性。
- DAO Support(DAO 支援):Spring 提供了對資料訪問物件(Data Access Object)的支援,透過封裝資料庫訪問邏輯到 DAO 中,簡化了資料訪問層的開發和維護。
- JDBC:Spring 對 JDBC(Java Database Connectivity)提供了高度整合的支援,透過 JDBC Template 等類,簡化了資料庫訪問的程式碼編寫,並處理了資源管理和異常處理等細節。
- R2DBC:Spring 透過支援 R2DBC(Reactive Relational Database Connectivity),使得在響應式應用程式中進行關係型資料庫的非同步操作更加便捷。
- O/R Mapping(物件關係對映):Spring 提供了強大的物件關係對映支援,透過框架內建的 ORM(Object-Relational Mapping)工具,將資料庫表的記錄對映為 Java 物件,方便進行物件的持久化與操作。
- XML Marshalling(XML 序列化):Spring 提供了對 XML 資料的序列化和反序列化支援,可以方便地將 Java 物件轉換為 XML 格式或將 XML 轉換為 Java 物件,用於處理 XML 資料的讀寫操作。
這些資料訪問功能使得在 Spring 框架中進行資料持久化和操作變得更加簡單、高效和可靠,提升了開發人員的生產力。
Web Servet:
- Spring MVC:Spring MVC 是 Spring 框架中的 Web 模組,提供了基於模型-檢視-控制器(MVC)設計模式的 Web 應用程式開發支援,用於構建靈活、可擴充套件的 Web 應用程式。
- WebSocket:Spring 框架支援 WebSocket 技術,可以實現全雙工的通訊方式,適用於需要實時性和互動性的 Web 應用程式開發。
- SockJS:SockJS 是一個 JavaScript 庫,Spring 框架透過對 SockJS 的支援,可以實現在不同瀏覽器上對 WebSocket 的相容性,確保更廣泛的客戶端支援。
- STOMP Messaging:STOMP(Simple Text Oriented Messaging Protocol)是一種簡單文字導向的訊息協議,在 Spring 框架中提供了對 STOMP 協議的支援,用於實現基於訊息的應用程式通訊。
這些功能為開發者提供了豐富的選擇,使得在 Spring 框架中開發 Web 應用程式更加便捷、高效,並支援現代化的 Web 開發需求。
Web Reactive:
- Spring WebFlux:Spring WebFlux 是 Spring 框架中的響應式程式設計模組,基於 Reactor 庫提供了一種非阻塞的、非同步的程式設計模型,用於構建高效能、可擴充套件的響應式 Web 應用程式。
- WebClient:Spring WebClient 是一個非阻塞的、非同步的 HTTP 客戶端,用於在 WebFlux 應用程式中進行遠端服務呼叫,支援響應式流處理和各種協議。
- WebSocket:Spring 框架透過對 WebSocket 技術的支援,可以實現全雙工的通訊方式,用於構建實時的、互動性強的 Web 應用程式。
- RSocket:RSocket 是一種可擴充套件的、非同步的、多種傳輸協議的訊息通訊協議,Spring 框架提供了對 RSocket 的支援,用於構建分散式系統中的可靠通訊。
這些功能使得在 Spring 框架中開發響應式的 Web 應用程式更加便捷、高效,並能夠處理大量併發請求和實時資料互動。它們適用於需要高效能、可伸縮性和實時性的現代 Web 應用程式開發。
Integration 整合:
- REST Clients:Spring 框架提供了對 RESTful 服務的客戶端支援,可以方便地進行 REST API 的呼叫和互動,實現與其他服務的整合。
- JMS:Spring 對 Java Message Service(JMS)提供了整合支援,用於在分散式系統中進行非同步訊息傳遞,實現應用程式之間的解耦和通訊。
- JCA:Spring 框架支援 Java Connector Architecture(JCA),用於與企業資訊系統(如 ERP、CRM 等)進行連線和整合,實現企業級應用程式的互操作性。
- JMX:Spring 提供了對 Java Management Extensions(JMX)的支援,用於監控和管理應用程式的執行時狀態,實現應用程式的監控和遠端管理。
- Email:Spring 框架提供了傳送和接收電子郵件的功能支援,簡化了電子郵件服務的整合和操作,用於實現郵件通知和互動功能。
- Tasks:Spring 支援非同步任務執行,透過 TaskExecutor 介面和相關類,可以實現非同步任務的排程和執行,提升應用程式的效能和響應速度。
- Scheduling:Spring 提供了任務排程的功能支援,可以透過註解或配置的方式實現定時任務的執行,用於處理週期性任務和定時作業。
- Caching:Spring 框架提供了對快取的抽象和支援,可以透過快取註解實現方法級別的快取,提升應用程式的效能和響應速度。
- Observability:Spring 支援應用程式的可觀察性,包括日誌記錄、指標監控、跟蹤和分析等功能,幫助開發者瞭解應用程式的執行狀態並快速定位問題。
- JVM Checkpoint Restore:Spring 支援 JVM 的檢查點恢復,可以在應用程式異常時儲存當前狀態,並在恢復時恢復到之前的狀態,減少資料丟失和影響範圍。
這些功能使得在 Spring 框架中整合其他系統或者擴充套件變的十分容易。
總結
透過原始碼,我們可以看到Spring6框架的核心和組成。
Spring框架的核心技術包括IoC容器、依賴注入、AOP等,為應用程式提供物件生命週期管理、解耦和麵向切面程式設計等功能;同時,它擁有豐富的測試支援,簡化了單元測試、整合測試的編寫與執行;在資料訪問層面,Spring全面支援多種資料庫技術,並提供了事務管理、模板類等工具;Web層方面,Spring不僅涵蓋了Servlet/JSP/Web MVC的傳統Web開發,還支援響應式程式設計模型如WebFlux;此外,Spring還具備強大的整合能力,方便與外部系統互動並實現可觀察性和擴充套件性。
在“Spring6全攻略”中,後續將透過如下的流程來完成整個Spring6的攻略。
- 編寫demo使用api
- 閱讀api的原始碼和實現
- 編寫api的實現
- 編寫單元測試
參考資料
影片
- 黑馬程式設計師Spring影片教程,深度講解spring5底層原理https://www.bilibili.com/video/BV1P44y1N7QG
文件
- Spring 6 javadoc https://docs.spring.io/spring-framework/docs/6.1.x/javadoc-api/
- Spring 6 參考文件 https://docs.spring.io/spring-framework/docs/6.1.x/reference/html/
- Spring 6 原始碼 https://github.com/spring-projects/spring-framework
關於作者
來自一線全棧程式設計師nine的八年探索與實踐,持續迭代中。歡迎關注公眾號“雨林尋北”或新增個人衛星codetrend(備註技術)。