Spring5 源碼編譯:環境搭建+測試


目錄

1. 搭建gradle環境和下載源碼

1.1. 下載源碼

1.2. 搭建gradle環境

1.3. 配置gradle的默認鏡像

2. 預編譯spring-oxm

2.1. 配置build.gradle

2.2. 配置settings.gradle

2.3. 預編譯

2.4. 結果

3. 構建源碼

3.1. 導入Idea

3.2. 開始構建

3.3. 錯誤

4. 測試

4.1. 搭建測試Module

4.2 踩的坑

4.2.1. 找不到符號 coroutinesutils

4.2.2  No tasks available

4.2.3. 引入cglib時缺失jar包

4.2.4. 找不到org.springframework.aop.target

參考


1. 搭建gradle環境和下載源碼

1.1. 下載源碼

上github搜 spring-framework,找到star數最多的,用git clone或者下載.zip文件的形式都可以,把源碼下載下來。

打開源碼文件夾,在 gradle -> wrapper -> gradle-wrapper.properties 文件中,找到如下屬性配置:

distributionUrl=https\://services.gradle.org/distributions/gradle-5.6.4-bin.zip

根據上面的gradle版本號,去 https://services.gradle.org/distributions/ 下載對應版本的gradle文件。

bin結尾的是gradle執行環境,src結尾的是源碼文件,all結尾的包括bin和src。

 

下載gradle文件后,一份用於搭建gradle環境,另一份拷到源碼文件夾的gradle -> wrapper 目錄下,

然后將上述屬性配置改為:

distributionUrl=gradle-5.6.4-bin.zip
這樣在使用gradlew進行預編譯時,就不用再去下載執行文件了。

 

1.2. 搭建gradle環境

解壓你的gradle文件到自定義的安裝目錄下,然后用如下命令創建一個本地倉庫:

mkdir .gradle

本地倉庫的位置自己定。

然后配置環境變量:

>1 新建變量:GRADLE_HOME,就是你的gradle的安裝目錄,比如:D:\DevTools\gradle\gradle-5.6

>2 新建變量:GRADLE_USER_HOME,這個是你的gradle的本地倉庫目錄,比如:D:\DevTools\gradle\.gradle

>3 在Path中,添加:%GRADLE_HOME%\bin

>4 如果有maven倉庫,並且想在構建項目時使用maven倉庫,配置的maven地址必須是M2_HOME(不能是 MAVEN_HOME),否則gradle找不到你的maven配置。

打開命令行,輸入gradle -v,如果彈出提示,則配置成功。

 

1.3. 配置gradle的默認鏡像

在你的gradle倉庫中(.gradle目錄下),新建一個init.gradle文件,其中寫入:

  1. allprojects{
  2. repositories {
  3. mavenLocal()
  4. maven { url "https://maven.aliyun.com/nexus/content/groups/public/"}
  5. def ALIYUN_REPOSITORY_URL = 'https://maven.aliyun.com/repository/public/'
  6. def ALIYUN_JCENTER_URL = 'https://maven.aliyun.com/repository/jcenter/'
  7. def ALIYUN_GOOGLE_URL = 'https://maven.aliyun.com/repository/google/'
  8. def ALIYUN_GRADLE_PLUGIN_URL = 'https://maven.aliyun.com/repository/gradle-plugin/'
  9. all { ArtifactRepository repo ->
  10. if(repo instanceof MavenArtifactRepository){
  11. def url = repo.url.toString()
  12. if (url.startsWith('https://repo1.maven.org/maven2/')) {
  13. project.logger.lifecycle "Repository ${repo.url} replaced by $ALIYUN_REPOSITORY_URL."
  14. remove repo
  15. }
  16. if (url.startsWith('https://jcenter.bintray.com/')) {
  17. project.logger.lifecycle "Repository ${repo.url} replaced by $ALIYUN_JCENTER_URL."
  18. remove repo
  19. }
  20. if (url.startsWith('https://dl.google.com/dl/android/maven2/')) {
  21. project.logger.lifecycle "Repository ${repo.url} replaced by $ALIYUN_GOOGLE_URL."
  22. remove repo
  23. }
  24. if (url.startsWith('https://plugins.gradle.org/m2/')) {
  25. project.logger.lifecycle "Repository ${repo.url} replaced by $ALIYUN_GRADLE_PLUGIN_URL."
  26. remove repo
  27. }
  28. }
  29. }
  30. maven { url ALIYUN_REPOSITORY_URL }
  31. maven { url ALIYUN_JCENTER_URL }
  32. maven { url ALIYUN_GOOGLE_URL }
  33. maven { url ALIYUN_GRADLE_PLUGIN_URL }
  34. }
  35. }

 

2. 預編譯spring-oxm

2.1. 配置build.gradle

我下載的源碼目錄為:spring-framework-study,那么進入該目錄你可以看到一個import-into-idea.md,

打開它可以看到引入idea前需要做的工作:

 

  1. 1. Precompile `spring-oxm` with `./gradlew :spring-oxm:compileTestJava`
  2. 2. Import into IntelliJ (File -> New -> Project from Existing Sources -> Navigate to directory -> Select build.gradle)
  3. 3. When prompted exclude the `spring-aspects` module (or after the import via File-> Project Structure -> Modules)
  4. 4. Code away

解釋一下:

1 你需要預編譯spring-oxm;2 選擇build.gradle導入idea;3 將spring-aspects從項目結構中排除

 

打開源碼主目錄下的build.gradle,先配置鏡像:

  1. buildscript {
  2. repositories {
  3. maven { url 'https://maven.aliyun.com/nexus/content/groups/public/' }
  4. maven { url 'https://maven.aliyun.com/nexus/content/repositories/jcenter' }
  5. google()
  6. jcenter()
  7. }
  8. }
  9. plugins {
  10. ...
  11. }
  12. allprojects {
  13. repositories {
  14. maven { url 'https://maven.aliyun.com/nexus/content/groups/public/' }
  15. maven { url 'https://maven.aliyun.com/nexus/content/repositories/jcenter' }
  16. google()
  17. jcenter()
  18. }
  19. }
 

注意:gradle配置時,buildscript和plugins必須在第一行配置。如果有buildscrpit,那么它必須在第一行。

(plugins中的插件后面可能帶有 apply false,將 apply false 全部刪除,即應用所有插件)

 

找到 build.gradle中的 configure(allprojects) 中的 repositories,配置如下:

  1. repositories {
  2. mavenLocal()
  3. //mavenCentral()
  4. maven { url "https://repo.springsource.org/plugins-release" }
  5. maven { url "https://repo.spring.io/libs-spring-framework-build" }
  6. }

注意:所有的倉庫配置url前的http都要加上s:"https://xxx",要不然 > Task :checkstyleNohttp 任務會報錯。

 

2.2. 配置settings.gradle

將 include "spring-aspects" 注釋掉:

//include "spring-aspects"

 

2.3. 預編譯

在項目目錄下,運行命令行命令(windows下):

gradlew.bat :spring-oxm:compileTestJava

注意:網上說的中間加 cleanIdea,此時(第一次編譯)並不管用,會報錯。

 

2.4. 結果

如果結果中有 BUILD SUCCESSFUL 字樣就明預編譯成功,

可能出現的錯誤:

  1. Build scan background action failed.
  2. org.gradle.process.internal.ExecException: Process 'command 'git'' finished with non-zero exit value 128

git的問題,暫時還不知道如何解決,不影響構建。

 

3. 構建源碼

3.1. 導入Idea

Idea中,import project ,然后選擇源碼主目錄下的build.gradle,然后導入。

配置本地的gradle:

 

導入后,會進行configure build,過程很漫長。

還是找BUILD SUCCESSFUL字樣。

 

3.2. 開始構建

在gradle選項卡中,找到build命令,雙擊開始構建

 

又是一個漫長的過程。

構建完畢后,找BUILD SUCCESSFUL字樣。

 

3.3. 錯誤

1. 如果構建過程中報錯,采用如下辦法:

一開始嘗試直接構建Spring源碼,在構建的過程中會報錯,通過修改docs.gradle 腳本,注釋 dokka 和 asciidoctor

docs.gradle文件在gradle目錄下。

 

2. 報錯:validUsage()

at org.junit.platform.commons.util.ReflectionUtils.invokeMethod(ReflectionUtils.java:686) 
注釋:spring-test.gradle中這行代碼 
  1. // useJUnitPlatform {
  2. // includeEngines 'junit-jupiter'
  3. // excludeTags 'failing-test-case'
  4. // }

 

4. 測試

4.1. 搭建測試Module

在主目錄下新建Module,然后選擇gradle -> jar,ArtifactId和groupId自己填。

項目結構如下:

 

在新建的Module中的build.gradle,添加如下依賴:

  1. dependencies {
  2. compile( project(":spring-context"))
  3. compile( project(":spring-instrument"))
  4. compile 'junit:junit:4.12'
  5. compile group: 'org.hamcrest', name: 'hamcrest-core', version: '1.3'
  6. }

我的TestApp是我的主程序入口,在其中用到了junit,所以需要在添加依賴時,使用compile,而不是testCompile.

 

junit依賴hamcrest-core,所以也要引入后者。

 

  1. public class TestApp {
  2. @Test
  3. public void MyTest() {
  4. ClassPathResource res = new ClassPathResource("spring-config.xml");
  5. DefaultListableBeanFactory factory = new DefaultListableBeanFactory();
  6. XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(factory);
  7. reader.loadBeanDefinitions(res);
  8. System.out.println(factory.getBean(TestBean.class).getName());
  9. }
  10. }
TestBean.java:
  1. public class TestBean {
  2. private String name = "hehe";
  3. public TestBean(String name) {
  4. this.name = name;
  5. }
  6. public TestBean() {
  7. }
  8. @Override
  9. public String toString() {
  10. return "TestBean{" +
  11. "name='" + name + '\'' +
  12. '}';
  13. }
  14. public String getName() {
  15. return name;
  16. }
  17. public void setName(String name) {
  18. this.name = name;
  19. }
  20. }
spring-config.xml:
  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <beans xmlns="http://www.springframework.org/schema/beans"
  3. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  4. xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
  5. <bean id="testBean" class="com.gaojx.spring.hello.TestBean"/>
  6. </beans>
 

4.2 踩的坑

4.2.1. 找不到符號 coroutinesutils

build時,提示找不到CoroutinesUtils,解決方法:

在spring-core -> kotlin-coroutines -> src -> main -> kotlin -> org.framework.core -> CoroutinesUtils.kt 上,

右鍵,Build Module 'spring-kotlin-coroutines.main':

 

4.2.2  No tasks available

運行Run @Test時,提示 No tasks available,

在 File -> settings -> gradle,將下列選項替換成 IntelliJ IDEA:

 

4.2.3. 引入cglib時缺失jar包

構建時,缺失spring-cglib-repack和spring-objenesis-repack,(在spring-core模塊下,提示上述兩個jar包缺失)。

在gradle選項卡中,spring主項目下,Tasks -> other,雙擊 cglibRepackJar 和 objenesisRepackJar:

 

此時在項目的settings.gradle中,就可以打開 include "spring-aspects" 了。

 

4.2.4. 找不到org.springframework.aop.target

build時提示:程序包org.springframework.aop.target 不存在。

這個是最最最最最最弱智的失誤了!!!!

因為我在Idea的設置中,將名稱為target的目錄給隱藏了:

 

所以程序一直提示找不到aop下面的target包。

 

 

最終測試成功:

 

 from:https://blog.csdn.net/guyexiangyun/article/details/106527732

參考

https://www.cnblogs.com/yjteilz/p/6228833.html

https://blog.csdn.net/Pphu_1220/article/details/104528492

https://www.cnblogs.com/coderxiaohei/p/11628378.html

https://www.cnblogs.com/java-chen-hao/p/11046190.html

https://www.cnblogs.com/coderdxj/p/11437873.html

https://blog.csdn.net/baomw/article/details/83956300

https://blog.csdn.net/yzpbright/article/details/53492458

 

最詳細的Spring核心IOC的源碼分析(每次看都有不同的感悟)

原文轉自   : https://javadoop.com/post/spring-ioc

Spring 最重要的概念是 IOC 和 AOP,本篇文章其實就是要帶領大家來分析下 Spring 的 IOC 容器。既然大家平時都要用到 Spring,怎么可以不好好了解 Spring 呢?閱讀本文並不能讓你成為 Spring 專家,不過一定有助於大家理解 Spring 的很多概念,幫助大家排查應用中和 Spring 相關的一些問題。

閱讀建議:讀者至少需要知道怎么配置 Spring,了解 Spring 中的各種概念,少部分內容我還假設讀者使用過 SpringMVC。本文要說的 IOC 總體來說有兩處地方最重要,一個是創建 Bean 容器,一個是初始化 Bean,如果讀者覺得一次性看完本文壓力有點大,那么可以按這個思路分兩次消化。讀者不一定對 Spring 容器的源碼感興趣,也許附錄部分介紹的知識對讀者有些許作用。

我采用的源碼版本是 4.3.11.RELEASE,算是 5.0.x 前比較新的版本了。為了降低難度,本文所說的所有的內容都是基於 xml 的配置的方式,實際使用已經很少人這么做了,至少不是純 xml 配置,不過從理解源碼的角度來看用這種方式來說無疑是最合適的。如果讀者對注解方式的源碼感興趣,也許等我有時間的時候可以寫篇文章介紹介紹。

我希望能將此文寫成一篇 Spring IOC 源碼分析的好文章,希望通過本文可以讓讀者不懼怕閱讀 Spring 源碼。

為了保持文章的嚴謹性,如果讀者發現我哪里說錯了請一定不吝指出,非常希望可以聽到讀者的聲音。

目錄

引言

先看下最基本的啟動 Spring 容器的例子:

  1. public static void main(String[] args) {
  2. ApplicationContext context = new ClassPathXmlApplicationContext("classpath:applicationfile.xml");
  3. }

以上代碼就可以利用配置文件來啟動一個 Spring 容器了,請使用 maven 的小伙伴直接在 dependencies 中加上以下依賴即可,我比較反對那些不知道要添加什么依賴,然后把 Spring 的所有相關的東西都加進來的方式。

  1. <dependency>
  2. <groupId>org.springframework</groupId>
  3. <artifactId>spring-context</artifactId>
  4. <version>4.3.11.RELEASE</version>
  5. </dependency>

spring-context 會自動將 spring-core、spring-beans、spring-aop、spring-expression 這幾個基礎 jar 包帶進來。

多說一句,很多開發者入門就直接接觸的 SpringMVC,對 Spring 其實不是很了解,Spring 是漸進式的工具,並不具有很強的侵入性,它的模塊也划分得很合理,即使你的應用不是 web 應用,或者之前完全沒有使用到 Spring,而你就想用 Spring 的依賴注入這個功能,其實完全是可以的,它的引入不會對其他的組件產生沖突。

廢話說完,我們繼續。ApplicationContext context = new ClassPathXmlApplicationContext(...) 其實很好理解,從名字上就可以猜出一二,就是在 ClassPath 中尋找 xml 配置文件,根據 xml 文件內容來構建 ApplicationContext。當然,除了 ClassPathXmlApplicationContext 以外,我們也還有其他構建 ApplicationContext 的方案可供選擇,我們先來看看大體的繼承結構是怎么樣的:

1

讀者可以大致看一下類名,源碼分析的時候不至於找不着看哪個類,因為 Spring 為了適應各種使用場景,提供的各個接口都可能有很多的實現類。對於我們來說,就是揪着一個完整的分支看完。當然,讀本文的時候讀者也不必太擔心,每個代碼塊分析的時候,我都會告訴讀者我們在說哪個類第幾行。

我們可以看到,ClassPathXmlApplicationContext 兜兜轉轉了好久才到 ApplicationContext 接口,同樣的,我們也可以使用綠顏色的 FileSystemXmlApplicationContext 和 AnnotationConfigApplicationContext 這兩個類。

FileSystemXmlApplicationContext 的構造函數需要一個 xml 配置文件在系統中的路徑,其他和 ClassPathXmlApplicationContext 基本上一樣。

AnnotationConfigApplicationContext 是基於注解來使用的,它不需要配置文件,采用 java 配置類和各種注解來配置,是比較簡單的方式,也是大勢所趨吧。

不過本文旨在幫助大家理解整個構建流程,所以決定使用 ClassPathXmlApplicationContext 進行分析。

我們先來一個簡單的例子來看看怎么實例化 ApplicationContext。

首先,定義一個接口:

  1. public interface MessageService {
  2. String getMessage();
  3. }

定義接口實現類:

  1. public class MessageServiceImpl implements MessageService {
  2.  
  3. public String getMessage() {
  4. return "hello world";
  5. }
  6. }

接下來,我們在 resources 目錄新建一個配置文件,文件名隨意,通常叫 application.xml 或 application-xxx.xml就可以了:

  1. <?xml version="1.0" encoding="UTF-8" ?>
  2. <beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  3. xmlns="http://www.springframework.org/schema/beans"
  4. xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd" default-autowire="byName">
  5.  
  6. <bean id="messageService" class="com.javadoop.example.MessageServiceImpl"/>
  7. </beans>

這樣,我們就可以跑起來了:

  1. public class App {
  2. public static void main(String[] args) {
  3. // 用我們的配置文件來啟動一個 ApplicationContext
  4. ApplicationContext context = new ClassPathXmlApplicationContext("classpath:application.xml");
  5.  
  6. System. out.println("context 啟動成功");
  7.  
  8. // 從 context 中取出我們的 Bean,而不是用 new MessageServiceImpl() 這種方式
  9. MessageService messageService = context.getBean(MessageService.class);
  10. // 這句將輸出: hello world
  11. System. out.println(messageService.getMessage());
  12. }
  13. }

以上例子很簡單,不過也夠引出本文的主題了,就是怎么樣通過配置文件來啟動 Spring 的 ApplicationContext?也就是我們今天要分析的 IOC 的核心了。ApplicationContext 啟動過程中,會負責創建實例 Bean,往各個 Bean 中注入依賴等。

BeanFactory 簡介

初學者可別以為我之前說那么多和 BeanFactory 無關,前面說的 ApplicationContext 其實就是一個 BeanFactory。我們來看下和 BeanFactory 接口相關的主要的繼承結構:

2

我想,大家看完這個圖以后,可能就不是很開心了。ApplicationContext 往下的繼承結構前面一張圖說過了,這里就不重復了。這張圖呢,背下來肯定是不需要的,有幾個重點和大家說明下就好。

  1. ApplicationContext 繼承了 ListableBeanFactory,這個 Listable 的意思就是,通過這個接口,我們可以獲取多個 Bean,最頂層 BeanFactory 接口的方法都是獲取單個 Bean 的。
  2. ApplicationContext 繼承了 HierarchicalBeanFactory,Hierarchical 單詞本身已經能說明問題了,也就是說我們可以在應用中起多個 BeanFactory,然后可以將各個 BeanFactory 設置為父子關系。
  3. AutowireCapableBeanFactory 這個名字中的 Autowire 大家都非常熟悉,它就是用來自動裝配 Bean 用的,但是仔細看上圖,ApplicationContext 並沒有繼承它,不過不用擔心,不使用繼承,不代表不可以使用組合,如果你看到 ApplicationContext 接口定義中的最后一個方法 getAutowireCapableBeanFactory() 就知道了。
  4. ConfigurableListableBeanFactory 也是一個特殊的接口,看圖,特殊之處在於它繼承了第二層所有的三個接口,而 ApplicationContext 沒有。這點之后會用到。
  5. 請先不用花時間在其他的接口和類上,先理解我說的這幾點就可以了。

然后,請讀者打開編輯器,翻一下 BeanFactory、ListableBeanFactory、HierarchicalBeanFactory、AutowireCapableBeanFactory、ApplicationContext 這幾個接口的代碼,大概看一下各個接口中的方法,大家心里要有底,限於篇幅,我就不貼代碼介紹了。

啟動過程分析

下面將會是冗長的代碼分析,請讀者先喝個水。記住,一定要在電腦中打開源碼,不然純看是很累的。

第一步,我們肯定要從 ClassPathXmlApplicationContext 的構造方法說起。

  1. public class ClassPathXmlApplicationContext extends AbstractXmlApplicationContext {
  2. private Resource[] configResources;
  3.  
  4. // 如果已經有 ApplicationContext 並需要配置成父子關系,那么調用這個構造方法
  5. public ClassPathXmlApplicationContext(ApplicationContext parent) {
  6. super(parent);
  7. }
  8. ...
  9. public ClassPathXmlApplicationContext(String[] configLocations, boolean refresh, ApplicationContext parent)
  10. throws BeansException {
  11.  
  12. super(parent);
  13. // 根據提供的路徑,處理成配置文件數組(以分號、逗號、空格、tab、換行符分割)
  14. setConfigLocations(configLocations);
  15. if (refresh) {
  16. refresh(); // 核心方法
  17. }
  18. }
  19. ...
  20. }

接下來,就是 refresh(),這里簡單說下為什么是 refresh(),而不是 init() 這種名字的方法。因為 ApplicationContext 建立起來以后,其實我們是可以通過調用 refresh() 這個方法重建的,這樣會將原來的 ApplicationContext 銷毀,然后再重新執行一次初始化操作。

往下看,refresh() 方法里面調用了那么多方法,就知道肯定不簡單了,請讀者先看個大概,細節之后會詳細說。

  1. @Override
  2. public void refresh() throws BeansException, IllegalStateException {
  3. // 來個鎖,不然 refresh() 還沒結束,你又來個啟動或銷毀容器的操作,那不就亂套了嘛
  4. synchronized (this.startupShutdownMonitor) {
  5.  
  6. // 准備工作,記錄下容器的啟動時間、標記“已啟動”狀態、處理配置文件中的占位符
  7. prepareRefresh();
  8.  
  9. // 這步比較關鍵,這步完成后,配置文件就會解析成一個個 Bean 定義,注冊到 BeanFactory 中,
  10. // 當然,這里說的 Bean 還沒有初始化,只是配置信息都提取出來了,
  11. // 注冊也只是將這些信息都保存到了注冊中心(說到底核心是一個 beanName-> beanDefinition 的 map)
  12. ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
  13.  
  14. // 設置 BeanFactory 的類加載器,添加幾個 BeanPostProcessor,手動注冊幾個特殊的 bean
  15. // 這塊待會會展開說
  16. prepareBeanFactory(beanFactory);
  17.  
  18. try {
  19. // 【這里需要知道 BeanFactoryPostProcessor 這個知識點,Bean 如果實現了此接口,
  20. // 那么在容器初始化以后,Spring 會負責調用里面的 postProcessBeanFactory 方法。】
  21.  
  22. // 這里是提供給子類的擴展點,到這里的時候,所有的 Bean 都加載、注冊完成了,但是都還沒有初始化
  23. // 具體的子類可以在這步的時候添加一些特殊的 BeanFactoryPostProcessor 的實現類或做點什么事
  24. postProcessBeanFactory(beanFactory);
  25. // 調用 BeanFactoryPostProcessor 各個實現類的 postProcessBeanFactory(factory) 方法
  26. invokeBeanFactoryPostProcessors(beanFactory);
  27.  
  28. // 注冊 BeanPostProcessor 的實現類,注意看和 BeanFactoryPostProcessor 的區別
  29. // 此接口兩個方法: postProcessBeforeInitialization 和 postProcessAfterInitialization
  30. // 兩個方法分別在 Bean 初始化之前和初始化之后得到執行。注意,到這里 Bean 還沒初始化
  31. registerBeanPostProcessors(beanFactory);
  32.  
  33. // 初始化當前 ApplicationContext 的 MessageSource,國際化這里就不展開說了,不然沒完沒了了
  34. initMessageSource();
  35.  
  36. // 初始化當前 ApplicationContext 的事件廣播器,這里也不展開了
  37. initApplicationEventMulticaster();
  38.  
  39. // 從方法名就可以知道,典型的模板方法(鈎子方法),
  40. // 具體的子類可以在這里初始化一些特殊的 Bean(在初始化 singleton beans 之前)
  41. onRefresh();
  42.  
  43. // 注冊事件監聽器,監聽器需要實現 ApplicationListener 接口。這也不是我們的重點,過
  44. registerListeners();
  45.  
  46. // 重點,重點,重點
  47. // 初始化所有的 singleton beans
  48. //(lazy-init 的除外)
  49. finishBeanFactoryInitialization(beanFactory);
  50.  
  51. // 最后,廣播事件,ApplicationContext 初始化完成
  52. finishRefresh();
  53. }
  54.  
  55. catch (BeansException ex) {
  56. if (logger.isWarnEnabled()) {
  57. logger.warn( "Exception encountered during context initialization - " +
  58. "cancelling refresh attempt: " + ex);
  59. }
  60.  
  61. // Destroy already created singletons to avoid dangling resources.
  62. // 銷毀已經初始化的 singleton 的 Beans,以免有些 bean 會一直占用資源
  63. destroyBeans();
  64.  
  65. // Reset 'active' flag.
  66. cancelRefresh(ex);
  67.  
  68. // 把異常往外拋
  69. throw ex;
  70. }
  71.  
  72. finally {
  73. // Reset common introspection caches in Spring's core, since we
  74. // might not ever need metadata for singleton beans anymore...
  75. resetCommonCaches();
  76. }
  77. }
  78. }

下面,我們開始一步步來肢解這個 refresh() 方法。

創建 Bean 容器前的准備工作

這個比較簡單,直接看代碼中的幾個注釋即可。

  1. protected void prepareRefresh() {
  2. // 記錄啟動時間,
  3. // 將 active 屬性設置為 true,closed 屬性設置為 false,它們都是 AtomicBoolean 類型
  4. this.startupDate = System.currentTimeMillis();
  5. this.closed.set(false);
  6. this.active.set(true);
  7.  
  8. if (logger.isInfoEnabled()) {
  9. logger.info( "Refreshing " + this);
  10. }
  11.  
  12. // Initialize any placeholder property sources in the context environment
  13. initPropertySources();
  14.  
  15. // 校驗 xml 配置文件
  16. getEnvironment().validateRequiredProperties();
  17.  
  18. this.earlyApplicationEvents = new LinkedHashSet<ApplicationEvent>();
  19. }

創建 Bean 容器,加載並注冊 Bean

注意,這個方法是全文最重要的部分之一,這里將會初始化 BeanFactory、加載 Bean、注冊 Bean 等等。

當然,這步結束后,Bean 並沒有完成初始化。

// AbstractApplicationContext.java

  1. protected ConfigurableListableBeanFactory obtainFreshBeanFactory() {
  2. // 關閉舊的 BeanFactory (如果有),創建新的 BeanFactory,加載 Bean 定義、注冊 Bean 等等
  3. refreshBeanFactory();
  4.  
  5. // 返回剛剛創建的 BeanFactory
  6. ConfigurableListableBeanFactory beanFactory = getBeanFactory();
  7. if (logger.isDebugEnabled()) {
  8. logger.debug( "Bean factory for " + getDisplayName() + ": " + beanFactory);
  9. }
  10. return beanFactory;
  11. }

// AbstractRefreshableApplicationContext.java 120

  1. @Override
  2. protected final void refreshBeanFactory() throws BeansException {
  3. // 如果 ApplicationContext 中已經加載過 BeanFactory 了,銷毀所有 Bean,關閉 BeanFactory
  4. // 注意,應用中 BeanFactory 本來就是可以多個的,這里可不是說應用全局是否有 BeanFactory,而是當前
  5. // ApplicationContext 是否有 BeanFactory
  6. if (hasBeanFactory()) {
  7. destroyBeans();
  8. closeBeanFactory();
  9. }
  10. try {
  11. // 初始化一個 DefaultListableBeanFactory,為什么用這個,我們馬上說。
  12. DefaultListableBeanFactory beanFactory = createBeanFactory();
  13. // 用於 BeanFactory 的序列化,我想不部分人應該都用不到
  14. beanFactory.setSerializationId(getId());
  15.  
  16. // 下面這兩個方法很重要,別跟丟了,具體細節之后說
  17. // 設置 BeanFactory 的兩個配置屬性:是否允許 Bean 覆蓋、是否允許循環引用
  18. customizeBeanFactory(beanFactory);
  19.  
  20. // 加載 Bean 到 BeanFactory 中
  21. loadBeanDefinitions(beanFactory);
  22. synchronized (this.beanFactoryMonitor) {
  23. this.beanFactory = beanFactory;
  24. }
  25. }
  26. catch (IOException ex) {
  27. throw new ApplicationContextException("I/O error parsing bean definition source for " + getDisplayName(), ex);
  28. }
  29. }

看到這里的時候,我覺得讀者就應該站在高處看 ApplicationContext 了,ApplicationContext 繼承自 BeanFactory,但是它不應該被理解為 BeanFactory 的實現類,而是說其內部持有一個實例化的 BeanFactory(DefaultListableBeanFactory)。以后所有的 BeanFactory 相關的操作其實是給這個實例來處理的。

我們說說為什么選擇實例化 DefaultListableBeanFactory ?前面我們說了有個很重要的接口 ConfigurableListableBeanFactory,它實現了 BeanFactory 下面一層的所有三個接口,我把之前的繼承圖再拿過來大家再仔細看一下:

3

我們可以看到 ConfigurableListableBeanFactory 只有一個實現類 DefaultListableBeanFactory,而且實現類 DefaultListableBeanFactory 還通過實現右邊的 AbstractAutowireCapableBeanFactory 通吃了右路。所以結論就是,最底下這個家伙 DefaultListableBeanFactory 基本上是最牛的 BeanFactory 了,這也是為什么這邊會使用這個類來實例化的原因。

在繼續往下之前,我們需要先了解 BeanDefinition。我們說 BeanFactory 是 Bean 容器,那么 Bean 又是什么呢?

這里的 BeanDefinition 就是我們所說的 Spring 的 Bean,我們自己定義的各個 Bean 其實會轉換成一個個 BeanDefinition 存在於 Spring 的 BeanFactory 中。

所以,如果有人問你 Bean 是什么的時候,你要知道 Bean 在代碼層面上是 BeanDefinition 的實例。

BeanDefinition 中保存了我們的 Bean 信息,比如這個 Bean 指向的是哪個類、是否是單例的、是否懶加載、這個 Bean 依賴了哪些 Bean 等等。

BeanDefinition 接口定義

我們來看下 BeanDefinition 的接口定義:

  1. public interface BeanDefinition extends AttributeAccessor, BeanMetadataElement {
  2.  
  3. // 我們可以看到,默認只提供 sington 和 prototype 兩種,
  4. // 很多讀者都知道還有 request, session, globalSession, application, websocket 這幾種,
  5. // 不過,它們屬於基於 web 的擴展。
  6. String SCOPE_SINGLETON = ConfigurableBeanFactory.SCOPE_SINGLETON;
  7. String SCOPE_PROTOTYPE = ConfigurableBeanFactory.SCOPE_PROTOTYPE;
  8.  
  9. // 比較不重要,直接跳過吧
  10. int ROLE_APPLICATION = 0;
  11. int ROLE_SUPPORT = 1;
  12. int ROLE_INFRASTRUCTURE = 2;
  13.  
  14. // 設置父 Bean,這里涉及到 bean 繼承,不是 java 繼承。請參見附錄介紹
  15. void setParentName(String parentName);
  16.  
  17. // 獲取父 Bean
  18. String getParentName();
  19.  
  20. // 設置 Bean 的類名稱
  21. void setBeanClassName(String beanClassName);
  22.  
  23. // 獲取 Bean 的類名稱
  24. String getBeanClassName();
  25.  
  26.  
  27. // 設置 bean 的 scope
  28. void setScope(String scope);
  29.  
  30. String getScope();
  31.  
  32. // 設置是否懶加載
  33. void setLazyInit(boolean lazyInit);
  34.  
  35. boolean isLazyInit();
  36.  
  37. // 設置該 Bean 依賴的所有的 Bean,注意,這里的依賴不是指屬性依賴(如 @Autowire 標記的),
  38. // 是 depends-on="" 屬性設置的值。
  39. void setDependsOn(String... dependsOn);
  40.  
  41. // 返回該 Bean 的所有依賴
  42. String[] getDependsOn();
  43.  
  44. // 設置該 Bean 是否可以注入到其他 Bean 中,只對根據類型注入有效,
  45. // 如果根據名稱注入,即使這邊設置了 false,也是可以的
  46. void setAutowireCandidate(boolean autowireCandidate);
  47.  
  48. // 該 Bean 是否可以注入到其他 Bean 中
  49. boolean isAutowireCandidate();
  50.  
  51. // 主要的。同一接口的多個實現,如果不指定名字的話,Spring 會優先選擇設置 primary 為 true 的 bean
  52. void setPrimary(boolean primary);
  53.  
  54. // 是否是 primary 的
  55. boolean isPrimary();
  56.  
  57. // 如果該 Bean 采用工廠方法生成,指定工廠名稱。對工廠不熟悉的讀者,請參加附錄
  58. void setFactoryBeanName(String factoryBeanName);
  59. // 獲取工廠名稱
  60. String getFactoryBeanName();
  61. // 指定工廠類中的 工廠方法名稱
  62. void setFactoryMethodName(String factoryMethodName);
  63. // 獲取工廠類中的 工廠方法名稱
  64. String getFactoryMethodName();
  65.  
  66. // 構造器參數
  67. ConstructorArgumentValues getConstructorArgumentValues();
  68.  
  69. // Bean 中的屬性值,后面給 bean 注入屬性值的時候會說到
  70. MutablePropertyValues getPropertyValues();
  71.  
  72. // 是否 singleton
  73. boolean isSingleton();
  74.  
  75. // 是否 prototype
  76. boolean isPrototype();
  77.  
  78. // 如果這個 Bean 原生是抽象類,那么不能實例化
  79. boolean isAbstract();
  80.  
  81. int getRole();
  82. String getDescription();
  83. String getResourceDescription();
  84. BeanDefinition getOriginatingBeanDefinition();
  85. }

這個 BeanDefinition 其實已經包含很多的信息了,暫時不清楚所有的方法對應什么東西沒關系,希望看完本文后讀者可以徹底搞清楚里面的所有東西。

這里接口雖然那么多,但是沒有類似 getInstance() 這種方法來獲取我們定義的類的實例,真正的我們定義的類生成的實例到哪里去了呢?別着急,這個要很后面才能講到。

有了 BeanDefinition 的概念以后,我們再往下看 refreshBeanFactory() 方法中的剩余部分:

  1. customizeBeanFactory( beanFactory);
  2. loadBeanDefinitions( beanFactory);

雖然只有兩個方法,但路還很長啊。。。

customizeBeanFactory

customizeBeanFactory(beanFactory) 比較簡單,就是配置是否允許 BeanDefinition 覆蓋、是否允許循環引用。

  1. protected void customizeBeanFactory(DefaultListableBeanFactory beanFactory) {
  2. if (this.allowBeanDefinitionOverriding != null) {
  3. // 是否允許 Bean 定義覆蓋
  4. beanFactory.setAllowBeanDefinitionOverriding( this.allowBeanDefinitionOverriding);
  5. }
  6. if (this.allowCircularReferences != null) {
  7. // 是否允許 Bean 間的循環依賴
  8. beanFactory.setAllowCircularReferences( this.allowCircularReferences);
  9. }
  10. }

BeanDefinition 的覆蓋問題大家也許會碰到,就是在配置文件中定義 bean 時使用了相同的 id 或 name,默認情況下,allowBeanDefinitionOverriding 屬性為 null,如果在同一配置文件中重復了,會拋錯,但是如果不是同一配置文件中,會發生覆蓋。

循環引用也很好理解:A 依賴 B,而 B 依賴 A。或 A 依賴 B,B 依賴 C,而 C 依賴 A。

默認情況下,Spring 允許循環依賴,當然如果你在 A 的構造方法中依賴 B,在 B 的構造方法中依賴 A 是不行的。

至於這兩個屬性怎么配置?我在附錄中進行了介紹,尤其對於覆蓋問題,很多人都希望禁止出現 Bean 覆蓋,可是 Spring 默認是不同文件的時候可以覆蓋的。

之后的源碼中還會出現這兩個屬性,讀者有個印象就可以了。

加載 Bean: loadBeanDefinitions

接下來是最重要的 loadBeanDefinitions(beanFactory) 方法了,這個方法將根據配置,加載各個 Bean,然后放到 BeanFactory 中。

讀取配置的操作在 XmlBeanDefinitionReader 中,其負責加載配置、解析。

// AbstractXmlApplicationContext.java 80

  1. /** 我們可以看到,此方法將通過一個 XmlBeanDefinitionReader 實例來加載各個 Bean。*/
  2. @Override
  3. protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException {
  4. // 給這個 BeanFactory 實例化一個 XmlBeanDefinitionReader
  5. XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);
  6.  
  7. // Configure the bean definition reader with this context's
  8. // resource loading environment.
  9. beanDefinitionReader.setEnvironment( this.getEnvironment());
  10. beanDefinitionReader.setResourceLoader( this);
  11. beanDefinitionReader.setEntityResolver( new ResourceEntityResolver(this));
  12.  
  13. // 初始化 BeanDefinitionReader,其實這個是提供給子類覆寫的,
  14. // 我看了一下,沒有類覆寫這個方法,我們姑且當做不重要吧
  15. initBeanDefinitionReader(beanDefinitionReader);
  16. // 重點來了,繼續往下
  17. loadBeanDefinitions(beanDefinitionReader);
  18. }

現在還在這個類中,接下來用剛剛初始化的 Reader 開始來加載 xml 配置,這塊代碼讀者可以選擇性跳過,不是很重要。也就是說,下面這個代碼塊,讀者可以很輕松地略過。

// AbstractXmlApplicationContext.java 120

  1. protected void loadBeanDefinitions(XmlBeanDefinitionReader reader) throws BeansException, IOException {
  2. Resource[] configResources = getConfigResources();
  3. if (configResources != null) {
  4. // 往下看
  5. reader.loadBeanDefinitions(configResources);
  6. }
  7. String[] configLocations = getConfigLocations();
  8. if (configLocations != null) {
  9. reader.loadBeanDefinitions(configLocations);
  10. }
  11. }
  12.  
  13. // 上面雖然有兩個分支,不過第二個分支很快通過解析路徑轉換為 Resource 以后也會進到這里
  14. @Override
  15. public int loadBeanDefinitions(Resource... resources) throws BeanDefinitionStoreException {
  16. Assert.notNull(resources, "Resource array must not be null");
  17. int counter = 0;
  18. // 注意這里是個 for 循環,也就是每個文件是一個 resource
  19. for (Resource resource : resources) {
  20. // 繼續往下看
  21. counter += loadBeanDefinitions(resource);
  22. }
  23. return counter;
  24. }
  25.  
  26. // XmlBeanDefinitionReader 303
  27. @Override
  28. public int loadBeanDefinitions(Resource resource) throws BeanDefinitionStoreException {
  29. return loadBeanDefinitions(new EncodedResource(resource));
  30. }
  31.  
  32. // XmlBeanDefinitionReader 314
  33. public int loadBeanDefinitions(EncodedResource encodedResource) throws BeanDefinitionStoreException {
  34. Assert.notNull(encodedResource, "EncodedResource must not be null");
  35. if (logger.isInfoEnabled()) {
  36. logger.info( "Loading XML bean definitions from " + encodedResource.getResource());
  37. }
  38. // 用一個 ThreadLocal 來存放所有的配置文件資源
  39. Set<EncodedResource> currentResources = this.resourcesCurrentlyBeingLoaded.get();
  40. if (currentResources == null) {
  41. currentResources = new HashSet<EncodedResource>(4);
  42. this.resourcesCurrentlyBeingLoaded.set(currentResources);
  43. }
  44. if (!currentResources.add(encodedResource)) {
  45. throw new BeanDefinitionStoreException(
  46. "Detected cyclic loading of " + encodedResource + " - check your import definitions!");
  47. }
  48. try {
  49. InputStream inputStream = encodedResource.getResource().getInputStream();
  50. try {
  51. InputSource inputSource = new InputSource(inputStream);
  52. if (encodedResource.getEncoding() != null) {
  53. inputSource.setEncoding(encodedResource.getEncoding());
  54. }
  55. // 核心部分
  56. return doLoadBeanDefinitions(inputSource, encodedResource.getResource());
  57. }
  58. finally {
  59. inputStream.close();
  60. }
  61. }
  62. catch (IOException ex) {
  63. throw new BeanDefinitionStoreException(
  64. "IOException parsing XML document from " + encodedResource.getResource(), ex);
  65. }
  66. finally {
  67. currentResources.remove(encodedResource);
  68. if (currentResources.isEmpty()) {
  69. this.resourcesCurrentlyBeingLoaded.remove();
  70. }
  71. }
  72. }
  73.  
  74. // 還在這個文件中,第 388 行
  75. protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource)
  76. throws BeanDefinitionStoreException {
  77. try {
  78. // 這里就不看了
  79. Document doc = doLoadDocument(inputSource, resource);
  80. // 繼續
  81. return registerBeanDefinitions(doc, resource);
  82. }
  83. catch (...
  84. }
  85. // 還在這個文件中,第 505 行
  86. // 返回從當前配置文件加載了多少數量的 Bean
  87. public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException {
  88. BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader();
  89. int countBefore = getRegistry().getBeanDefinitionCount();
  90. documentReader.registerBeanDefinitions(doc, createReaderContext(resource));
  91. return getRegistry().getBeanDefinitionCount() - countBefore;
  92. }
  93. // DefaultBeanDefinitionDocumentReader 90
  94. @Override
  95. public void registerBeanDefinitions(Document doc, XmlReaderContext readerContext) {
  96. this.readerContext = readerContext;
  97. logger.debug( "Loading bean definitions");
  98. Element root = doc.getDocumentElement();
  99. doRegisterBeanDefinitions(root);
  100. }

經過漫長的鏈路,一個配置文件終於轉換為一顆 DOM 樹了,注意,這里指的是其中一個配置文件,不是所有的,讀者可以看到上面有個 for 循環的。下面從根節點開始解析:

doRegisterBeanDefinitions:

  1. // DefaultBeanDefinitionDocumentReader 116
  2. protected void doRegisterBeanDefinitions(Element root) {
  3. // 我們看名字就知道,BeanDefinitionParserDelegate 必定是一個重要的類,它負責解析 Bean 定義,
  4. // 這里為什么要定義一個 parent? 看到后面就知道了,是遞歸問題,
  5. // 因為 <beans /> 內部是可以定義 <beans /> 的,所以這個方法的 root 其實不一定就是 xml 的根節點,也可以是嵌套在里面的 <beans /> 節點,從源碼分析的角度,我們當做根節點就好了
  6. BeanDefinitionParserDelegate parent = this.delegate;
  7. this.delegate = createDelegate(getReaderContext(), root, parent);
  8.  
  9. if (this.delegate.isDefaultNamespace(root)) {
  10. // 這塊說的是根節點 <beans ... profile="dev" /> 中的 profile 是否是當前環境需要的,
  11. // 如果當前環境配置的 profile 不包含此 profile,那就直接 return 了,不對此 <beans /> 解析
  12. // 不熟悉 profile 為何物,不熟悉怎么配置 profile 讀者的請移步附錄區
  13. String profileSpec = root.getAttribute(PROFILE_ATTRIBUTE);
  14. if (StringUtils.hasText(profileSpec)) {
  15. String[] specifiedProfiles = StringUtils.tokenizeToStringArray(
  16. profileSpec, BeanDefinitionParserDelegate.MULTI_VALUE_ATTRIBUTE_DELIMITERS);
  17. if (!getReaderContext().getEnvironment().acceptsProfiles(specifiedProfiles)) {
  18. if (logger.isInfoEnabled()) {
  19. logger.info( "Skipped XML bean definition file due to specified profiles [" + profileSpec +
  20. "] not matching: " + getReaderContext().getResource());
  21. }
  22. return;
  23. }
  24. }
  25. }
  26.  
  27. preProcessXml(root); // 鈎子
  28. parseBeanDefinitions(root, this.delegate);
  29. postProcessXml(root); // 鈎子
  30.  
  31. this.delegate = parent;
  32. }

preProcessXml(root) 和 postProcessXml(root) 是給子類用的鈎子方法,鑒於沒有被使用到,也不是我們的重點,我們直接跳過。

這里涉及到了 profile 的問題,對於不了解的讀者,我在附錄中對 profile 做了簡單的解釋,讀者可以參考一下。

接下來,看核心解析方法 parseBeanDefinitions(root, this.delegate) :

  1. // default namespace 涉及到的就四個標簽 <import />、<alias />、<bean /> 和 <beans />,
  2. // 其他的屬於 custom 的
  3. protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) {
  4. if (delegate.isDefaultNamespace(root)) {
  5. NodeList nl = root.getChildNodes();
  6. for (int i = 0; i < nl.getLength(); i++) {
  7. Node node = nl.item(i);
  8. if (node instanceof Element) {
  9. Element ele = (Element) node;
  10. if (delegate.isDefaultNamespace(ele)) {
  11. parseDefaultElement(ele, delegate);
  12. }
  13. else {
  14. delegate.parseCustomElement(ele);
  15. }
  16. }
  17. }
  18. }
  19. else {
  20. delegate.parseCustomElement(root);
  21. }
  22. }

從上面的代碼,我們可以看到,對於每個配置來說,分別進入到 parseDefaultElement(ele, delegate); 和 delegate.parseCustomElement(ele); 這兩個分支了。

parseDefaultElement(ele, delegate) 代表解析的節點是 <import /><alias /><bean /><beans /> 這幾個。

這里的四個標簽之所以是 default 的,是因為它們是處於這個 namespace 下定義的:

http://www.springframework.org/schema/beans 

又到初學者科普時間,不熟悉 namespace 的讀者請看下面貼出來的 xml,這里的第二行 xmlns 就是咯。

  1. <beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  2. xmlns="http://www.springframework.org/schema/beans"
  3. xsi:schemaLocation="
  4. http://www.springframework.org/schema/beans
  5. http://www.springframework.org/schema/beans/spring-beans.xsd"
  6. default-autowire="byName">

而對於其他的標簽,將進入到 delegate.parseCustomElement(element) 這個分支。如我們經常會使用到的 <mvc /><task /><context /><aop />等。

這些屬於擴展,如果需要使用上面這些 ”非 default“ 標簽,那么上面的 xml 頭部的地方也要引入相應的 namespace 和 .xsd 文件的路徑,如下所示。同時代碼中需要提供相應的 parser 來解析,如 MvcNamespaceHandler、TaskNamespaceHandler、ContextNamespaceHandler、AopNamespaceHandler 等。

假如讀者想分析 <context:property-placeholder location="classpath:xx.properties" /> 的實現原理,就應該到 ContextNamespaceHandler 中找答案。

  1. <beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  2. xmlns="http://www.springframework.org/schema/beans"
  3. xmlns:context="http://www.springframework.org/schema/context"
  4. xmlns:mvc="http://www.springframework.org/schema/mvc"
  5. xsi:schemaLocation="
  6. http://www.springframework.org/schema/beans
  7. http://www.springframework.org/schema/beans/spring-beans.xsd
  8. http://www.springframework.org/schema/context
  9. http://www.springframework.org/schema/context/spring-context.xsd
  10. http://www.springframework.org/schema/mvc
  11. http://www.springframework.org/schema/mvc/spring-mvc.xsd
  12. "
  13. default-autowire="byName">

回過神來,看看處理 default 標簽的方法:

  1. private void parseDefaultElement(Element ele, BeanDefinitionParserDelegate delegate) {
  2. if (delegate.nodeNameEquals(ele, IMPORT_ELEMENT)) {
  3. // 處理 <import /> 標簽
  4. importBeanDefinitionResource(ele);
  5. }
  6. else if (delegate.nodeNameEquals(ele, ALIAS_ELEMENT)) {
  7. // 處理 <alias /> 標簽定義
  8. // <alias name="fromName" alias="toName"/>
  9. processAliasRegistration(ele);
  10. }
  11. else if (delegate.nodeNameEquals(ele, BEAN_ELEMENT)) {
  12. // 處理 <bean /> 標簽定義,這也算是我們的重點吧
  13. processBeanDefinition(ele, delegate);
  14. }
  15. else if (delegate.nodeNameEquals(ele, NESTED_BEANS_ELEMENT)) {
  16. // 如果碰到的是嵌套的 <beans /> 標簽,需要遞歸
  17. doRegisterBeanDefinitions(ele);
  18. }
  19. }

如果每個標簽都說,那我不吐血,你們都要吐血了。我們挑我們的重點 <bean /> 標簽出來說。

processBeanDefinition

下面是 processBeanDefinition 解析 <bean /> 標簽:

// DefaultBeanDefinitionDocumentReader 298

  1. protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) {
  2. // 將 <bean /> 節點中的信息提取出來,然后封裝到一個 BeanDefinitionHolder 中,細節往下看
  3. BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele);
  4.  
  5. // 下面的幾行先不要看,跳過先,跳過先,跳過先,后面會繼續說的
  6.  
  7. if (bdHolder != null) {
  8. bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder);
  9. try {
  10. // Register the final decorated instance.
  11. BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry());
  12. }
  13. catch (BeanDefinitionStoreException ex) {
  14. getReaderContext().error( "Failed to register bean definition with name '" +
  15. bdHolder.getBeanName() + "'", ele, ex);
  16. }
  17. // Send registration event.
  18. getReaderContext().fireComponentRegistered( new BeanComponentDefinition(bdHolder));
  19. }
  20. }

繼續往下看怎么解析之前,我們先看下 <bean /> 標簽中可以定義哪些屬性:

Property  
class 類的全限定名
name 可指定 id、name(用逗號、分號、空格分隔)
scope 作用域
constructor arguments 指定構造參數
properties 設置屬性的值
autowiring mode no(默認值)、byName、byType、 constructor
lazy-initialization mode 是否懶加載(如果被非懶加載的bean依賴了那么其實也就不能懶加載了)
initialization method bean 屬性設置完成后,會調用這個方法
destruction method bean 銷毀后的回調方法

上面表格中的內容我想大家都非常熟悉吧,如果不熟悉,那就是你不夠了解 Spring 的配置了。

簡單地說就是像下面這樣子:

  1. <bean id="exampleBean" name="name1, name2, name3" class="com.javadoop.ExampleBean"
  2. scope="singleton" lazy-init="true" init-method="init" destroy-method="cleanup">
  3.  
  4. <!-- 可以用下面三種形式指定構造參數 -->
  5. <constructor-arg type="int" value="7500000"/>
  6. <constructor-arg name="years" value="7500000"/>
  7. <constructor-arg index="0" value="7500000"/>
  8.  
  9. <!-- property 的幾種情況 -->
  10. <property name="beanOne">
  11. <ref bean="anotherExampleBean"/>
  12. </property>
  13. <property name="beanTwo" ref="yetAnotherBean"/>
  14. <property name="integerProperty" value="1"/>
  15. </bean>

當然,除了上面舉例出來的這些,還有 factory-bean、factory-method、<lockup-method /><replaced-method /><meta /><qualifier /> 這幾個,大家是不是熟悉呢?

有了以上這些知識以后,我們再繼續往里看怎么解析 bean 元素,是怎么轉換到 BeanDefinitionHolder 的。

// BeanDefinitionParserDelegate 428

  1. public BeanDefinitionHolder parseBeanDefinitionElement(Element ele) {
  2. return parseBeanDefinitionElement(ele, null);
  3. }
  4.  
  5. public BeanDefinitionHolder parseBeanDefinitionElement(Element ele, BeanDefinition containingBean) {
  6. String id = ele.getAttribute(ID_ATTRIBUTE);
  7. String nameAttr = ele.getAttribute(NAME_ATTRIBUTE);
  8.  
  9. List< String> aliases = new ArrayList<String>();
  10.  
  11. // 將 name 屬性的定義按照 ”逗號、分號、空格“ 切分,形成一個別名列表數組,
  12. // 當然,如果你不定義的話,就是空的了
  13. // 我在附錄中簡單介紹了一下 id 和 name 的配置,大家可以看一眼,有個20秒就可以了
  14. if (StringUtils.hasLength(nameAttr)) {
  15. String[] nameArr = StringUtils.tokenizeToStringArray(nameAttr, MULTI_VALUE_ATTRIBUTE_DELIMITERS);
  16. aliases.addAll(Arrays.asList(nameArr));
  17. }
  18.  
  19. String beanName = id;
  20. // 如果沒有指定id, 那么用別名列表的第一個名字作為beanName
  21. if (!StringUtils.hasText(beanName) && !aliases.isEmpty()) {
  22. beanName = aliases.remove( 0);
  23. if (logger.isDebugEnabled()) {
  24. logger.debug( "No XML 'id' specified - using '" + beanName +
  25. "' as bean name and " + aliases + " as aliases");
  26. }
  27. }
  28.  
  29. if (containingBean == null) {
  30. checkNameUniqueness(beanName, aliases, ele);
  31. }
  32.  
  33. // 根據 <bean ...>...</bean> 中的配置創建 BeanDefinition,然后把配置中的信息都設置到實例中,
  34. // 細節后面再說
  35. AbstractBeanDefinition beanDefinition = parseBeanDefinitionElement(ele, beanName, containingBean);
  36.  
  37. // 到這里,整個 <bean /> 標簽就算解析結束了,一個 BeanDefinition 就形成了。
  38. if (beanDefinition != null) {
  39. // 如果都沒有設置 id 和 name,那么此時的 beanName 就會為 null,進入下面這塊代碼產生
  40. // 如果讀者不感興趣的話,我覺得不需要關心這塊代碼,對本文源碼分析來說,這些東西不重要
  41. if (!StringUtils.hasText(beanName)) {
  42. try {
  43. if (containingBean != null) {// 按照我們的思路,這里 containingBean 是 null 的
  44. beanName = BeanDefinitionReaderUtils.generateBeanName(
  45. beanDefinition, this.readerContext.getRegistry(), true);
  46. }
  47. else {
  48. // 如果我們不定義 id 和 name,那么我們引言里的那個例子:
  49. // 1. beanName 為:com.javadoop.example.MessageServiceImpl#0
  50. // 2. beanClassName 為:com.javadoop.example.MessageServiceImpl
  51.  
  52. beanName = this.readerContext.generateBeanName(beanDefinition);
  53.  
  54. String beanClassName = beanDefinition.getBeanClassName();
  55. if (beanClassName != null &&
  56. beanName.startsWith(beanClassName) && beanName.length() > beanClassName.length() &&
  57. ! this.readerContext.getRegistry().isBeanNameInUse(beanClassName)) {
  58. // 把 beanClassName 設置為 Bean 的別名
  59. aliases.add(beanClassName);
  60. }
  61. }
  62. if (logger.isDebugEnabled()) {
  63. logger.debug( "Neither XML 'id' nor 'name' specified - " +
  64. "using generated bean name [" + beanName + "]");
  65. }
  66. }
  67. catch (Exception ex) {
  68. error(ex.getMessage(), ele);
  69. return null;
  70. }
  71. }
  72. String[] aliasesArray = StringUtils.toStringArray(aliases);
  73. // 返回 BeanDefinitionHolder
  74. return new BeanDefinitionHolder(beanDefinition, beanName, aliasesArray);
  75. }
  76.  
  77. return null;
  78. }

看看怎么根據配置創建 BeanDefinition:

  1. public AbstractBeanDefinition parseBeanDefinitionElement(
  2. Element ele, String beanName, BeanDefinition containingBean) {
  3.  
  4. this.parseState.push( new BeanEntry(beanName));
  5.  
  6. String className = null;
  7. if (ele.hasAttribute(CLASS_ATTRIBUTE)) {
  8. className = ele.getAttribute(CLASS_ATTRIBUTE).trim();
  9. }
  10.  
  11. try {
  12. String parent = null;
  13. if (ele.hasAttribute(PARENT_ATTRIBUTE)) {
  14. parent = ele.getAttribute(PARENT_ATTRIBUTE);
  15. }
  16. // 創建 BeanDefinition,然后設置類信息而已,很簡單,就不貼代碼了
  17. AbstractBeanDefinition bd = createBeanDefinition(className, parent);
  18.  
  19. // 設置 BeanDefinition 的一堆屬性,這些屬性定義在 AbstractBeanDefinition 中
  20. parseBeanDefinitionAttributes(ele, beanName, containingBean, bd);
  21. bd.setDescription(DomUtils.getChildElementValueByTagName(ele, DESCRIPTION_ELEMENT));
  22.  
  23. /**
  24. * 下面的一堆是解析 <bean>......</bean> 內部的子元素,
  25. * 解析出來以后的信息都放到 bd 的屬性中
  26. */
  27.  
  28. // 解析 <meta />
  29. parseMetaElements(ele, bd);
  30. // 解析 <lookup-method />
  31. parseLookupOverrideSubElements(ele, bd.getMethodOverrides());
  32. // 解析 <replaced-method />
  33. parseReplacedMethodSubElements(ele, bd.getMethodOverrides());
  34. // 解析 <constructor-arg />
  35. parseConstructorArgElements(ele, bd);
  36. // 解析 <property />
  37. parsePropertyElements(ele, bd);
  38. // 解析 <qualifier />
  39. parseQualifierElements(ele, bd);
  40.  
  41. bd.setResource(this.readerContext.getResource());
  42. bd.setSource(extractSource(ele));
  43.  
  44. return bd;
  45. }
  46. catch (ClassNotFoundException ex) {
  47. error("Bean class [" + className + "] not found", ele, ex);
  48. }
  49. catch (NoClassDefFoundError err) {
  50. error("Class that bean class [" + className + "] depends on not found", ele, err);
  51. }
  52. catch (Throwable ex) {
  53. error("Unexpected failure during bean definition parsing", ele, ex);
  54. }
  55. finally {
  56. this.parseState.pop();
  57. }
  58.  
  59. return null;
  60. }

到這里,我們已經完成了根據 <bean /> 配置創建了一個 BeanDefinitionHolder 實例。注意,是一個。

我們回到解析 <bean /> 的入口方法:

  1. protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) {
  2. // 將 <bean /> 節點轉換為 BeanDefinitionHolder,就是上面說的一堆
  3. BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele);
  4. if (bdHolder != null) {
  5. // 如果有自定義屬性的話,進行相應的解析,先忽略
  6. bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder);
  7. try {
  8. // 我們把這步叫做 注冊Bean 吧
  9. BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry());
  10. }
  11. catch (BeanDefinitionStoreException ex) {
  12. getReaderContext().error( "Failed to register bean definition with name '" +
  13. bdHolder.getBeanName() + "'", ele, ex);
  14. }
  15. // 注冊完成后,發送事件,本文不展開說這個
  16. getReaderContext().fireComponentRegistered( new BeanComponentDefinition(bdHolder));
  17. }
  18. }

大家再仔細看一下這塊吧,我們后面就不回來說這個了。這里已經根據一個 <bean /> 標簽產生了一個 BeanDefinitionHolder 的實例,這個實例里面也就是一個 BeanDefinition 的實例和它的 beanName、aliases 這三個信息,注意,我們的關注點始終在 BeanDefinition 上:

  1. public class BeanDefinitionHolder implements BeanMetadataElement {
  2.  
  3. private final BeanDefinition beanDefinition;
  4.  
  5. private final String beanName;
  6.  
  7. private final String[] aliases;
  8. ...

然后我們准備注冊這個 BeanDefinition,最后,把這個注冊事件發送出去。

下面,我們開始說注冊 Bean 吧。

注冊 Bean

// BeanDefinitionReaderUtils 143

  1. public static void registerBeanDefinition(
  2. BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry)
  3. throws BeanDefinitionStoreException {
  4.  
  5. String beanName = definitionHolder.getBeanName();
  6. // 注冊這個 Bean
  7. registry.registerBeanDefinition(beanName, definitionHolder.getBeanDefinition());
  8.  
  9. // 如果還有別名的話,也要根據別名統統注冊一遍,不然根據別名就找不到 Bean 了,這我們就不開心了
  10. String[] aliases = definitionHolder.getAliases();
  11. if (aliases != null) {
  12. for (String alias : aliases) {
  13. // alias -> beanName 保存它們的別名信息,這個很簡單,用一個 map 保存一下就可以了,
  14. // 獲取的時候,會先將 alias 轉換為 beanName,然后再查找
  15. registry.registerAlias(beanName, alias);
  16. }
  17. }
  18. }

別名注冊的放一邊,畢竟它很簡單,我們看看怎么注冊 Bean。

// DefaultListableBeanFactory 793

  1. @Override
  2. public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition)
  3. throws BeanDefinitionStoreException {
  4.  
  5. Assert.hasText(beanName, "Bean name must not be empty");
  6. Assert.notNull(beanDefinition, "BeanDefinition must not be null");
  7.  
  8. if (beanDefinition instanceof AbstractBeanDefinition) {
  9. try {
  10. ((AbstractBeanDefinition) beanDefinition).validate();
  11. }
  12. catch (BeanDefinitionValidationException ex) {
  13. throw new BeanDefinitionStoreException(...);
  14. }
  15. }
  16.  
  17. // old? 還記得 “允許 bean 覆蓋” 這個配置嗎?allowBeanDefinitionOverriding
  18. BeanDefinition oldBeanDefinition;
  19.  
  20. // 之后會看到,所有的 Bean 注冊后會放入這個 beanDefinitionMap 中
  21. oldBeanDefinition = this.beanDefinitionMap.get(beanName);
  22.  
  23. // 處理重復名稱的 Bean 定義的情況
  24. if (oldBeanDefinition != null) {
  25. if (!isAllowBeanDefinitionOverriding()) {
  26. // 如果不允許覆蓋的話,拋異常
  27. throw new BeanDefinitionStoreException(beanDefinition.getResourceDescription()...
  28. }
  29. else if (oldBeanDefinition.getRole() < beanDefinition.getRole()) {
  30. // log...用框架定義的 Bean 覆蓋用戶自定義的 Bean
  31. }
  32. else if (!beanDefinition.equals(oldBeanDefinition)) {
  33. // log...用新的 Bean 覆蓋舊的 Bean
  34. }
  35. else {
  36. // log...用同等的 Bean 覆蓋舊的 Bean,這里指的是 equals 方法返回 true 的 Bean
  37. }
  38. // 覆蓋
  39. this.beanDefinitionMap.put(beanName, beanDefinition);
  40. }
  41. else {
  42. // 判斷是否已經有其他的 Bean 開始初始化了.
  43. // 注意,"注冊Bean" 這個動作結束,Bean 依然還沒有初始化,我們后面會有大篇幅說初始化過程,
  44. // 在 Spring 容器啟動的最后,會 預初始化 所有的 singleton beans
  45. if (hasBeanCreationStarted()) {
  46. // Cannot modify startup-time collection elements anymore (for stable iteration)
  47. synchronized ( this.beanDefinitionMap) {
  48. this.beanDefinitionMap.put(beanName, beanDefinition);
  49. List<String> updatedDefinitions = new ArrayList<String>( this.beanDefinitionNames.size() + 1);
  50. updatedDefinitions.addAll( this.beanDefinitionNames);
  51. updatedDefinitions.add(beanName);
  52. this.beanDefinitionNames = updatedDefinitions;
  53. if (this.manualSingletonNames.contains(beanName)) {
  54. Set<String> updatedSingletons = new LinkedHashSet<String>( this.manualSingletonNames);
  55. updatedSingletons.remove(beanName);
  56. this.manualSingletonNames = updatedSingletons;
  57. }
  58. }
  59. }
  60. else {
  61. // 最正常的應該是進到這里。
  62.  
  63. // 將 BeanDefinition 放到這個 map 中,這個 map 保存了所有的 BeanDefinition
  64. this.beanDefinitionMap.put(beanName, beanDefinition);
  65. // 這是個 ArrayList,所以會按照 bean 配置的順序保存每一個注冊的 Bean 的名字
  66. this.beanDefinitionNames.add(beanName);
  67. // 這是個 LinkedHashSet,代表的是手動注冊的 singleton bean,
  68. // 注意這里是 remove 方法,到這里的 Bean 當然不是手動注冊的
  69. // 手動指的是通過調用以下方法注冊的 bean :
  70. // registerSingleton(String beanName, Object singletonObject)
  71. // 這不是重點,解釋只是為了不讓大家疑惑。Spring 會在后面"手動"注冊一些 Bean,如 "environment"、"systemProperties" 等 bean
  72. this.manualSingletonNames.remove(beanName);
  73. }
  74. // 這個不重要,在預初始化的時候會用到,不必管它。
  75. this.frozenBeanDefinitionNames = null;
  76. }
  77.  
  78. if (oldBeanDefinition != null || containsSingleton(beanName)) {
  79. resetBeanDefinition(beanName);
  80. }
  81. }

總結一下,到這里已經初始化了 Bean 容器,<bean /> 配置也相應的轉換為了一個個 BeanDefinition,然后注冊了各個 BeanDefinition 到注冊中心,並且發送了注冊事件。

Bean 容器實例化完成后

說到這里,我們回到 refresh() 方法,我重新貼了一遍代碼,看看我們說到哪了。是的,我們才說完 obtainFreshBeanFactory() 方法。

考慮到篇幅,這里開始大幅縮減掉沒必要詳細介紹的部分,大家直接看下面的代碼中的注釋就好了。

  1. @Override
  2. public void refresh() throws BeansException, IllegalStateException {
  3. // 來個鎖,不然 refresh() 還沒結束,你又來個啟動或銷毀容器的操作,那不就亂套了嘛
  4. synchronized (this.startupShutdownMonitor) {
  5.  
  6. // 准備工作,記錄下容器的啟動時間、標記“已啟動”狀態、處理配置文件中的占位符
  7. prepareRefresh();
  8.  
  9. // 這步比較關鍵,這步完成后,配置文件就會解析成一個個 Bean 定義,注冊到 BeanFactory 中,
  10. // 當然,這里說的 Bean 還沒有初始化,只是配置信息都提取出來了,
  11. // 注冊也只是將這些信息都保存到了注冊中心(說到底核心是一個 beanName-> beanDefinition 的 map)
  12. ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
  13.  
  14. // 設置 BeanFactory 的類加載器,添加幾個 BeanPostProcessor,手動注冊幾個特殊的 bean
  15. // 這塊待會會展開說
  16. prepareBeanFactory(beanFactory);
  17.  
  18. try {
  19. // 【這里需要知道 BeanFactoryPostProcessor 這個知識點,Bean 如果實現了此接口,
  20. // 那么在容器初始化以后,Spring 會負責調用里面的 postProcessBeanFactory 方法。】
  21.  
  22. // 這里是提供給子類的擴展點,到這里的時候,所有的 Bean 都加載、注冊完成了,但是都還沒有初始化
  23. // 具體的子類可以在這步的時候添加一些特殊的 BeanFactoryPostProcessor 的實現類或做點什么事
  24. postProcessBeanFactory(beanFactory);
  25. // 調用 BeanFactoryPostProcessor 各個實現類的 postProcessBeanFactory(factory) 方法
  26. invokeBeanFactoryPostProcessors(beanFactory);
  27.  
  28. // 注冊 BeanPostProcessor 的實現類,注意看和 BeanFactoryPostProcessor 的區別
  29. // 此接口兩個方法: postProcessBeforeInitialization 和 postProcessAfterInitialization
  30. // 兩個方法分別在 Bean 初始化之前和初始化之后得到執行。注意,到這里 Bean 還沒初始化
  31. registerBeanPostProcessors(beanFactory);
  32.  
  33. // 初始化當前 ApplicationContext 的 MessageSource,國際化這里就不展開說了,不然沒完沒了了
  34. initMessageSource();
  35.  
  36. // 初始化當前 ApplicationContext 的事件廣播器,這里也不展開了
  37. initApplicationEventMulticaster();
  38.  
  39. // 從方法名就可以知道,典型的模板方法(鈎子方法),
  40. // 具體的子類可以在這里初始化一些特殊的 Bean(在初始化 singleton beans 之前)
  41. onRefresh();
  42.  
  43. // 注冊事件監聽器,監聽器需要實現 ApplicationListener 接口。這也不是我們的重點,過
  44. registerListeners();
  45.  
  46. // 重點,重點,重點
  47. // 初始化所有的 singleton beans
  48. //(lazy-init 的除外)
  49. finishBeanFactoryInitialization(beanFactory);
  50.  
  51. // 最后,廣播事件,ApplicationContext 初始化完成
  52. finishRefresh();
  53. }
  54.  
  55. catch (BeansException ex) {
  56. if (logger.isWarnEnabled()) {
  57. logger.warn( "Exception encountered during context initialization - " +
  58. "cancelling refresh attempt: " + ex);
  59. }
  60.  
  61. // Destroy already created singletons to avoid dangling resources.
  62. // 銷毀已經初始化的 singleton 的 Beans,以免有些 bean 會一直占用資源
  63. destroyBeans();
  64.  
  65. // Reset 'active' flag.
  66. cancelRefresh(ex);
  67.  
  68. // 把異常往外拋
  69. throw ex;
  70. }
  71.  
  72. finally {
  73. // Reset common introspection caches in Spring's core, since we
  74. // might not ever need metadata for singleton beans anymore...
  75. resetCommonCaches();
  76. }
  77. }
  78. }

准備 Bean 容器: prepareBeanFactory

這里簡單介紹下 prepareBeanFactory(factory) 方法:

  1. /**
  2. * Configure the factory's standard context characteristics,
  3. * such as the context's ClassLoader and post-processors.
  4. * @param beanFactory the BeanFactory to configure
  5. */
  6. protected void prepareBeanFactory(ConfigurableListableBeanFactory beanFactory) {
  7. // 設置 BeanFactory 的類加載器,我們知道 BeanFactory 需要加載類,也就需要類加載器,
  8. // 這里設置為當前 ApplicationContext 的類加載器
  9. beanFactory.setBeanClassLoader(getClassLoader());
  10. // 設置 BeanExpressionResolver
  11. beanFactory.setBeanExpressionResolver(new StandardBeanExpressionResolver(beanFactory.getBeanClassLoader()));
  12. //
  13. beanFactory.addPropertyEditorRegistrar(new ResourceEditorRegistrar(this, getEnvironment()));
  14.  
  15. // 添加一個 BeanPostProcessor,這個 processor 比較簡單,
  16. // 實現了 Aware 接口的幾個特殊的 beans 在初始化的時候,這個 processor 負責回調
  17. beanFactory.addBeanPostProcessor(new ApplicationContextAwareProcessor(this));
  18.  
  19. // 下面幾行的意思就是,如果某個 bean 依賴於以下幾個接口的實現類,在自動裝配的時候忽略它們,
  20. // Spring 會通過其他方式來處理這些依賴。
  21. beanFactory.ignoreDependencyInterface(EnvironmentAware.class);
  22. beanFactory.ignoreDependencyInterface(EmbeddedValueResolverAware.class);
  23. beanFactory.ignoreDependencyInterface(ResourceLoaderAware.class);
  24. beanFactory.ignoreDependencyInterface(ApplicationEventPublisherAware.class);
  25. beanFactory.ignoreDependencyInterface(MessageSourceAware.class);
  26. beanFactory.ignoreDependencyInterface(ApplicationContextAware.class);
  27.  
  28. /**
  29. * 下面幾行就是為特殊的幾個 bean 賦值,如果有 bean 依賴了以下幾個,會注入這邊相應的值,
  30. * 之前我們說過,"當前 ApplicationContext 持有一個 BeanFactory",這里解釋了第一行
  31. * ApplicationContext 繼承了 ResourceLoader、ApplicationEventPublisher、MessageSource
  32. * 所以對於這幾個,可以賦值為 this,注意 this 是一個 ApplicationContext
  33. * 那這里怎么沒看到為 MessageSource 賦值呢?那是因為 MessageSource 被注冊成為了一個普通的 bean
  34. */
  35. beanFactory.registerResolvableDependency(BeanFactory.class, beanFactory);
  36. beanFactory.registerResolvableDependency(ResourceLoader.class, this);
  37. beanFactory.registerResolvableDependency(ApplicationEventPublisher.class, this);
  38. beanFactory.registerResolvableDependency(ApplicationContext.class, this);
  39.  
  40. // 這個 BeanPostProcessor 也很簡單,在 bean 實例化后,如果是 ApplicationListener 的子類,
  41. // 那么將其添加到 listener 列表中,可以理解成:注冊事件監聽器
  42. beanFactory.addBeanPostProcessor(new ApplicationListenerDetector(this));
  43.  
  44. // Detect a LoadTimeWeaver and prepare for weaving, if found.
  45. // 這里涉及到特殊的 bean,名為:loadTimeWeaver,這不是我們的重點,忽略它
  46. if (beanFactory.containsBean(LOAD_TIME_WEAVER_BEAN_NAME)) {
  47. beanFactory.addBeanPostProcessor(new LoadTimeWeaverAwareProcessor(beanFactory));
  48. // Set a temporary ClassLoader for type matching.
  49. beanFactory.setTempClassLoader(new ContextTypeMatchClassLoader(beanFactory.getBeanClassLoader()));
  50. }
  51.  
  52. /**
  53. * 從下面幾行代碼我們可以知道,Spring 往往很 "智能" 就是因為它會幫我們默認注冊一些有用的 bean,
  54. * 我們也可以選擇覆蓋
  55. */
  56.  
  57. // 如果沒有定義 "environment" 這個 bean,那么 Spring 會 "手動" 注冊一個
  58. if (!beanFactory.containsLocalBean(ENVIRONMENT_BEAN_NAME)) {
  59. beanFactory.registerSingleton(ENVIRONMENT_BEAN_NAME, getEnvironment());
  60. }
  61. // 如果沒有定義 "systemProperties" 這個 bean,那么 Spring 會 "手動" 注冊一個
  62. if (!beanFactory.containsLocalBean(SYSTEM_PROPERTIES_BEAN_NAME)) {
  63. beanFactory.registerSingleton(SYSTEM_PROPERTIES_BEAN_NAME, getEnvironment().getSystemProperties());
  64. }
  65. // 如果沒有定義 "systemEnvironment" 這個 bean,那么 Spring 會 "手動" 注冊一個
  66. if (!beanFactory.containsLocalBean(SYSTEM_ENVIRONMENT_BEAN_NAME)) {
  67. beanFactory.registerSingleton(SYSTEM_ENVIRONMENT_BEAN_NAME, getEnvironment().getSystemEnvironment());
  68. }
  69. }

在上面這塊代碼中,Spring 對一些特殊的 bean 進行了處理,讀者如果暫時還不能消化它們也沒有關系,慢慢往下看。

初始化所有的 singleton beans

我們的重點當然是 finishBeanFactoryInitialization(beanFactory); 這個巨頭了,這里會負責初始化所有的 singleton beans。

注意,后面的描述中,我都會使用初始化或預初始化來代表這個階段。主要是 Spring 需要在這個階段完成所有的 singleton beans 的實例化。

我們來總結一下,到目前為止,應該說 BeanFactory 已經創建完成,並且所有的實現了 BeanFactoryPostProcessor 接口的 Bean 都已經初始化並且其中的 postProcessBeanFactory(factory) 方法已經得到執行了。所有實現了 BeanPostProcessor 接口的 Bean 也都完成了初始化。

剩下的就是初始化其他還沒被初始化的 singleton beans 了,我們知道它們是單例的,如果沒有設置懶加載,那么 Spring 會在接下來初始化所有的 singleton beans。

// AbstractApplicationContext.java 834

  1. // 初始化剩余的 singleton beans
  2. protected void finishBeanFactoryInitialization(ConfigurableListableBeanFactory beanFactory) {
  3.  
  4. // 首先,初始化名字為 conversionService 的 Bean。本着送佛送到西的精神,我在附錄中簡單介紹了一下 ConversionService,因為這實在太實用了
  5. // 什么,看代碼這里沒有初始化 Bean 啊!
  6. // 注意了,初始化的動作包裝在 beanFactory.getBean(...) 中,這里先不說細節,先往下看吧
  7. if (beanFactory.containsBean(CONVERSION_SERVICE_BEAN_NAME) &&
  8. beanFactory.isTypeMatch(CONVERSION_SERVICE_BEAN_NAME, ConversionService.class)) {
  9. beanFactory.setConversionService(
  10. beanFactory.getBean(CONVERSION_SERVICE_BEAN_NAME, ConversionService.class));
  11. }
  12.  
  13. // Register a default embedded value resolver if no bean post-processor
  14. // (such as a PropertyPlaceholderConfigurer bean) registered any before:
  15. // at this point, primarily for resolution in annotation attribute values.
  16. if (!beanFactory.hasEmbeddedValueResolver()) {
  17. beanFactory.addEmbeddedValueResolver( new StringValueResolver() {
  18. @Override
  19. public String resolveStringValue(String strVal) {
  20. return getEnvironment().resolvePlaceholders(strVal);
  21. }
  22. });
  23. }
  24.  
  25. // 先初始化 LoadTimeWeaverAware 類型的 Bean
  26. // 一般用於織入第三方模塊,在 class 文件載入 JVM 的時候動態織入,這里不展開說
  27. String[] weaverAwareNames = beanFactory.getBeanNamesForType(LoadTimeWeaverAware.class, false, false);
  28. for (String weaverAwareName : weaverAwareNames) {
  29. getBean(weaverAwareName);
  30. }
  31.  
  32. // Stop using the temporary ClassLoader for type matching.
  33. beanFactory.setTempClassLoader( null);
  34.  
  35. // 沒什么別的目的,因為到這一步的時候,Spring 已經開始預初始化 singleton beans 了,
  36. // 肯定不希望這個時候還出現 bean 定義解析、加載、注冊。
  37. beanFactory.freezeConfiguration();
  38.  
  39. // 開始初始化剩下的
  40. beanFactory.preInstantiateSingletons();
  41. }

從上面最后一行往里看,我們又回到 DefaultListableBeanFactory 這個類了,這個類大家應該都不陌生了吧。

preInstantiateSingletons

// DefaultListableBeanFactory 728

  1. @Override
  2. public void preInstantiateSingletons() throws BeansException {
  3. if (this.logger.isDebugEnabled()) {
  4. this.logger.debug( "Pre-instantiating singletons in " + this);
  5. }
  6.  
  7. List<String> beanNames = new ArrayList<String>(this.beanDefinitionNames);
  8.  
  9. // 觸發所有的非懶加載的 singleton beans 的初始化操作
  10. for (String beanName : beanNames) {
  11.  
  12. // 合並父 Bean 中的配置,注意 <bean id="" class="" parent="" /> 中的 parent,用的不多吧,
  13. // 考慮到這可能會影響大家的理解,我在附錄中解釋了一下 "Bean 繼承",請移步
  14. RootBeanDefinition bd = getMergedLocalBeanDefinition(beanName);
  15.  
  16. // 非抽象、非懶加載的 singletons。如果配置了 'abstract = true',那是不需要初始化的
  17. if (!bd.isAbstract() && bd.isSingleton() && !bd.isLazyInit()) {
  18. // 處理 FactoryBean(讀者如果不熟悉 FactoryBean,請移步附錄區了解)
  19. if (isFactoryBean(beanName)) {
  20. // FactoryBean 的話,在 beanName 前面加上 ‘&’ 符號。再調用 getBean,getBean 方法別急
  21. final FactoryBean<?> factory = (FactoryBean<?>) getBean(FACTORY_BEAN_PREFIX + beanName);
  22. // 判斷當前 FactoryBean 是否是 SmartFactoryBean 的實現,此處忽略,直接跳過
  23. boolean isEagerInit;
  24. if (System.getSecurityManager() != null && factory instanceof SmartFactoryBean) {
  25. isEagerInit = AccessController.doPrivileged( new PrivilegedAction<Boolean>() {
  26. @Override
  27. public Boolean run() {
  28. return ((SmartFactoryBean<?>) factory).isEagerInit();
  29. }
  30. }, getAccessControlContext());
  31. }
  32. else {
  33. isEagerInit = (factory instanceof SmartFactoryBean &&
  34. ((SmartFactoryBean <?>) factory).isEagerInit());
  35. }
  36. if (isEagerInit) {
  37.  
  38. getBean(beanName);
  39. }
  40. }
  41. else {
  42. // 對於普通的 Bean,只要調用 getBean(beanName) 這個方法就可以進行初始化了
  43. getBean(beanName);
  44. }
  45. }
  46. }
  47.  
  48.  
  49. // 到這里說明所有的非懶加載的 singleton beans 已經完成了初始化
  50. // 如果我們定義的 bean 是實現了 SmartInitializingSingleton 接口的,那么在這里得到回調,忽略
  51. for (String beanName : beanNames) {
  52. Object singletonInstance = getSingleton(beanName);
  53. if (singletonInstance instanceof SmartInitializingSingleton) {
  54. final SmartInitializingSingleton smartSingleton = (SmartInitializingSingleton) singletonInstance;
  55. if (System.getSecurityManager() != null) {
  56. AccessController.doPrivileged( new PrivilegedAction<Object>() {
  57. @Override
  58. public Object run() {
  59. smartSingleton.afterSingletonsInstantiated();
  60. return null;
  61. }
  62. }, getAccessControlContext());
  63. }
  64. else {
  65. smartSingleton.afterSingletonsInstantiated();
  66. }
  67. }
  68. }
  69. }

接下來,我們就進入到 getBean(beanName) 方法了,這個方法我們經常用來從 BeanFactory 中獲取一個 Bean,而初始化的過程也封裝到了這個方法里。

getBean

在繼續前進之前,讀者應該具備 FactoryBean 的知識,如果讀者還不熟悉,請移步附錄部分了解 FactoryBean。

// AbstractBeanFactory 196

  1. @Override
  2. public Object getBean(String name) throws BeansException {
  3. return doGetBean(name, null, null, false);
  4. }
  5.  
  6. // 我們在剖析初始化 Bean 的過程,但是 getBean 方法我們經常是用來從容器中獲取 Bean 用的,注意切換思路,
  7. // 已經初始化過了就從容器中直接返回,否則就先初始化再返回
  8. @SuppressWarnings( "unchecked")
  9. protected <T> T doGetBean(
  10. final String name, final Class<T> requiredType, final Object[] args, boolean typeCheckOnly)
  11. throws BeansException {
  12. // 獲取一個 “正統的” beanName,處理兩種情況,一個是前面說的 FactoryBean(前面帶 ‘&’),
  13. // 一個是別名問題,因為這個方法是 getBean,獲取 Bean 用的,你要是傳一個別名進來,是完全可以的
  14. final String beanName = transformedBeanName(name);
  15.  
  16. // 注意跟着這個,這個是返回值
  17. Object bean;
  18.  
  19. // 檢查下是不是已經創建過了
  20. Object sharedInstance = getSingleton(beanName);
  21.  
  22. // 這里說下 args 唄,雖然看上去一點不重要。前面我們一路進來的時候都是 getBean(beanName),
  23. // 所以 args 其實是 null 的,但是如果 args 不為空的時候,那么意味着調用方不是希望獲取 Bean,而是創建 Bean
  24. if (sharedInstance != null && args == null) {
  25. if (logger.isDebugEnabled()) {
  26. if (isSingletonCurrentlyInCreation(beanName)) {
  27. logger.debug( "...");
  28. }
  29. else {
  30. logger.debug( "Returning cached instance of singleton bean '" + beanName + "'");
  31. }
  32. }
  33. // 下面這個方法:如果是普通 Bean 的話,直接返回 sharedInstance,
  34. // 如果是 FactoryBean 的話,返回它創建的那個實例對象
  35. // (FactoryBean 知識,讀者若不清楚請移步附錄)
  36. bean = getObjectForBeanInstance(sharedInstance, name, beanName, null);
  37. }
  38.  
  39. else {
  40. if (isPrototypeCurrentlyInCreation(beanName)) {
  41. // 當前線程已經創建過了此 beanName 的 prototype 類型的 bean,那么拋異常
  42. throw new BeanCurrentlyInCreationException(beanName);
  43. }
  44.  
  45. // 檢查一下這個 BeanDefinition 在容器中是否存在
  46. BeanFactory parentBeanFactory = getParentBeanFactory();
  47. if (parentBeanFactory != null && !containsBeanDefinition(beanName)) {
  48. // 如果當前容器不存在這個 BeanDefinition,試試父容器中有沒有
  49. String nameToLookup = originalBeanName(name);
  50. if (args != null) {
  51. // 返回父容器的查詢結果
  52. return (T) parentBeanFactory.getBean(nameToLookup, args);
  53. }
  54. else {
  55. // No args -> delegate to standard getBean method.
  56. return parentBeanFactory.getBean(nameToLookup, requiredType);
  57. }
  58. }
  59.  
  60. if (!typeCheckOnly) {
  61. // typeCheckOnly 為 false,將當前 beanName 放入一個 alreadyCreated 的 Set 集合中。
  62. markBeanAsCreated(beanName);
  63. }
  64.  
  65. /*
  66. * 稍稍總結一下:
  67. * 到這里的話,要准備創建 Bean 了,對於 singleton 的 Bean 來說,容器中還沒創建過此 Bean;
  68. * 對於 prototype 的 Bean 來說,本來就是要創建一個新的 Bean。
  69. */
  70. try {
  71. final RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);
  72. checkMergedBeanDefinition(mbd, beanName, args);
  73.  
  74. // 先初始化依賴的所有 Bean,這個很好理解。
  75. // 注意,這里的依賴指的是 depends-on 中定義的依賴
  76. String[] dependsOn = mbd.getDependsOn();
  77. if (dependsOn != null) {
  78. for (String dep : dependsOn) {
  79. // 檢查是不是有循環依賴,這里的循環依賴和我們前面說的循環依賴又不一樣,這里肯定是不允許出現的,不然要亂套了,讀者想一下就知道了
  80. if (isDependent(beanName, dep)) {
  81. throw new BeanCreationException(mbd.getResourceDescription(), beanName,
  82. "Circular depends-on relationship between '" + beanName + "' and '" + dep + "'");
  83. }
  84. // 注冊一下依賴關系
  85. registerDependentBean(dep, beanName);
  86. // 先初始化被依賴項
  87. getBean(dep);
  88. }
  89. }
  90.  
  91. // 創建 singleton 的實例
  92. if (mbd.isSingleton()) {
  93. sharedInstance = getSingleton(beanName, new ObjectFactory<Object>() {
  94. @Override
  95. public Object getObject() throws BeansException {
  96. try {
  97. // 執行創建 Bean,詳情后面再說
  98. return createBean(beanName, mbd, args);
  99. }
  100. catch (BeansException ex) {
  101. destroySingleton(beanName);
  102. throw ex;
  103. }
  104. }
  105. });
  106. bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
  107. }
  108.  
  109. // 創建 prototype 的實例
  110. else if (mbd.isPrototype()) {
  111. // It's a prototype -> create a new instance.
  112. Object prototypeInstance = null;
  113. try {
  114. beforePrototypeCreation(beanName);
  115. // 執行創建 Bean
  116. prototypeInstance = createBean(beanName, mbd, args);
  117. }
  118. finally {
  119. afterPrototypeCreation(beanName);
  120. }
  121. bean = getObjectForBeanInstance(prototypeInstance, name, beanName, mbd);
  122. }
  123.  
  124. // 如果不是 singleton 和 prototype 的話,需要委托給相應的實現類來處理
  125. else {
  126. String scopeName = mbd.getScope();
  127. final Scope scope = this.scopes.get(scopeName);
  128. if (scope == null) {
  129. throw new IllegalStateException("No Scope registered for scope name '" + scopeName + "'");
  130. }
  131. try {
  132. Object scopedInstance = scope.get(beanName, new ObjectFactory<Object>() {
  133. @Override
  134. public Object getObject() throws BeansException {
  135. beforePrototypeCreation(beanName);
  136. try {
  137. // 執行創建 Bean
  138. return createBean(beanName, mbd, args);
  139. }
  140. finally {
  141. afterPrototypeCreation(beanName);
  142. }
  143. }
  144. });
  145. bean = getObjectForBeanInstance(scopedInstance, name, beanName, mbd);
  146. }
  147. catch (IllegalStateException ex) {
  148. throw new BeanCreationException(beanName,
  149. "Scope '" + scopeName + "' is not active for the current thread; consider " +
  150. "defining a scoped proxy for this bean if you intend to refer to it from a singleton",
  151. ex);
  152. }
  153. }
  154. }
  155. catch (BeansException ex) {
  156. cleanupAfterBeanCreationFailure(beanName);
  157. throw ex;
  158. }
  159. }
  160.  
  161. // 最后,檢查一下類型對不對,不對的話就拋異常,對的話就返回了
  162. if (requiredType != null && bean != null && !requiredType.isInstance(bean)) {
  163. try {
  164. return getTypeConverter().convertIfNecessary(bean, requiredType);
  165. }
  166. catch (TypeMismatchException ex) {
  167. if (logger.isDebugEnabled()) {
  168. logger.debug( "Failed to convert bean '" + name + "' to required type '" +
  169. ClassUtils.getQualifiedName(requiredType) + "'", ex);
  170. }
  171. throw new BeanNotOfRequiredTypeException(name, requiredType, bean.getClass());
  172. }
  173. }
  174. return (T) bean;
  175. }

大家應該也猜到了,接下來當然是分析 createBean 方法:

protected abstract Object createBean(String beanName, RootBeanDefinition mbd, Object[] args) throws BeanCreationException; 

第三個參數 args 數組代表創建實例需要的參數,不就是給構造方法用的參數,或者是工廠 Bean 的參數嘛,不過要注意,在我們的初始化階段,args 是 null。

這回我們要到一個新的類了 AbstractAutowireCapableBeanFactory,看類名,AutowireCapable?類名是不是也說明了點問題了。

主要是為了以下場景,采用 @Autowired 注解注入屬性值:

  1. public class MessageServiceImpl implements MessageService {
  2. @Autowired
  3. private UserService userService;
  4.  
  5. public String getMessage() {
  6. return userService.getMessage();
  7. }
  8. }
<bean id="messageService" class="com.javadoop.example.MessageServiceImpl" /> 

好了,讀者要知道這么回事就可以了,繼續向前。

// AbstractAutowireCapableBeanFactory 447

  1. /**
  2. * Central method of this class: creates a bean instance,
  3. * populates the bean instance, applies post-processors, etc.
  4. * @see #doCreateBean
  5. */
  6. @Override
  7. protected Object createBean(String beanName, RootBeanDefinition mbd, Object[] args) throws BeanCreationException {
  8. if (logger.isDebugEnabled()) {
  9. logger.debug( "Creating instance of bean '" + beanName + "'");
  10. }
  11. RootBeanDefinition mbdToUse = mbd;
  12.  
  13. // 確保 BeanDefinition 中的 Class 被加載
  14. Class<?> resolvedClass = resolveBeanClass(mbd, beanName);
  15. if (resolvedClass != null && !mbd.hasBeanClass() && mbd.getBeanClassName() != null) {
  16. mbdToUse = new RootBeanDefinition(mbd);
  17. mbdToUse.setBeanClass(resolvedClass);
  18. }
  19.  
  20. // 准備方法覆寫,這里又涉及到一個概念:MethodOverrides,它來自於 bean 定義中的 <lookup-method />
  21. // 和 <replaced-method />,如果讀者感興趣,回到 bean 解析的地方看看對這兩個標簽的解析。
  22. // 我在附錄中也對這兩個標簽的相關知識點進行了介紹,讀者可以移步去看看
  23. try {
  24. mbdToUse.prepareMethodOverrides();
  25. }
  26. catch (BeanDefinitionValidationException ex) {
  27. throw new BeanDefinitionStoreException(mbdToUse.getResourceDescription(),
  28. beanName, "Validation of method overrides failed", ex);
  29. }
  30.  
  31. try {
  32. // 讓 BeanPostProcessor 在這一步有機會返回代理,而不是 bean 實例,
  33. // 要徹底了解清楚這個,需要去看 InstantiationAwareBeanPostProcessor 接口,這里就不展開說了
  34. Object bean = resolveBeforeInstantiation(beanName, mbdToUse);
  35. if (bean != null) {
  36. return bean;
  37. }
  38. }
  39. catch (Throwable ex) {
  40. throw new BeanCreationException(mbdToUse.getResourceDescription(), beanName,
  41. "BeanPostProcessor before instantiation of bean failed", ex);
  42. }
  43. // 重頭戲,創建 bean
  44. Object beanInstance = doCreateBean(beanName, mbdToUse, args);
  45. if (logger.isDebugEnabled()) {
  46. logger.debug( "Finished creating instance of bean '" + beanName + "'");
  47. }
  48. return beanInstance;
  49. }

創建 Bean

往里看 doCreateBean 這個方法:

  1. /**
  2. * Actually create the specified bean. Pre-creation processing has already happened
  3. * at this point, e.g. checking {@code postProcessBeforeInstantiation} callbacks.
  4. * <p>Differentiates between default bean instantiation, use of a
  5. * factory method, and autowiring a constructor.
  6. * @param beanName the name of the bean
  7. * @param mbd the merged bean definition for the bean
  8. * @param args explicit arguments to use for constructor or factory method invocation
  9. * @return a new instance of the bean
  10. * @throws BeanCreationException if the bean could not be created
  11. * @see #instantiateBean
  12. * @see #instantiateUsingFactoryMethod
  13. * @see #autowireConstructor
  14. */
  15. protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final Object[] args)
  16. throws BeanCreationException {
  17.  
  18. // Instantiate the bean.
  19. BeanWrapper instanceWrapper = null;
  20. if (mbd.isSingleton()) {
  21. instanceWrapper = this.factoryBeanInstanceCache.remove(beanName);
  22. }
  23. if (instanceWrapper == null) {
  24. // 說明不是 FactoryBean,這里實例化 Bean,這里非常關鍵,細節之后再說
  25. instanceWrapper = createBeanInstance(beanName, mbd, args);
  26. }
  27. // 這個就是 Bean 里面的 我們定義的類 的實例,很多地方我描述成 "bean 實例"
  28. final Object bean = (instanceWrapper != null ? instanceWrapper.getWrappedInstance() : null);
  29. // 類型
  30. Class<?> beanType = (instanceWrapper != null ? instanceWrapper.getWrappedClass() : null);
  31. mbd.resolvedTargetType = beanType;
  32.  
  33. // 建議跳過吧,涉及接口:MergedBeanDefinitionPostProcessor
  34. synchronized (mbd.postProcessingLock) {
  35. if (!mbd.postProcessed) {
  36. try {
  37. // MergedBeanDefinitionPostProcessor,這個我真不展開說了,直接跳過吧,很少用的
  38. applyMergedBeanDefinitionPostProcessors(mbd, beanType, beanName);
  39. }
  40. catch (Throwable ex) {
  41. throw new BeanCreationException(mbd.getResourceDescription(), beanName,
  42. "Post-processing of merged bean definition failed", ex);
  43. }
  44. mbd.postProcessed = true;
  45. }
  46. }
  47.  
  48. // Eagerly cache singletons to be able to resolve circular references
  49. // even when triggered by lifecycle interfaces like BeanFactoryAware.
  50. // 下面這塊代碼是為了解決循環依賴的問題,以后有時間,我再對循環依賴這個問題進行解析吧
  51. boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences &&
  52. isSingletonCurrentlyInCreation(beanName));
  53. if (earlySingletonExposure) {
  54. if (logger.isDebugEnabled()) {
  55. logger.debug( "Eagerly caching bean '" + beanName +
  56. "' to allow for resolving potential circular references");
  57. }
  58. addSingletonFactory(beanName, new ObjectFactory<Object>() {
  59. @Override
  60. public Object getObject() throws BeansException {
  61. return getEarlyBeanReference(beanName, mbd, bean);
  62. }
  63. });
  64. }
  65.  
  66. // Initialize the bean instance.
  67. Object exposedObject = bean;
  68. try {
  69. // 這一步也是非常關鍵的,這一步負責屬性裝配,因為前面的實例只是實例化了,並沒有設值,這里就是設值
  70. populateBean(beanName, mbd, instanceWrapper);
  71. if (exposedObject != null) {
  72. // 還記得 init-method 嗎?還有 InitializingBean 接口?還有 BeanPostProcessor 接口?
  73. // 這里就是處理 bean 初始化完成后的各種回調
  74. exposedObject = initializeBean(beanName, exposedObject, mbd);
  75. }
  76. }
  77. catch (Throwable ex) {
  78. if (ex instanceof BeanCreationException && beanName.equals(((BeanCreationException) ex).getBeanName())) {
  79. throw (BeanCreationException) ex;
  80. }
  81. else {
  82. throw new BeanCreationException(
  83. mbd.getResourceDescription(), beanName, "Initialization of bean failed", ex);
  84. }
  85. }
  86.  
  87. if (earlySingletonExposure) {
  88. //
  89. Object earlySingletonReference = getSingleton(beanName, false);
  90. if (earlySingletonReference != null) {
  91. if (exposedObject == bean) {
  92. exposedObject = earlySingletonReference;
  93. }
  94. else if (!this.allowRawInjectionDespiteWrapping && hasDependentBean(beanName)) {
  95. String[] dependentBeans = getDependentBeans(beanName);
  96. Set<String> actualDependentBeans = new LinkedHashSet<String>(dependentBeans.length);
  97. for (String dependentBean : dependentBeans) {
  98. if (!removeSingletonIfCreatedForTypeCheckOnly(dependentBean)) {
  99. actualDependentBeans.add(dependentBean);
  100. }
  101. }
  102. if (!actualDependentBeans.isEmpty()) {
  103. throw new BeanCurrentlyInCreationException(beanName,
  104. "Bean with name '" + beanName + "' has been injected into other beans [" +
  105. StringUtils.collectionToCommaDelimitedString(actualDependentBeans) +
  106. "] in its raw version as part of a circular reference, but has eventually been " +
  107. "wrapped. This means that said other beans do not use the final version of the " +
  108. "bean. This is often the result of over-eager type matching - consider using " +
  109. "'getBeanNamesOfType' with the 'allowEagerInit' flag turned off, for example.");
  110. }
  111. }
  112. }
  113. }
  114.  
  115. // Register bean as disposable.
  116. try {
  117. registerDisposableBeanIfNecessary(beanName, bean, mbd);
  118. }
  119. catch (BeanDefinitionValidationException ex) {
  120. throw new BeanCreationException(
  121. mbd.getResourceDescription(), beanName, "Invalid destruction signature", ex);
  122. }
  123.  
  124. return exposedObject;
  125. }

到這里,我們已經分析完了 doCreateBean 方法,總的來說,我們已經說完了整個初始化流程。

接下來我們挑 doCreateBean 中的三個細節出來說說。一個是創建 Bean 實例的 createBeanInstance 方法,一個是依賴注入的 populateBean 方法,還有就是回調方法 initializeBean。

注意了,接下來的這三個方法要認真說那也是極其復雜的,很多地方我就點到為止了,感興趣的讀者可以自己往里看,最好就是碰到不懂的,自己寫代碼去調試它。

創建 Bean 實例

我們先看看 createBeanInstance 方法。需要說明的是,這個方法如果每個分支都分析下去,必然也是極其復雜冗長的,我們挑重點說。此方法的目的就是實例化我們指定的類。

  1. protected BeanWrapper createBeanInstance(String beanName, RootBeanDefinition mbd, Object[] args) {
  2. // 確保已經加載了此 class
  3. Class<?> beanClass = resolveBeanClass(mbd, beanName);
  4.  
  5. // 校驗一下這個類的訪問權限
  6. if (beanClass != null && !Modifier.isPublic(beanClass.getModifiers()) && !mbd.isNonPublicAccessAllowed()) {
  7. throw new BeanCreationException(mbd.getResourceDescription(), beanName,
  8. "Bean class isn't public, and non-public access not allowed: " + beanClass.getName());
  9. }
  10.  
  11. if (mbd.getFactoryMethodName() != null) {
  12. // 采用工廠方法實例化,不熟悉這個概念的讀者請看附錄,注意,不是 FactoryBean
  13. return instantiateUsingFactoryMethod(beanName, mbd, args);
  14. }
  15.  
  16. // 如果不是第一次創建,比如第二次創建 prototype bean。
  17. // 這種情況下,我們可以從第一次創建知道,采用無參構造函數,還是構造函數依賴注入 來完成實例化
  18. boolean resolved = false;
  19. boolean autowireNecessary = false;
  20. if (args == null) {
  21. synchronized (mbd.constructorArgumentLock) {
  22. if (mbd.resolvedConstructorOrFactoryMethod != null) {
  23. resolved = true;
  24. autowireNecessary = mbd.constructorArgumentsResolved;
  25. }
  26. }
  27. }
  28. if (resolved) {
  29. if (autowireNecessary) {
  30. // 構造函數依賴注入
  31. return autowireConstructor(beanName, mbd, null, null);
  32. }
  33. else {
  34. // 無參構造函數
  35. return instantiateBean(beanName, mbd);
  36. }
  37. }
  38.  
  39. // 判斷是否采用有參構造函數
  40. Constructor<?>[] ctors = determineConstructorsFromBeanPostProcessors(beanClass, beanName);
  41. if (ctors != null ||
  42. mbd.getResolvedAutowireMode() == RootBeanDefinition.AUTOWIRE_CONSTRUCTOR ||
  43. mbd.hasConstructorArgumentValues() || !ObjectUtils.isEmpty(args)) {
  44. // 構造函數依賴注入
  45. return autowireConstructor(beanName, mbd, ctors, args);
  46. }
  47.  
  48. // 調用無參構造函數
  49. return instantiateBean(beanName, mbd);
  50. }

挑個簡單的無參構造函數構造實例來看看:

  1. protected BeanWrapper instantiateBean(final String beanName, final RootBeanDefinition mbd) {
  2. try {
  3. Object beanInstance;
  4. final BeanFactory parent = this;
  5. if (System.getSecurityManager() != null) {
  6. beanInstance = AccessController.doPrivileged( new PrivilegedAction<Object>() {
  7. @Override
  8. public Object run() {
  9.  
  10. return getInstantiationStrategy().instantiate(mbd, beanName, parent);
  11. }
  12. }, getAccessControlContext());
  13. }
  14. else {
  15. // 實例化
  16. beanInstance = getInstantiationStrategy().instantiate(mbd, beanName, parent);
  17. }
  18. // 包裝一下,返回
  19. BeanWrapper bw = new BeanWrapperImpl(beanInstance);
  20. initBeanWrapper(bw);
  21. return bw;
  22. }
  23. catch (Throwable ex) {
  24. throw new BeanCreationException(
  25. mbd.getResourceDescription(), beanName, "Instantiation of bean failed", ex);
  26. }
  27. }

我們可以看到,關鍵的地方在於:

beanInstance = getInstantiationStrategy().instantiate(mbd, beanName, parent); 

這里會進行實際的實例化過程,我們進去看看:

// SimpleInstantiationStrategy 59

  1. @Override
  2. public Object instantiate(RootBeanDefinition bd, String beanName, BeanFactory owner) {
  3.  
  4. // 如果不存在方法覆寫,那就使用 java 反射進行實例化,否則使用 CGLIB,
  5. // 方法覆寫 請參見附錄"方法注入"中對 lookup-method 和 replaced-method 的介紹
  6. if (bd.getMethodOverrides().isEmpty()) {
  7. Constructor<?> constructorToUse;
  8. synchronized (bd.constructorArgumentLock) {
  9. constructorToUse = (Constructor<?>) bd.resolvedConstructorOrFactoryMethod;
  10. if (constructorToUse == null) {
  11. final Class<?> clazz = bd.getBeanClass();
  12. if (clazz.isInterface()) {
  13. throw new BeanInstantiationException(clazz, "Specified class is an interface");
  14. }
  15. try {
  16. if (System.getSecurityManager() != null) {
  17. constructorToUse = AccessController.doPrivileged( new PrivilegedExceptionAction<Constructor<?>>() {
  18. @Override
  19. public Constructor<?> run() throws Exception {
  20. return clazz.getDeclaredConstructor((Class[]) null);
  21. }
  22. });
  23. }
  24. else {
  25. constructorToUse = clazz.getDeclaredConstructor((Class[]) null);
  26. }
  27. bd.resolvedConstructorOrFactoryMethod = constructorToUse;
  28. }
  29. catch (Throwable ex) {
  30. throw new BeanInstantiationException(clazz, "No default constructor found", ex);
  31. }
  32. }
  33. }
  34. // 利用構造方法進行實例化
  35. return BeanUtils.instantiateClass(constructorToUse);
  36. }
  37. else {
  38. // 存在方法覆寫,利用 CGLIB 來完成實例化,需要依賴於 CGLIB 生成子類,這里就不展開了
  39. return instantiateWithMethodInjection(bd, beanName, owner);
  40. }
  41. }

到這里,我們就算實例化完成了。我們開始說怎么進行屬性注入。

bean 屬性注入

// AbstractAutowireCapableBeanFactory 1203

  1. protected void populateBean(String beanName, RootBeanDefinition mbd, BeanWrapper bw) {
  2. // bean 實例的所有屬性都在這里了
  3. PropertyValues pvs = mbd.getPropertyValues();
  4.  
  5. if (bw == null) {
  6. if (!pvs.isEmpty()) {
  7. throw new BeanCreationException(
  8. mbd.getResourceDescription(), beanName, "Cannot apply property values to null instance");
  9. }
  10. else {
  11. // Skip property population phase for null instance.
  12. return;
  13. }
  14. }
  15.  
  16. // 到這步的時候,bean 實例化完成(通過工廠方法或構造方法),但是還沒開始屬性設值,
  17. // InstantiationAwareBeanPostProcessor 的實現類可以在這里對 bean 進行狀態修改,
  18. // 我也沒找到有實際的使用,所以我們暫且忽略這塊吧
  19. boolean continueWithPropertyPopulation = true;
  20. if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {
  21. for (BeanPostProcessor bp : getBeanPostProcessors()) {
  22. if (bp instanceof InstantiationAwareBeanPostProcessor) {
  23. InstantiationAwareBeanPostProcessor ibp = (InstantiationAwareBeanPostProcessor) bp;
  24. // 如果返回 false,代表不需要進行后續的屬性設值,也不需要再經過其他的 BeanPostProcessor 的處理
  25. if (!ibp.postProcessAfterInstantiation(bw.getWrappedInstance(), beanName)) {
  26. continueWithPropertyPopulation = false;
  27. break;
  28. }
  29. }
  30. }
  31. }
  32.  
  33. if (!continueWithPropertyPopulation) {
  34. return;
  35. }
  36.  
  37. if (mbd.getResolvedAutowireMode() == RootBeanDefinition.AUTOWIRE_BY_NAME ||
  38. mbd.getResolvedAutowireMode() == RootBeanDefinition.AUTOWIRE_BY_TYPE) {
  39. MutablePropertyValues newPvs = new MutablePropertyValues(pvs);
  40.  
  41. // 通過名字找到所有屬性值,如果是 bean 依賴,先初始化依賴的 bean。記錄依賴關系
  42. if (mbd.getResolvedAutowireMode() == RootBeanDefinition.AUTOWIRE_BY_NAME) {
  43. autowireByName(beanName, mbd, bw, newPvs);
  44. }
  45.  
  46. // 通過類型裝配。復雜一些
  47. if (mbd.getResolvedAutowireMode() == RootBeanDefinition.AUTOWIRE_BY_TYPE) {
  48. autowireByType(beanName, mbd, bw, newPvs);
  49. }
  50.  
  51. pvs = newPvs;
  52. }
  53.  
  54. boolean hasInstAwareBpps = hasInstantiationAwareBeanPostProcessors();
  55. boolean needsDepCheck = (mbd.getDependencyCheck() != RootBeanDefinition.DEPENDENCY_CHECK_NONE);
  56.  
  57. if (hasInstAwareBpps || needsDepCheck) {
  58. PropertyDescriptor[] filteredPds = filterPropertyDescriptorsForDependencyCheck(bw, mbd.allowCaching);
  59. if (hasInstAwareBpps) {
  60. for (BeanPostProcessor bp : getBeanPostProcessors()) {
  61. if (bp instanceof InstantiationAwareBeanPostProcessor) {
  62. InstantiationAwareBeanPostProcessor ibp = (InstantiationAwareBeanPostProcessor) bp;
  63. // 這里有個非常有用的 BeanPostProcessor 進到這里: AutowiredAnnotationBeanPostProcessor
  64. // 對采用 @Autowired、@Value 注解的依賴進行設值,這里的內容也是非常豐富的,不過本文不會展開說了,感興趣的讀者請自行研究
  65. pvs = ibp.postProcessPropertyValues(pvs, filteredPds, bw.getWrappedInstance(), beanName);
  66. if (pvs == null) {
  67. return;
  68. }
  69. }
  70. }
  71. }
  72. if (needsDepCheck) {
  73. checkDependencies(beanName, mbd, filteredPds, pvs);
  74. }
  75. }
  76. // 設置 bean 實例的屬性值
  77. applyPropertyValues(beanName, mbd, bw, pvs);
  78. }

initializeBean

屬性注入完成后,這一步其實就是處理各種回調了。

  1. protected Object initializeBean(final String beanName, final Object bean, RootBeanDefinition mbd) {
  2. if (System.getSecurityManager() != null) {
  3. AccessController.doPrivileged( new PrivilegedAction<Object>() {
  4. @Override
  5. public Object run() {
  6. invokeAwareMethods(beanName, bean);
  7. return null;
  8. }
  9. }, getAccessControlContext());
  10. }
  11. else {
  12. // 如果 bean 實現了 BeanNameAware、BeanClassLoaderAware 或 BeanFactoryAware 接口,回調
  13. invokeAwareMethods(beanName, bean);
  14. }
  15.  
  16. Object wrappedBean = bean;
  17. if (mbd == null || !mbd.isSynthetic()) {
  18. // BeanPostProcessor 的 postProcessBeforeInitialization 回調
  19. wrappedBean = applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName);
  20. }
  21.  
  22. try {
  23. // 處理 bean 中定義的 init-method,
  24. // 或者如果 bean 實現了 InitializingBean 接口,調用 afterPropertiesSet() 方法
  25. invokeInitMethods(beanName, wrappedBean, mbd);
  26. }
  27. catch (Throwable ex) {
  28. throw new BeanCreationException(
  29. (mbd != null ? mbd.getResourceDescription() : null),
  30. beanName, "Invocation of init method failed", ex);
  31. }
  32.  
  33. if (mbd == null || !mbd.isSynthetic()) {
  34. // BeanPostProcessor 的 postProcessAfterInitialization 回調
  35. wrappedBean = applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);
  36. }
  37. return wrappedBean;
  38. }

大家發現沒有,BeanPostProcessor 的兩個回調都發生在這邊,只不過中間處理了 init-method,是不是和讀者原來的認知有點不一樣了?

附錄

id 和 name

每個 Bean 在 Spring 容器中都有一個唯一的名字(beanName)和 0 個或多個別名(aliases)。

我們從 Spring 容器中獲取 Bean 的時候,可以根據 beanName,也可以通過別名。

beanFactory.getBean("beanName or alias"); 

在配置 <bean /> 的過程中,我們可以配置 id 和 name,看幾個例子就知道是怎么回事了。

<bean id="messageService" name="m1, m2, m3" class="com.javadoop.example.MessageServiceImpl"> 

以上配置的結果就是:beanName 為 messageService,別名有 3 個,分別為 m1、m2、m3。

<bean name="m1, m2, m3" class="com.javadoop.example.MessageServiceImpl" /> 

以上配置的結果就是:beanName 為 m1,別名有 2 個,分別為 m2、m3。

<bean class="com.javadoop.example.MessageServiceImpl"> 

beanName 為:com.javadoop.example.MessageServiceImpl#0,

別名 1 個,為: com.javadoop.example.MessageServiceImpl

<bean id="messageService" class="com.javadoop.example.MessageServiceImpl"> 

以上配置的結果就是:beanName 為 messageService,沒有別名。

配置是否允許 Bean 覆蓋、是否允許循環依賴

我們說過,默認情況下,allowBeanDefinitionOverriding 屬性為 null。如果在同一配置文件中 Bean id 或 name 重復了,會拋錯,但是如果不是同一配置文件中,會發生覆蓋。

可是有些時候我們希望在系統啟動的過程中就嚴格杜絕發生 Bean 覆蓋,因為萬一出現這種情況,會增加我們排查問題的成本。

循環依賴說的是 A 依賴 B,而 B 又依賴 A。或者是 A 依賴 B,B 依賴 C,而 C 卻依賴 A。默認 allowCircularReferences 也是 null。

它們兩個屬性是一起出現的,必然可以在同一個地方一起進行配置。

添加這兩個屬性的作者 Juergen Hoeller 在這個 jira 的討論中說明了怎么配置這兩個屬性。

  1. public class NoBeanOverridingContextLoader extends ContextLoader {
  2.  
  3. @Override
  4. protected void customizeContext(ServletContext servletContext, ConfigurableWebApplicationContext applicationContext) {
  5. super.customizeContext(servletContext, applicationContext);
  6. AbstractRefreshableApplicationContext arac = (AbstractRefreshableApplicationContext) applicationContext;
  7. arac.setAllowBeanDefinitionOverriding( false);
  8. }
  9. }
  1. public class MyContextLoaderListener extends org.springframework.web.context.ContextLoaderListener {
  2.  
  3. @Override
  4. protected ContextLoader createContextLoader() {
  5. return new NoBeanOverridingContextLoader();
  6. }
  7.  
  8. }
  1. <listener>
  2. <listener-class>com.javadoop.MyContextLoaderListener</listener-class>
  3. </listener>

如果以上方式不能滿足你的需求,請參考這個鏈接:解決spring中不同配置文件中存在name或者id相同的bean可能引起的問題

profile

我們可以把不同環境的配置分別配置到單獨的文件中,舉個例子:

  1. <beans profile="development"
  2. xmlns="http://www.springframework.org/schema/beans"
  3. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  4. xmlns:jdbc="http://www.springframework.org/schema/jdbc"
  5. xsi:schemaLocation="...">
  6.  
  7. <jdbc:embedded-database id="dataSource">
  8. <jdbc:script location="classpath:com/bank/config/sql/schema.sql"/>
  9. <jdbc:script location="classpath:com/bank/config/sql/test-data.sql"/>
  10. </jdbc:embedded-database>
  11. </beans>
  1. <beans profile="production"
  2. xmlns="http://www.springframework.org/schema/beans"
  3. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  4. xmlns:jee="http://www.springframework.org/schema/jee"
  5. xsi:schemaLocation="...">
  6.  
  7. <jee:jndi-lookup id="dataSource" jndi-name="java:comp/env/jdbc/datasource"/>
  8. </beans>

應該不必做過多解釋了吧,看每個文件第一行的 profile=""。

當然,我們也可以在一個配置文件中使用:

  1. <beans xmlns="http://www.springframework.org/schema/beans"
  2. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  3. xmlns:jdbc="http://www.springframework.org/schema/jdbc"
  4. xmlns:jee="http://www.springframework.org/schema/jee"
  5. xsi:schemaLocation="...">
  6.  
  7. <beans profile="development">
  8. <jdbc:embedded-database id="dataSource">
  9. <jdbc:script location="classpath:com/bank/config/sql/schema.sql"/>
  10. <jdbc:script location="classpath:com/bank/config/sql/test-data.sql"/>
  11. </jdbc:embedded-database>
  12. </beans>
  13.  
  14. <beans profile="production">
  15. <jee:jndi-lookup id="dataSource" jndi-name="java:comp/env/jdbc/datasource"/>
  16. </beans>
  17. </beans>

理解起來也很簡單吧。

接下來的問題是,怎么使用特定的 profile 呢?Spring 在啟動的過程中,會去尋找 “spring.profiles.active” 的屬性值,根據這個屬性值來的。那怎么配置這個值呢?

Spring 會在這幾個地方尋找 spring.profiles.active 的屬性值:操作系統環境變量、JVM 系統變量、web.xml 中定義的參數、JNDI。

最簡單的方式莫過於在程序啟動的時候指定:

-Dspring.profiles.active="profile1,profile2" 

profile 可以激活多個

當然,我們也可以通過代碼的形式從 Environment 中設置 profile:

  1. AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
  2. ctx.getEnvironment().setActiveProfiles( "development");
  3. ctx.register(SomeConfig. class, StandaloneDataConfig.class, JndiDataConfig.class);
  4. ctx.refresh(); // 重啟

如果是 Spring Boot 的話更簡單,我們一般會創建 application.properties、application-dev.properties、application-prod.properties 等文件,其中 application.properties 配置各個環境通用的配置,application-{profile}.properties 中配置特定環境的配置,然后在啟動的時候指定 profile:

java -Dspring.profiles.active=prod -jar JavaDoop.jar 

如果是單元測試中使用的話,在測試類中使用 @ActiveProfiles 指定,這里就不展開了。

工廠模式生成 Bean

請讀者注意 factory-bean 和 FactoryBean 的區別。這節說的是前者,是說靜態工廠或實例工廠,而后者是 Spring 中的特殊接口,代表一類特殊的 Bean,附錄的下面一節會介紹 FactoryBean。

設計模式里,工廠方法模式分靜態工廠和實例工廠,我們分別看看 Spring 中怎么配置這兩個,來個代碼示例就什么都清楚了。

靜態工廠:

  1. <bean id= "clientService"
  2. class="examples.ClientService"
  3. factory-method= "createInstance"/>
  1. public class ClientService {
  2. private static ClientService clientService = new ClientService();
  3. private ClientService() {}
  4.  
  5. // 靜態方法
  6. public static ClientService createInstance() {
  7. return clientService;
  8. }
  9. }

實例工廠:

  1. <bean id="serviceLocator" class="examples.DefaultServiceLocator">
  2. <!-- inject any dependencies required by this locator bean -->
  3. </bean>
  4.  
  5. <bean id="clientService"
  6. factory-bean="serviceLocator"
  7. factory-method="createClientServiceInstance"/>
  8.  
  9. <bean id="accountService"
  10. factory-bean="serviceLocator"
  11. factory-method="createAccountServiceInstance"/>
  1. public class DefaultServiceLocator {
  2.  
  3. private static ClientService clientService = new ClientServiceImpl();
  4.  
  5. private static AccountService accountService = new AccountServiceImpl();
  6.  
  7. public ClientService createClientServiceInstance() {
  8. return clientService;
  9. }
  10.  
  11. public AccountService createAccountServiceInstance() {
  12. return accountService;
  13. }
  14. }

FactoryBean

FactoryBean 適用於 Bean 的創建過程比較復雜的場景,比如數據庫連接池的創建。

  1. public interface FactoryBean<T> {
  2. T getObject() throws Exception;
  3. Class<T> getObjectType();
  4. boolean isSingleton();
  5. }
  1. public class Person {
  2. private Car car ;
  3. private void setCar(Car car){ this.car = car; }
  4. }

我們假設現在需要創建一個 Person 的 Bean,首先我們需要一個 Car 的實例,我們這里假設 Car 的實例創建很麻煩,那么我們可以把創建 Car 的復雜過程包裝起來:

  1. public class MyCarFactoryBean implements FactoryBean<Car>{
  2. private String make;
  3. private int year ;
  4.  
  5. public void setMake(String m){ this.make =m ; }
  6.  
  7. public void setYear(int y){ this.year = y; }
  8.  
  9. public Car getObject(){
  10. // 這里我們假設 Car 的實例化過程非常復雜,反正就不是幾行代碼可以寫完的那種
  11. CarBuilder cb = CarBuilder.car();
  12.  
  13. if(year!=0) cb.setYear(this.year);
  14. if(StringUtils.hasText(this.make)) cb.setMake( this.make );
  15. return cb.factory();
  16. }
  17.  
  18. public Class<Car> getObjectType() { return Car.class ; }
  19.  
  20. public boolean isSingleton() { return false; }
  21. }

我們看看裝配的時候是怎么配置的:

  1. <bean class = "com.javadoop.MyCarFactoryBean" id = "car">
  2. <property name = "make" value ="Honda"/>
  3. <property name = "year" value ="1984"/>
  4. </bean>
  5. <bean class = "com.javadoop.Person" id = "josh">
  6. <property name = "car" ref = "car"/>
  7. </bean>

看到不一樣了嗎?id 為 “car” 的 bean 其實指定的是一個 FactoryBean,不過配置的時候,我們直接讓配置 Person 的 Bean 直接依賴於這個 FactoryBean 就可以了。中間的過程 Spring 已經封裝好了。

說到這里,我們再來點干貨。我們知道,現在還用 xml 配置 Bean 依賴的越來越少了,更多時候,我們可能會采用 java config 的方式來配置,這里有什么不一樣呢?

  1. @Configuration
  2. public class CarConfiguration {
  3.  
  4. @Bean
  5. public MyCarFactoryBean carFactoryBean(){
  6. MyCarFactoryBean cfb = new MyCarFactoryBean();
  7. cfb.setMake( "Honda");
  8. cfb.setYear( 1984);
  9. return cfb;
  10. }
  11.  
  12. @Bean
  13. public Person aPerson(){
  14. Person person = new Person();
  15. // 注意這里的不同
  16. person.setCar(carFactoryBean().getObject());
  17. return person;
  18. }
  19. }

這個時候,其實我們的思路也很簡單,把 MyCarFactoryBean 看成是一個簡單的 Bean 就可以了,不必理會什么 FactoryBean,它是不是 FactoryBean 和我們沒關系。

初始化 Bean 的回調

有以下四種方案:

<bean id="exampleInitBean" class="examples.ExampleBean" init-method="init"/> 
  1. public class AnotherExampleBean implements InitializingBean {
  2.  
  3. public void afterPropertiesSet() {
  4. // do some initialization work
  5. }
  6. }
  1. @Bean(initMethod = "init")
  2. public Foo foo() {
  3. return new Foo();
  4. }
  1. @PostConstruct
  2. public void init() {
  3.  
  4. }

銷毀 Bean 的回調

<bean id="exampleInitBean" class="examples.ExampleBean" destroy-method="cleanup"/> 
  1. public class AnotherExampleBean implements DisposableBean {
  2.  
  3. public void destroy() {
  4. // do some destruction work (like releasing pooled connections)
  5. }
  6. }
  1. @Bean(destroyMethod = "cleanup")
  2. public Bar bar() {
  3. return new Bar();
  4. }
  1. @PreDestroy
  2. public void cleanup() {
  3.  
  4. }

ConversionService

既然文中說到了這個,順便提一下好了。

最有用的場景就是,它用來將前端傳過來的參數和后端的 controller 方法上的參數進行綁定的時候用。

像前端傳過來的字符串、整數要轉換為后端的 String、Integer 很容易,但是如果 controller 方法需要的是一個枚舉值,或者是 Date 這些非基礎類型(含基礎類型包裝類)值的時候,我們就可以考慮采用 ConversionService 來進行轉換。

  1. <bean id="conversionService"
  2. class="org.springframework.context.support.ConversionServiceFactoryBean">
  3. <property name="converters">
  4. <list>
  5. <bean class="com.javadoop.learning.utils.StringToEnumConverterFactory"/>
  6. </list>
  7. </property>
  8. </bean>

ConversionService 接口很簡單,所以要自定義一個 convert 的話也很簡單。

下面再說一個實現這種轉換很簡單的方式,那就是實現 Converter 接口。

來看一個很簡單的例子,這樣比什么都管用。

  1. public class StringToDateConverter implements Converter<String, Date> {
  2.  
  3. @Override
  4. public Date convert(String source) {
  5. try {
  6. return DateUtils.parseDate(source, "yyyy-MM-dd", "yyyy-MM-dd HH:mm:ss", "yyyy-MM-dd HH:mm", "HH:mm:ss", "HH:mm");
  7. } catch (ParseException e) {
  8. return null;
  9. }
  10. }
  11. }

只要注冊這個 Bean 就可以了。這樣,前端往后端傳的時間描述字符串就很容易綁定成 Date 類型了,不需要其他任何操作。

Bean 繼承

在初始化 Bean 的地方,我們說過了這個:

RootBeanDefinition bd = getMergedLocalBeanDefinition(beanName); 

這里涉及到的就是 <bean parent="" /> 中的 parent 屬性,我們來看看 Spring 中是用這個來干什么的。

首先,我們要明白,這里的繼承和 java 語法中的繼承沒有任何關系,不過思路是相通的。child bean 會繼承 parent bean 的所有配置,也可以覆蓋一些配置,當然也可以新增額外的配置。

Spring 中提供了繼承自 AbstractBeanDefinition 的 ChildBeanDefinition 來表示 child bean。

看如下一個例子:

  1. <bean id= "inheritedTestBean" abstract="true" class="org.springframework.beans.TestBean">
  2. <property name= "name" value="parent"/>
  3. <property name= "age" value="1"/>
  4. </bean>
  5.  
  6. <bean id= "inheritsWithDifferentClass" class="org.springframework.beans.DerivedTestBean"
  7. parent= "inheritedTestBean" init-method="initialize">
  8.  
  9. <property name= "name" value="override"/>
  10. </bean>

parent bean 設置了 abstract="true" 所以它不會被實例化,child bean 繼承了 parent bean 的兩個屬性,但是對 name 屬性進行了覆寫。

child bean 會繼承 scope、構造器參數值、屬性值、init-method、destroy-method 等等。

當然,我不是說 parent bean 中的 abstract = true 在這里是必須的,只是說如果加上了以后 Spring 在實例化 singleton beans 的時候會忽略這個 bean。

比如下面這個極端 parent bean,它沒有指定 class,所以毫無疑問,這個 bean 的作用就是用來充當模板用的 parent bean,此處就必須加上 abstract = true。

  1. <bean id="inheritedTestBeanWithoutClass" abstract="true">
  2. <property name="name" value="parent"/>
  3. <property name="age" value="1"/>
  4. </bean>

方法注入

一般來說,我們的應用中大多數的 Bean 都是 singleton 的。singleton 依賴 singleton,或者 prototype 依賴 prototype 都很好解決,直接設置屬性依賴就可以了。

但是,如果是 singleton 依賴 prototype 呢?這個時候不能用屬性依賴,因為如果用屬性依賴的話,我們每次其實拿到的還是第一次初始化時候的 bean。

一種解決方案就是不要用屬性依賴,每次獲取依賴的 bean 的時候從 BeanFactory 中取。這個也是大家最常用的方式了吧。怎么取,我就不介紹了,大部分 Spring 項目大家都會定義那么個工具類的。

另一種解決方案就是這里要介紹的通過使用 Lookup method。

lookup-method

我們來看一下 Spring Reference 中提供的一個例子:

  1. package fiona.apple;
  2.  
  3. // no more Spring imports!
  4.  
  5. public abstract class CommandManager {
  6.  
  7. public Object process(Object commandState) {
  8. // grab a new instance of the appropriate Command interface
  9. Command command = createCommand();
  10. // set the state on the (hopefully brand new) Command instance
  11. command.setState(commandState);
  12. return command.execute();
  13. }
  14.  
  15. // okay... but where is the implementation of this method?
  16. protected abstract Command createCommand();
  17. }

xml 配置 <lookup-method />

  1. <!-- a stateful bean deployed as a prototype (non-singleton) -->
  2. <bean id="myCommand" class="fiona.apple.AsyncCommand" scope="prototype">
  3. <!-- inject dependencies here as required -->
  4. </bean>
  5.  
  6. <!-- commandProcessor uses statefulCommandHelper -->
  7. <bean id="commandManager" class="fiona.apple.CommandManager">
  8. <lookup-method name="createCommand" bean="myCommand"/>
  9. </bean>

Spring 采用 CGLIB 生成字節碼的方式來生成一個子類。我們定義的類不能定義為 final class,抽象方法上也不能加 final。

lookup-method 上的配置也可以采用注解來完成,這樣就可以不用配置 <lookup-method /> 了,其他不變:

  1. public abstract class CommandManager {
  2.  
  3. public Object process(Object commandState) {
  4. MyCommand command = createCommand();
  5. command.setState(commandState);
  6. return command.execute();
  7. }
  8.  
  9. @Lookup("myCommand")
  10. protected abstract Command createCommand();
  11. }

注意,既然用了注解,要配置注解掃描:<context:component-scan base-package="com.javadoop" />

甚至,我們可以像下面這樣:

  1. public abstract class CommandManager {
  2.  
  3. public Object process(Object commandState) {
  4. MyCommand command = createCommand();
  5. command.setState(commandState);
  6. return command.execute();
  7. }
  8.  
  9. @Lookup
  10. protected abstract MyCommand createCommand();
  11. }

上面的返回值用了 MyCommand,當然,如果 Command 只有一個實現類,那返回值也可以寫 Command。

replaced-method

記住它的功能,就是替換掉 bean 中的一些方法。

  1. public class MyValueCalculator {
  2.  
  3. public String computeValue(String input) {
  4. // some real code...
  5. }
  6.  
  7. // some other methods...
  8. }

方法覆寫,注意要實現 MethodReplacer 接口:

  1. public class ReplacementComputeValue implements org.springframework.beans.factory.support.MethodReplacer {
  2.  
  3. public Object reimplement(Object o, Method m, Object[] args) throws Throwable {
  4. // get the input value, work with it, and return a computed result
  5. String input = (String) args[0];
  6. ...
  7. return ...;
  8. }
  9. }

配置也很簡單:

  1. <bean id="myValueCalculator" class="x.y.z.MyValueCalculator">
  2. <!-- 定義 computeValue 這個方法要被替換掉 -->
  3. <replaced-method name="computeValue" replacer="replacementComputeValue">
  4. <arg-type>String</arg-type>
  5. </replaced-method>
  6. </bean>
  7.  
  8. <bean id="replacementComputeValue" class="a.b.c.ReplacementComputeValue"/>

arg-type 明顯不是必須的,除非存在方法重載,這樣必須通過參數類型列表來判斷這里要覆蓋哪個方法。

BeanPostProcessor

應該說 BeanPostProcessor 概念在 Spring 中也是比較重要的。我們看下接口定義:

  1. public interface BeanPostProcessor {
  2.  
  3. Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException;
  4.  
  5. Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException;
  6.  
  7. }

看這個接口中的兩個方法名字我們大體上可以猜測 bean 在初始化之前會執行 postProcessBeforeInitialization 這個方法,初始化完成之后會執行 postProcessAfterInitialization 這個方法。但是,這么理解是非常片面的。

首先,我們要明白,除了我們自己定義的 BeanPostProcessor 實現外,Spring 容器在啟動時自動給我們也加了幾個。如在獲取 BeanFactory 的 obtainFactory() 方法結束后的 prepareBeanFactory(factory),大家仔細看會發現,Spring 往容器中添加了這兩個 BeanPostProcessor:ApplicationContextAwareProcessor、ApplicationListenerDetector。

我們回到這個接口本身,讀者請看第一個方法,這個方法接受的第一個參數是 bean 實例,第二個參數是 bean 的名字,重點在返回值將會作為新的 bean 實例,所以,沒事的話這里不能隨便返回個 null。

那意味着什么呢?我們很容易想到的就是,我們這里可以對一些我們想要修飾的 bean 實例做一些事情。但是對於 Spring 框架來說,它會決定是不是要在這個方法中返回 bean 實例的代理,這樣就有更大的想象空間了。

最后,我們說說如果我們自己定義一個 bean 實現 BeanPostProcessor 的話,它的執行時機是什么時候?

如果仔細看了代碼分析的話,其實很容易知道了,在 bean 實例化完成、屬性注入完成之后,會執行回調方法,具體請參見類 AbstractAutowireCapableBeanFactory#initBean 方法。

首先會回調幾個實現了 Aware 接口的 bean,然后就開始回調 BeanPostProcessor 的 postProcessBeforeInitialization 方法,之后是回調 init-method,然后再回調 BeanPostProcessor 的 postProcessAfterInitialization 方法。

總結

按理說,總結應該寫在附錄前面,我就不講究了。

在花了那么多時間后,這篇文章終於算是基本寫完了,大家在驚嘆 Spring 給我們做了那么多的事的時候,應該透過現象看本質,去理解 Spring 寫得好的地方,去理解它的設計思想。

本文的缺陷在於對 Spring 預初始化 singleton beans 的過程分析不夠,主要是代碼量真的比較大,分支旁路眾多。同時,雖然附錄條目不少,但是龐大的 Spring 真的引出了很多的概念,希望日后有精力可以慢慢補充一些。

 https://blog.csdn.net/nuomizhende45/article/details/81158383

 

 

 

 

 

 

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM