這篇文章接着上篇Spring IOC實現原理筆記(一)的測試代碼,從ClassPathXmlApplicationContext開始分析spring的裝載對象到容器的實現。
先放出繼承圖,對源碼跟蹤有幫助
public ClassPathXmlApplicationContext(String configLocation) throws BeansException {
this(new String[]{configLocation}, true, (ApplicationContext)null);
}
public ClassPathXmlApplicationContext(String[] configLocations, boolean refresh, ApplicationContext parent) throws BeansException {
super(parent);//設置父ApplicationContext
this.setConfigLocations(configLocations);//設置配置文件路徑
if(refresh) {
this.refresh();
}
}
有點意外,邏輯這麼簡單,然後跟蹤refresh方法進去,跳到了AbstractApplicationContext類,然後就頭大了,如下
public void refresh() throws BeansException, IllegalStateException {
Object var1 = this.startupShutdownMonitor;
synchronized(this.startupShutdownMonitor) {
this.prepareRefresh();//步驟1:準備刷新上下文環境
ConfigurableListableBeanFactory beanFactory = this.obtainFreshBeanFactory();//步驟2:初始化BeanFactory,並進行XML文件讀取
this.prepareBeanFactory(beanFactory);//步驟3:對BeanFactory各種功能填充
try {
this.postProcessBeanFactory(beanFactory);//步驟4:子類富態方法做額外的處理
this.invokeBeanFactoryPostProcessors(beanFactory);//步驟5:激活各種BeanFactory處理器
this.registerBeanPostProcessors(beanFactory);//步驟6:註冊攔截Bean創建的Bean處理器
this.initMessageSource();//步驟7:爲上下文初始化Message源,即國際化處理
this.initApplicationEventMulticaster();//步驟8:初始化應用消息廣播器
this.onRefresh();//步驟9:留給子類具體實現
this.registerListeners();//步驟10:註冊消息廣播器
this.finishBeanFactoryInitialization(beanFactory);//步驟11:初始化剩餘的單實例(非惰性)
this.finishRefresh();//步驟12:完成刷新過程,通知生命週期處理器,發出刷新事件
} catch (BeansException var9) {
this.logger.warn("Exception encountered during context initialization - cancelling refresh attempt", var9);
this.destroyBeans();//步驟14
this.cancelRefresh(var9);//步驟15
throw var9;
} finally {
this.resetCommonCaches();//步驟13:清理緩存
}
}
}
因爲本篇只寫關於加載XML配置信息,而上面的【步驟2】就做了這一步工作,跟蹤進去
protected ConfigurableListableBeanFactory obtainFreshBeanFactory() {
this.refreshBeanFactory();//這裏開始初始化BeanFactory並解析XML信息
ConfigurableListableBeanFactory beanFactory = this.getBeanFactory();
return beanFactory;
}
protected final void refreshBeanFactory() throws BeansException {
if(this.hasBeanFactory()) {//銷燬原有的BeanFactory
this.destroyBeans();
this.closeBeanFactory();
}
try {
DefaultListableBeanFactory beanFactory = this.createBeanFactory();
beanFactory.setSerializationId(this.getId());
this.customizeBeanFactory(beanFactory);
this.loadBeanDefinitions(beanFactory);//進入解析XML階段
Object var2 = this.beanFactoryMonitor;
synchronized(this.beanFactoryMonitor) {
this.beanFactory = beanFactory;
}
} catch (IOException var5) {
...
}
}
可以看到refreshBeanFactory方法會把當前的BeanFactory銷燬,然後初始化一個DefaultListableBeanFactory實例(作爲AbstractRefreshableApplicationContext的一個成員變量)。
DefaultListableBeanFactory繼承圖如下
接下來跟蹤解析XML階段:loadBeanDefinitions(beanFactory);
protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException {
XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);
beanDefinitionReader.setEnvironment(this.getEnvironment());
beanDefinitionReader.setResourceLoader(this);
beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this));//爲解決聯網獲取xsd,dtd文件的問題
this.initBeanDefinitionReader(beanDefinitionReader);
this.loadBeanDefinitions(beanDefinitionReader);
}
上面看到創建了一個XmlBeanDefinitionReader實例,並持有beanFactory的引用(構造方法傳入的)。XmlBeanDefinitionReader就是用於解析XML,並把解析的bean封裝成BeanDefinitionHolder,然後傳給BeanDefinitionRegistry(實際就是DefaultListableBeanFactory),進行相關的緩存。
下面出場的是XML解析的主力XmlBeanDefinitionReader
...上面的loadBeanDefinitions方法,經過山路十八彎,最終進入下面的方法
protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource) throws BeanDefinitionStoreException {
try {
//解析XML成一個Document(包:org.w3c.dom)對象
Document doc = this.doLoadDocument(inputSource, resource);
//解析內容封裝成BeanDefinitionHolder,然後放入BeanDefinitionRegistry中緩存起來
return this.registerBeanDefinitions(doc, resource);
} catch...
}
最終解析會進入下面的方法
protected void doRegisterBeanDefinitions(Element root) {
BeanDefinitionParserDelegate parent = this.delegate;
this.delegate = this.createDelegate(this.getReaderContext(), root, parent);
//先判斷profile屬性進行判斷是否符合,不符合就退出不用解析了
if(this.delegate.isDefaultNamespace(root)) {
String profileSpec = root.getAttribute("profile");
if(StringUtils.hasText(profileSpec)) {
String[] specifiedProfiles = StringUtils.tokenizeToStringArray(profileSpec, ",; ");
if(!this.getReaderContext().getEnvironment().acceptsProfiles(specifiedProfiles)) {
return;
}
}
}
...
this.parseBeanDefinitions(root, this.delegate);//開始解析
...
}
protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) {
if(delegate.isDefaultNamespace(root)) {
NodeList nl = root.getChildNodes();
for(int i = 0; i < nl.getLength(); ++i) {
Node node = nl.item(i);
if(node instanceof Element) {
Element ele = (Element)node;
if(delegate.isDefaultNamespace(ele)) {
this.parseDefaultElement(ele, delegate);
} else {
delegate.parseCustomElement(ele);
}
}
}
} else {
delegate.parseCustomElement(root);
}
}
對XML解析分兩種情況,第一是對默認節點的解析(如bean,alias,import,beans等),第二是對自定義節點的解析(如tx,p,aop等)。解析得到的bean的相關配置信息封裝成BeanDefinitionHolder,然後傳給BeanDefinitionRegistry(實際就是DefaultListableBeanFactory,可從繼承圖看到出),進行相關緩存操作。
如果是解析自定義節點則是需要有相對應的NamespaceHandler,後續分析AOP,會分析NamespaceHandler
//解析默認節點
private void parseDefaultElement(Element ele, BeanDefinitionParserDelegate delegate) {
if(delegate.nodeNameEquals(ele, "import")) {
this.importBeanDefinitionResource(ele);
} else if(delegate.nodeNameEquals(ele, "alias")) {
this.processAliasRegistration(ele);
} else if(delegate.nodeNameEquals(ele, "bean")) {
this.processBeanDefinition(ele, delegate);
} else if(delegate.nodeNameEquals(ele, "beans")) {
this.doRegisterBeanDefinitions(ele);
}
}
//解析自定義節點
public BeanDefinition parseCustomElement(Element ele, BeanDefinition containingBd) {
String namespaceUri = this.getNamespaceURI(ele);
NamespaceHandler handler = this.readerContext.getNamespaceHandlerResolver().resolve(namespaceUri);
if(handler == null) {
...
return null;
} else {
return handler.parse(ele, new ParserContext(this.readerContext, this, containingBd));
}
}
看一下BeanDefinitionHolder類
public class BeanDefinitionHolder implements BeanMetadataElement {
private final BeanDefinition beanDefinition;
private final String beanName;
private final String[] aliases;
...
}
它記錄了一個bean的配置信息,還有bean的名稱、別名,通過它可以把一個bean的配置信息,別名緩存到BeanDefinitionRegistry
總結:
XML解析主要用了SAX技術解析,然後把解析的bean的信息封裝成BeanDefinition存入BeanDefinitionRegistry中,方便之後從容器中獲取實例時根據解析好的信息進行實例化。當然由於Spring提供了多種強大的配置,其中解析的過程並不輕鬆,要經過很多邏輯的處理。
在Spring IOC實現原理筆記(一)中有一個BeanFactoryPostProcessor實例,它在哪裏被調用的呢?其實就在【步驟5】
this.invokeBeanFactoryPostProcessors(beanFactory);
參考:
Spring源碼(4.2.2.RELEASE)
《Spring源碼深度解析》