本文以ClassPathXmlApplicationContext初始化流程为例,从构造方法进入开始跟踪代码:
public ClassPathXmlApplicationContext(String[] configLocations, boolean refresh, @Nullable ApplicationContext parent) throws BeansException {
super(parent);
this.setConfigLocations(configLocations);
if (refresh) {
this.refresh(); //该方法为spring容器初始化的核心方法
}
}
在构造方法中的this.refresh()方法为核心方法,而spring框架大部分初始化工作也是在该方法中完成的,跟踪代码我们可以发现,this.refresh()方法是在其父类AbstractApplicationContext实现的,进入该方法:
AbstractApplicationContext
public void refresh() throws BeansException, IllegalStateException {
Object var1 = this.startupShutdownMonitor;
synchronized(this.startupShutdownMonitor) {
this.prepareRefresh();
//在该方法中对xml文件中的各个标签进行解析
ConfigurableListableBeanFactory beanFactory = this.obtainFreshBeanFactory();
this.prepareBeanFactory(beanFactory);
try {
this.postProcessBeanFactory(beanFactory);
this.invokeBeanFactoryPostProcessors(beanFactory);
this.registerBeanPostProcessors(beanFactory);
this.initMessageSource();
this.initApplicationEventMulticaster();
this.onRefresh();
this.registerListeners();
this.finishBeanFactoryInitialization(beanFactory);
this.finishRefresh();
} catch (BeansException var9) {
if (this.logger.isWarnEnabled()) {
this.logger.warn("Exception encountered during context initialization - cancelling refresh attempt: " + var9);
}
this.destroyBeans();
this.cancelRefresh(var9);
throw var9;
} finally {
this.resetCommonCaches();
}
}
}
本文我们主要讨论xml文件中各种默认标签以及自定义标签(包括<context:component-scan>)的解析过程。因此,本文的重点放在this.obtainFreshBeanFactory()方法,进入该方法的实现:
AbstractApplicationContext
protected ConfigurableListableBeanFactory obtainFreshBeanFactory() {
this.refreshBeanFactory();
return this.getBeanFactory();
}
其中,this.refreshBeanFactory()是一个被Abstract关键字修饰的抽象方法,这是Spring中典型的模板模式的应用,具体由AbstractApplicationContext的子类AbstractRefreshableApplicationContext实现,类之间的继承关系如下:
public abstract class AbstractRefreshableApplicationContext extends AbstractApplicationContext
AbstractRefreshableApplicationContext
protected final void refreshBeanFactory() throws BeansException {
if (this.hasBeanFactory()) {
this.destroyBeans();
this.closeBeanFactory();
}
try {
DefaultListableBeanFactory beanFactory = this.createBeanFactory();
beanFactory.setSerializationId(this.getId());
this.customizeBeanFactory(beanFactory);
this.loadBeanDefinitions(beanFactory);
Object var2 = this.beanFactoryMonitor;
synchronized(this.beanFactoryMonitor) {
this.beanFactory = beanFactory;
}
} catch (IOException var5) {
throw new ApplicationContextException("I/O error parsing bean definition source for " + this.getDisplayName(), var5);
}
}
我们重点来看this.loadBeanDefinitions(beanFactory)方法,这里又是对模板设计模式的应用,在本类中使用Abstract关键字修饰,具体实现在子类AbstractXmlApplicationContext中,类之间的继承关系如下:
public abstract class AbstractXmlApplicationContext extends AbstractRefreshableConfigApplicationContext
AbstractXmlApplicationContext
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));
this.initBeanDefinitionReader(beanDefinitionReader);
this.loadBeanDefinitions(beanDefinitionReader);
}
在该方法中使用了委派的设计模式,创建出一个XmlBeanDefinitionReader对象,委派给这个对象来解析Spring配置文件中的各种标签,我们再次进入this.loadBeanDefinitions(beanDefinitionReader)方法
AbstractXmlApplicationContext
protected void loadBeanDefinitions(XmlBeanDefinitionReader reader) throws BeansException, IOException {
Resource[] configResources = this.getConfigResources();
if (configResources != null) {
reader.loadBeanDefinitions(configResources);
}
String[] configLocations = this.getConfigLocations();
if (configLocations != null) {
reader.loadBeanDefinitions(configLocations);
}
}
可以看到,最终实现xml标签解析动作的都是由传入的XmlBeanDefinitionReader对象来完成的,我们进入reader.loadBeanDefinitions(configLocations)方法,可以看到该方法同样是在XmlBeanDefinitionReader的父类中实现的,父子之间的继承关系如下:
public class XmlBeanDefinitionReader extends AbstractBeanDefinitionReader
具体实现如下:
AbstractBeanDefinitionReader
public int loadBeanDefinitions(Resource... resources) throws BeanDefinitionStoreException {
Assert.notNull(resources, "Resource array must not be null");
int count = 0;
Resource[] var3 = resources;
int var4 = resources.length;
for(int var5 = 0; var5 < var4; ++var5) {
Resource resource = var3[var5];
count += this.loadBeanDefinitions((Resource)resource);
}
return count;
}
此处的this.loadBeanDefinitions((Resource)resource)方法又是一个模板方法,具体实现交给子类,这样的话,基于xml文件的方式和基于注解的方式都可以通过其各自的实现类来做具体的实现。本文我们先看基于xml文件的方式,具体实现依旧在XmlBeanDefinitionReader类中,进入该方法的具体实现:
XmlBeanDefinitionReader
public int loadBeanDefinitions(EncodedResource encodedResource) throws BeanDefinitionStoreException {
Assert.notNull(encodedResource, "EncodedResource must not be null");
if (this.logger.isTraceEnabled()) {
this.logger.trace("Loading XML bean definitions from " + encodedResource);
}
Set<EncodedResource> currentResources = (Set)this.resourcesCurrentlyBeingLoaded.get();
if (currentResources == null) {
currentResources = new HashSet(4);
this.resourcesCurrentlyBeingLoaded.set(currentResources);
}
if (!((Set)currentResources).add(encodedResource)) {
throw new BeanDefinitionStoreException("Detected cyclic loading of " + encodedResource + " - check your import definitions!");
} else {
int var5;
try {
InputStream inputStream = encodedResource.getResource().getInputStream();
try {
InputSource inputSource = new InputSource(inputStream);
if (encodedResource.getEncoding() != null) {
inputSource.setEncoding(encodedResource.getEncoding());
}
//解析xml的核心方法
var5 = this.doLoadBeanDefinitions(inputSource, encodedResource.getResource());
} finally {
inputStream.close();
}
} catch (IOException var15) {
throw new BeanDefinitionStoreException("IOException parsing XML document from " + encodedResource.getResource(), var15);
} finally {
((Set)currentResources).remove(encodedResource);
if (((Set)currentResources).isEmpty()) {
this.resourcesCurrentlyBeingLoaded.remove();
}
}
return var5;
}
}
顺着this.doLoadBeanDefinitions(inputSource, encodedResource.getResource())方法一路跟踪进去,进到这个方法:
XmlBeanDefinitionReader
public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException {
BeanDefinitionDocumentReader documentReader = this.createBeanDefinitionDocumentReader();
int countBefore = this.getRegistry().getBeanDefinitionCount();
documentReader.registerBeanDefinitions(doc, this.createReaderContext(resource));
return this.getRegistry().getBeanDefinitionCount() - countBefore;
}
该方法里创建出一个BeanDefinitionDocumentReader对象,又通过委派模式调用documentReader.registerBeanDefinitions(doc, this.createReaderContext(resource))方法,跟进去发现,该方法的实现是由BeanDefinitionDocumentReader的子类DefaultBeanDefinitionDocumentReader实现的,一路跟踪到最终的具体实现(中间的调用过程省略),最终到下面的方法:
DefaultBeanDefinitionDocumentReader
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);
}
}
太不容易了,终于跟踪到具体解析标签的方法了,这个方法中包含两部分,其中this.parseDefaultElement(ele, delegate)方法是对xml中的默认标签进行解析,delegate.parseCustomElement(ele)是对自定义标签进行解析。本节对Spring容器初始化过程中的第一步,标签解析的整体流程进行了跟踪及记录,下一小节将对默认标签解析和自定义标签解析两部分内容,分别进行详细说明并记录。