Spring框架中的IoC容器源碼分析(上)
一、Spring IoC基礎
1、bean與BeanFactory的關係
2、BeanFactory與ApplicationContext區別
BeanFactory
是Spring
框架中IoC
容器的頂層接口,它只是用來定義一些基礎功能,定義一些基礎規範,而 ApplicationContext
是它的一個子接口,所以ApplicationContext
是具備BeanFactory
提供的全部功能的。
通常,我們稱BeanFactory
爲Spring IoC
的基礎容器,ApplicationContext
是容器的高級接口,比 BeanFactory
要擁有更多的功能,比如說國際化支持
和資源訪問
(xml
,java
配置類)等等。
3、啓動 IoC 容器的方式
3.1 Java環境下啓動IoC容器
ClassPathXmlApplicationContext
:從類的根路徑下加載配置文件(推薦使用
)FileSystemXmlApplicationContext
:從磁盤路徑上加載配置文件AnnotationConfigApplicationContext
:純註解模式下啓動Spring
容器
3.2 Web環境下啓動IoC容器
-
從xml啓動容器
<!DOCTYPE web-app PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN" "http://java.sun.com/dtd/web-app_2_3.dtd" > <web-app> <display-name>Archetype Created Web Application</display-name> <!--配置Spring ioc容器的配置文件--> <context-param> <param-name>contextConfigLocation</param-name> <param-value>classpath:applicationContext.xml</param-value> </context-param> <!--使用監聽器啓動Spring的IOC容器--> <listener> <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class> </listener> </web-app>
-
從配置類啓動容器
<!DOCTYPE web-app PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN" "http://java.sun.com/dtd/web-app_2_3.dtd" > <web-app> <display-name>Archetype Created Web Application</display-name> <!--告訴ContextloaderListener知道我們使用註解的方式啓動ioc容器--> <context-param> <param-name>contextClass</param-name> <param-value>org.springframework.web.context.support.AnnotationConfigWebApplicationContext</param-value> </context-param> <!--配置啓動類的全限定類名--> <context-param> <param-name>contextConfigLocation</param-name> <param-value>com.riemann.SpringConfig</param-value> </context-param> <!--使用監聽器啓動Spring的IOC容器--> <listener> <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class> </listener> </web-app>
二、Spring IoC高級特性
1、lazy-Init 延遲加載
1.1 Bean的延遲加載(延遲創建)
ApplicationContext
容器的默認行爲是在啓動服務器時將所有 singleton bean
提前進行實例化。提前實例化意味着作爲初始化過程的一部分,ApplicationContext
實例會創建並配置所有的singleton bean
。
比如:
<bean id="testBean" class="com.riemann.LazyBean" />
該bean默認的設置爲:
<bean id="testBean" calss="com.riemann.LazyBean" lazy-init="false" />
lazy-init="false"
,立即加載,表示在spring
啓動時,立刻進行實例化。
如果不想讓一個singleton bean
在 ApplicationContext
實現初始化時被提前實例化,那麼可以將bean
設置爲延遲實例化。
<bean id="testBean" calss="com.riemann.LazyBean" lazy-init="true" />
設置 lazy-init
爲 true
的 bean
將不會在 ApplicationContext
啓動時提前被實例化,而是第一次向容器
通過 getBean
索取 bean
時實例化的。
如果一個 bean
的 scope
屬性爲 scope="pototype"
時,即使設置了 lazy-init="false"
,容器啓動時也不會實例化bean
,而是調用 getBean
方法實例化的。
1.2 應用場景
- 開啓延遲加載一定程度提高容器啓動和運轉性能
- 對於不常使用的
Bean
設置延遲加載,這樣偶爾使用的時候再加載,不必要從一開始該Bean
就佔用資源
2、FactoryBean 和 BeanFactory
BeanFactory
接口是容器的頂級接口,定義了容器的一些基礎行爲,負責生產和管理Bean
的一個工廠,具體使用它下面的子接口類型,比如ApplicationContext
。
此處我們重點分析FactoryBean
:
Spring
中Bean
有兩種,一種是普通Bean
,一種是工廠Bean(FactoryBean)
,FactoryBean
可以生成
某一個類型的Bean
實例(返回給我們),也就是說我們可以藉助於它自定義Bean
的創建過程。
Bean
創建的三種方式中的靜態方法
和實例化方法
和FactoryBean
作用類似,FactoryBean
使用較多,尤
其在Spring
框架一些組件中會使用,還有其他框架和Spring
框架整合時使用。
我們下面來看個例子:
FactoryBean接口:
// 可以讓我們自定義Bean的創建過程(完成複雜Bean的定義)
public interface FactoryBean<T> {
@Nullable
// 返回FactoryBean創建的Bean實例,如果isSingleton返回true,則該實例會放到Spring容器 的單例對象緩存池中Map
T getObject() throws Exception;
@Nullable
// 返回FactoryBean創建的Bean類型
Class<?> getObjectType();
// 返回作用域是否單例
default boolean isSingleton() {
return true;
}
}
Company類:
@Data
public class Company {
private String name;
private String address;
private int scale;
}
CompanyFactoryBean類:
public class CompanyFactoryBean implements FactoryBean<Company> {
private String companyInfo; // 公司名稱,地址,規模
public void setCompanyInfo(String companyInfo) {
this.companyInfo = companyInfo;
}
@Override
public Company getObject() throws Exception {
// 模擬創建複雜對象Company
Company company = new Company();
String[] strings = companyInfo.split(",");
company.setName(strings[0]);
company.setAddress(strings[1]);
company.setScale(Integer.parseInt(strings[2]));
return company;
}
@Override
public Class<?> getObjectType() {
return Company.class;
}
@Override
public boolean isSingleton() {
return true;
}
}
xml配置:
<bean id="companyBean" class="com.riemann.factory.CompanyFactoryBean">
<property name="companyInfo" value="騰訊,南山科技園,60000"/>
</bean>
測試,獲取FactoryBean
產生的對象:
Object companyBean = applicationContext.getBean("companyBean"); System.out.println("bean:" + companyBean);
// 結果如下
bean:Company{name='騰訊', address='南山科技園', scale=60000}
測試,獲取FactoryBean
,需要在id
之前添加“&”
:
Object companyBean = applicationContext.getBean("&companyBean");
System.out.println("bean:" + companyBean);
// 結果如下
bean:com.riemann.factory.CompanyFactoryBean@53f6fd09
3、後置處理器
Spring
提供了兩種後處理bean
的擴展接口,分別爲 BeanPostProcessor
和
BeanFactoryPostProcessor
,兩者在使用上是有所區別的。
工廠初始化(BeanFactory
)—> Bean
對象
- 在
BeanFactory
初始化之後可以使用BeanFactoryPostProcessor
進行後置處理做一些事情 - 在
Bean
對象實例化(並不是Bean
的整個生命週期完成)之後可以使用BeanPostProcessor
進行後置處理做一些事情
注意:對象不一定是springbean
,而springbean
一定是個對象
3.1 BeanPostProcessor
BeanPostProcessor是針對Bean級別的處理,可以針對某個具體的Bean。
public interface BeanPostProcessor {
@Nullable
default Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
return bean;
}
@Nullable
default Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
return bean;
}
}
該接口提供了兩個方法,分別在Bean
的初始化方法前和初始化方法後執行,具體這個初始化方法指的是 什麼方法,類似我們在定義bean
時,定義了init-method
所指定的方法。
定義一個類實現了BeanPostProcessor
,默認是會對整個Spring
容器中所有的bean
進行處理。如果要對具體的某個bean
處理,可以通過方法參數判斷,兩個類型參數分別爲Object
和String
,第一個參數是每個bean
的實例,第二個參數是每個bean
的name
或者id
屬性的值。所以我們可以通過第二個參數,來判斷我們將要處理的具體的bean
。
注意:處理是發生在Spring
容器的實例化和依賴注入之後
。
3.2 BeanFactoryPostProcessor
BeanFactory
級別的處理,是針對整個Bean
的工廠進行處理,典型應用:PropertyPlaceholderConfigurer
@FunctionalInterface
public interface BeanFactoryPostProcessor {
void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException;
}
此接口只提供了一個方法,方法參數爲ConfigurableListableBeanFactory
,該參數類型定義了一些方法:
其中有個方法名爲getBeanDefinition
的方法,我們可以根據此方法,找到我們定義bean
的 BeanDefinition
對象。然後我們可以對定義的屬性進行修改,以下是BeanDefinition
中的方法:
方法名字類似我們bean
標籤的屬性,setBeanClassName
對應bean
標籤中的class
屬性,所以當我們拿到BeanDefinition
對象時,我們可以手動修改bean
標籤中所定義的屬性值。
BeanDefinition
對象:我們在 XML
中定義的 bean
標籤,Spring
解析 bean
標籤成爲一個 JavaBean
, 這個JavaBean
就是 BeanDefinition
。
注意:調用 BeanFactoryPostProcessor
方法時,這時候bean
還沒有實例化,此時 bean
剛被解析成 BeanDefinition
對象。
三、Spring IOC 源碼深度剖析
1、Spring IoC容器初始化主體流程
1.1 Spring IoC的容器體系
IoC
容器是Spring
的核心模塊,是抽象了對象管理、依賴關係管理
的框架解決方案。Spring
提供了很多的容器,其中 BeanFactory
是頂層容器(根容器)
,不能被實例化,它定義了所有 IoC
容器必須遵從的一套原則,具體的容器實現可以增加額外的功能,比如我們常用到的ApplicationContext
,其下更具體的實現如 ClassPathXmlApplicationContext
包含了解析 xml
等一系列的內容,AnnotationConfigApplicationContext
則是包含了註解解析
等一系列的內容。Spring IoC
容器繼承體系非常聰明,需要使用哪個層次用哪個層次即可,不必使用功能大而全的。
BeanFactory
頂級接口方法棧如下:
BeanFactory
容器繼承體系如下:
通過其接口設計,我們可以看到我們一貫使用的 ApplicationContext
除了繼承BeanFactory
的子接口, 還繼承了ResourceLoader
、MessageSource
等接口,因此其提供的功能也就更豐富了。
下面我們以 ClasspathXmlApplicationContext
爲例,深入源碼說明 IoC
容器的初始化流程。
1.2 Bean生命週期關鍵時機點
思路:創建一個類 RiemannBean
,讓其實現幾個特殊的接口,並分別在接口實現的構造器
、接口方法
中 斷點,觀察線程調用棧,分析出 Bean
對象創建和管理關鍵點的觸發時機。
RiemannBean
類
public class RiemannBean implements InitializingBean, ApplicationContextAware {
private ItBean itBean;
public void setItBean(ItBean itBean) {
this.itBean = itBean;
}
public RiemannBean() {
System.out.println("RiemannBean 構造器...");
}
@Override
public void afterPropertiesSet() throws Exception {
System.out.println("RiemannBean afterPropertiesSet...");
}
public void print() {
System.out.println("RiemannBean print方法業務邏輯執行...");
}
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
System.out.println("RiemannBean setApplicationContext...");
}
}
BeanPostProcessor
接口實現類
public class MyBeanPostProcessor implements BeanPostProcessor {
public MyBeanPostProcessor() {
System.out.println("BeanPostProcessor 實現類構造函數...");
}
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
if("riemannBean".equals(beanName)) {
System.out.println("BeanPostProcessor 實現類 postProcessBeforeInitialization 方法被調用中...");
}
return bean;
}
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
if("riemannBean".equals(beanName)) {
System.out.println("BeanPostProcessor 實現類 postProcessAfterInitialization 方法被調用中...");
}
return bean;
}
}
BeanFactoryPostProcessor
接口實現類
public class MyBeanFactoryPostProcessor implements BeanFactoryPostProcessor {
public MyBeanFactoryPostProcessor() {
System.out.println("BeanFactoryPostProcessor的實現類構造函數...");
}
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
System.out.println("BeanFactoryPostProcessor的實現方法調用中...");
}
}
applicationContext.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd
">
<bean id="riemannBean" class="com.riemann.RiemannBean">
<property name="ItBean" ref="itBean"/>
</bean>
<bean id="myBeanFactoryPostProcessor" class="com.riemann.MyBeanFactoryPostProcessor"/>
<bean id="myBeanPostProcessor" class="com.riemann.MyBeanPostProcessor"/>
<!--循環依賴問題-->
<bean id="itBean" class="com.riemann.ItBean">
<property name="riemannBean" ref="riemannBean"/>
</bean>
</beans>
IoC
容器源碼分析用例
public class IocTest {
/**
* Ioc 容器源碼分析基礎案例
*/
@Test
public void testIoC() {
// ApplicationContext是容器的高級接口,BeanFacotry(頂級容器/根容器,規範了/定義了容器的基礎行爲)
// Spring應用上下文,官方稱之爲 IoC容器(錯誤的認識:容器就是map而已;準確來說,map是ioc容器的一個成員,
// 叫做單例池, singletonObjects,容器是一組組件和過程的集合,包括BeanFactory、單例池、BeanPostProcessor等以及之間的協作流程)
/**
* Ioc容器創建管理Bean對象的,Spring Bean是有生命週期的
* 構造器執行、初始化方法執行、Bean後置處理器的before/after方法:AbstractApplicationContext#refresh#finishBeanFactoryInitialization
* Bean工廠後置處理器初始化、方法執行:AbstractApplicationContext#refresh#invokeBeanFactoryPostProcessors
* Bean後置處理器初始化:AbstractApplicationContext#refresh#registerBeanPostProcessors
*/
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("classpath:applicationContext.xml");
RiemannBean riemannBean = applicationContext.getBean(RiemannBean.class);
System.out.println(riemannBean);
}
}
上面註釋是我已經斷點調試完驗證的結果
1.3 小結
根據上面的調試分析,我們發現Bean
對象創建的幾個關鍵時機點代碼層級的調用都在 AbstractApplicationContext
類 的 refresh
方法中,可⻅這個方法對於Spring IoC
容器初始化來說相當關鍵,彙總如下:
關鍵點 | 觸發代碼 |
---|---|
構造器 | AbstractApplicationContext#refresh#finishBeanFactoryInitialization(beanFactory) |
BeanFactoryPostProcessor 初始化 | AbstractApplicationContext#refresh#invokeBeanFactoryPostProcessors(beanFactory) |
BeanFactoryPostProcessor 方法調用 | AbstractApplicationContext#refresh#invokeBeanFactoryPostProcessors(beanFactory) |
BeanPostProcessor 初始化 | AbstractApplicationContext#refresh#registerBeanPostProcessors(beanFactory) |
BeanPostProcessor 方法調用 | AbstractApplicationContext#refresh#finishBeanFactoryInitialization(beanFactory) |
2、Spring IoC容器初始化主流程源碼剖析
由上分析可知,Spring IoC
容器初始化的關鍵環節就在 AbstractApplicationContext#refresh()
方法中 ,我們查看 refresh
方法來俯瞰容器創建的主體流程,主體流程下的具體子流程我們後面再來討論。
@Override
public void refresh() throws BeansException, IllegalStateException {
/**
* 對象鎖加鎖
*/
synchronized (this.startupShutdownMonitor) {
// Prepare this context for refreshing.
/**
* 刷新前的預處理
* 表示在真正做refresh操作之前需要準備做的事情:
* 設置Spring容器啓動時間
* 開啓活躍狀態,撤銷關閉狀態
* 驗證環境信息裏一些必須存在的屬性
*/
prepareRefresh();
// Tell the subclass to refresh the internal bean factory.
/**
* 獲取BeanFactory:默認實現是DefaultListableBeanFactory
* Bean獲取並封裝成BeanDefinition對象
* 加載BeanDefinition 並註冊到BeanDefinitionRegistry
*/
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
// Prepare the bean factory for use in this context.
/**
* 獲取BeanFactory的準備工作:BeanFactory進行一些設置,比如context的類加載器等。
*/
prepareBeanFactory(beanFactory);
try {
// Allows post-processing of the bean factory in context subclasses.
/**
* BeanFactory準備工作完成後進行的後置處理工作
*/
postProcessBeanFactory(beanFactory);
// Invoke factory processors registered as beans in the context.
/**
* 實例化實現了BeanFactoryPostProcessor接口的Bean,並調用接口方法
*/
invokeBeanFactoryPostProcessors(beanFactory);
// Register bean processors that intercept bean creation.
/**
* 註冊BeanPostProcessor(Bean的後置處理器),在創建bean的前後等執行
*/
registerBeanPostProcessors(beanFactory);
// Initialize message source for this context.
/**
* 初始化MessageSource組件(做國際化功能、消息綁定、消息解析)
*/
initMessageSource();
// Initialize event multicaster for this context.
/**
* 初始化事件派發器
*/
initApplicationEventMulticaster();
// Initialize other special beans in specific context subclasses.
/**
* 子類重寫這個方法,在容器刷新的時候可以自定義邏輯,如創建Tomcat、Jetty等WEB服務器
*/
onRefresh();
// Check for listener beans and register them.
/**
* 註冊應用的監聽器,就是註冊實現了ApplicationListener接口的監聽器bean
*/
registerListeners();
// Instantiate all remaining (non-lazy-init) singletons.
/**
* 初始化所有剩下的非懶加載的單例bean
* 初始化創建非懶加載方式的單例Bean實例(未設置屬性)
* 填充屬性
* 初始化方法調用(比如調用afterPropertiesSet方法、init-method方法)
* 調用BeanPostProcessor(Bean的後置處理器)對實例bean進行後置處理
*/
finishBeanFactoryInitialization(beanFactory);
// Last step: publish corresponding event.
/**
* 完成context的刷新,主要是調用LifecycleProcessor的onRefresh()方法,並且發佈事件(ContextRefreshEvent)
*/
finishRefresh();
}
catch (BeansException ex) {
if (logger.isWarnEnabled()) {
logger.warn("Exception encountered during context initialization - " +
"cancelling refresh attempt: " + ex);
}
// Destroy already created singletons to avoid dangling resources.
destroyBeans();
// Reset 'active' flag.
cancelRefresh(ex);
// Propagate exception to caller.
throw ex;
}
finally {
// Reset common introspection caches in Spring's core, since we
// might not ever need metadata for singleton beans anymore...
resetCommonCaches();
}
}
}