Spring IOC容器初始化程序(一)資源定位程序
最近復習了一遍Spring IOC容器的初始化程序,結合書籍《Spring原始碼深度決議》總結了一下,IOC容器的初始化程序,大概分為以下三點:
-
定位資源
定位相關的組態檔,掃描相關注解
-
加載資源
將配置資訊加載到記憶體中
-
注冊
根據載入的配置資訊,初始化物件,并將其裝載至容器中

整體加載的時序圖

工程目錄

POM檔案
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.example</groupId>
<artifactId>springTest</artifactId>
<version>1.0-SNAPSHOT</version>
<dependencies>
<!-- https://mvnrepository.com/artifact/org.springframework/spring-context -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.0.2.RELEASE</version>
</dependency>
</dependencies>
</project>
測驗代碼
public class ServiceB {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("spring.xml");
Object serviceA = context.getBean("serviceA");
System.out.println(serviceA);
}
}
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"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:cache="http://www.springframework.org/schema/cache"
xmlns:p="http://www.springframework.org/schema/p"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/cache
http://www.springframework.org/schema/cache/spring-cache.xsd">
<context:component-scan base-package="com.donkeys.spring"/>
<bean id="serviceA" class="com.donkeys.spring.service.ServiceA"></bean>
</beans>
資源定位程序決議
代碼中,使用的是ClassPathXmlApplicationContext類去加載Sping的組態檔,所以先給出該類類圖

ClassPathXmlApplicationContext構造方法
public ClassPathXmlApplicationContext(
String[] configLocations, boolean refresh, @Nullable ApplicationContext parent)
throws BeansException {
super(parent);
//根據傳入的組態檔名稱,呼叫父類的setConfigLocations方法,決議組態檔路徑,
setConfigLocations(configLocations);
//refresh = true
//refresh() 方法會重啟整個容器
if (refresh) {
refresh();
}
}
構造方法中一共做了2件事,首先是設定組態檔的路徑,然后對整個容器進行重繪,
這里我們重點關注refresh()方法;進入refresh()方法
AbstractApplicationContext類的refresh()方法
@Override
public void refresh() throws BeansException, IllegalStateException {
synchronized (this.startupShutdownMonitor) {
// Prepare this context for refreshing.
//為重繪前做準備
prepareRefresh();
// Tell the subclass to refresh the internal bean factory.
//獲取IOC容器,這里就是處理資源定位以組態檔加載/注冊的方法
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
// Prepare the bean factory for use in this context.
prepareBeanFactory(beanFactory);
try {
// Allows post-processing of the bean factory in context subclasses.
postProcessBeanFactory(beanFactory);
// Invoke factory processors registered as beans in the context.
invokeBeanFactoryPostProcessors(beanFactory);
// Register bean processors that intercept bean creation.
registerBeanPostProcessors(beanFactory);
// Initialize message source for this context.
initMessageSource();
// Initialize event multicaster for this context.
initApplicationEventMulticaster();
// Initialize other special beans in specific context subclasses.
onRefresh();
// Check for listener beans and register them.
registerListeners();
// Instantiate all remaining (non-lazy-init) singletons.
finishBeanFactoryInitialization(beanFactory);
// Last step: publish corresponding event.
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();
}
}
}
這里主要關注**obtainFreshBeanFactory()**方法
/**
* Tell the subclass to refresh the internal bean factory.
* @return the fresh BeanFactory instance
* @see #refreshBeanFactory()
* @see #getBeanFactory()
*/
protected ConfigurableListableBeanFactory obtainFreshBeanFactory() {
//重繪IOC容器
//這里使用了委派設計模式,父類定義了抽象的refreshBeanFactory方法,具體呼叫實作呼叫子類的refreshBeanFactory方法
refreshBeanFactory();
//獲取一個新的容器
ConfigurableListableBeanFactory beanFactory = getBeanFactory();
if (logger.isDebugEnabled()) {
logger.debug("Bean factory for " + getDisplayName() + ": " + beanFactory);
}
return beanFactory;
}
**obtainFreshBeanFactory()**方法總共干了2件事,
- 重置容器,refreshBeanFactory()方法中會設定相關標志,清除舊的容器,同時為Spring背景關系生成一個新的容器
- 獲取一個新的容器
AbstractRefreshableApplicationContext的refreshBeanFactory()方法
下面我們進入**refreshBeanFactory()**方法
/**
* This implementation performs an actual refresh of this context's underlying
* bean factory, shutting down the previous bean factory (if any) and
* initializing a fresh bean factory for the next phase of the context's lifecycle.
* 該方法會將之前的bean工廠全部關閉,并初始化一個全新的bean 工廠類 用于Spring 背景關系的生命周期
* bean工廠就是IOC容器
*/
@Override
protected final void refreshBeanFactory() throws BeansException {
//判斷是否之前也有容器
//如果有就銷毀掉
if (hasBeanFactory()) {
destroyBeans();
closeBeanFactory();
}
try {
//創建一個新的工廠
DefaultListableBeanFactory beanFactory = createBeanFactory();
beanFactory.setSerializationId(getId());
customizeBeanFactory(beanFactory);
//讀取Bean物件的定義
//這里也是使用的委派設計模式
loadBeanDefinitions(beanFactory);
synchronized (this.beanFactoryMonitor) {
this.beanFactory = beanFactory;
}
}
catch (IOException ex) {
throw new ApplicationContextException("I/O error parsing bean definition source for " + getDisplayName(), ex);
}
}
這里創建了新的容器工廠,同時將新的工廠傳入了loadBeanDefinitions()方法中,下面來看一下在loadBeanDefinitions方法中具體做了什么操作,
AbstractXmlApplicationContext的 loadBeanDefinitions(DefaultListableBeanFactory beanFactory)方法
在AbstractRefreshableApplicationContext類的refreshBeanFactory方法中,呼叫了loadBeanDefinitions方法,但是這個方法它的一個抽象方法,具體實作應由子類去實作,我們在程式啟動時,使用的ClassPathXmlApplicationContext類,根據文章開頭的類圖可以知道,這里會呼叫子類AbstractXmlApplicationContext的loadBeanDefinitions方法去完成本次加載
@Override
protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException {
// Create a new XmlBeanDefinitionReader for the given BeanFactory.
//使用默認的beanFactory去創建 XmlBeanDefinitionReader
XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);
// Configure the bean definition reader with this context's
// resource loading environment.
//設定資源的加載環境
beanDefinitionReader.setEnvironment(this.getEnvironment());
//設定資源讀取器
beanDefinitionReader.setResourceLoader(this);
//設定物體決議器
beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this));
// Allow a subclass to provide custom initialization of the reader,
// then proceed with actually loading the bean definitions.
//初始化 bean物件定義讀取器
initBeanDefinitionReader(beanDefinitionReader);
//使用初始化完成的讀取器,呼叫loadBeanDefinitions方法
loadBeanDefinitions(beanDefinitionReader);
}
總的來說,這里只干了一件事,那就是初始化配置
//設定資源讀取器
beanDefinitionReader.setResourceLoader(this);
//初始化 bean物件定義讀取器
initBeanDefinitionReader(beanDefinitionReader);
-
設定資源讀取器,這里設定的資源讀取器就是當前這個物件本身
通過類圖我們可以發現我們這個類的頂級父類ApplicationContext,繼承自DefaultResourceLoader這個類,該類實作了ResourceLoader介面,說明這個類的實體化物件本身是具有資源讀取器的功能的
-
初始化bean物件定義讀取器,這里設定xml檔案的校驗方式
下面我們繼續看**loadBeanDefinitions(XmlBeanDefinitionReader reader)**方法
protected void loadBeanDefinitions(XmlBeanDefinitionReader reader) throws BeansException, IOException {
//從子類物件中獲取到資源定位
Resource[] configResources = getConfigResources();
if (configResources != null) {
//XmlBeanDefinitionReader 讀取器呼叫其父類的
reader.loadBeanDefinitions(configResources);
}
String[] configLocations = getConfigLocations();
if (configLocations != null) {
reader.loadBeanDefinitions(configLocations);
}
}
先看這一行
//這行代碼的具體實作是由子類完成的,通過類圖可以知道AbstractXmlApplicationContext的子類為ClassPathXmlApplicationContext
//這里主要將我們最開始在構造方法中設定好的組態檔進行回傳
Resource[] configResources = getConfigResources();
再看這一行
//如果回傳的組態檔不為空,就將回傳的已經封裝好的資源檔案進行讀取,
if (configResources != null) {
//XmlBeanDefinitionReader 讀取器呼叫其父類的
reader.loadBeanDefinitions(configResources);
}
至此,資源檔案定位程序已經加載完成,后續就是讀取和注冊,整個IOC容器加載程序中最重要的是讀取程序,我們可以從剛剛的定位程序來看,雖然叫定位程序,但是其實就是一個組態檔讀取器的初始化程序,這個程序會設定相關的決議策略以及校驗策略,最終讀取器生成后,就可以將我們早早設定好的組態檔載入然后進行讀取,
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/271329.html
標籤:java
下一篇:Synchronized深度刨析
