- 通过 AbstractApplicationContext 类的模板方法refresh()开始整个加载和初始化过程。
- prepareRefresh();方法是容器的初始化,并记录启动时间。
- obtainFreshBeanFactory()内调用模板方法refreshBeanFactory()的子类的实现开始XML配置文件的解析和加载为BeanDefinitions(这个类是解析加载和ICO容器初始化的桥梁),这是整个ICO容器初始化的第一步。
- AbstractRefreshableApplicationContext.refreshBeanFactory()是模板方法的实现,如下:
@Override
protected final void refreshBeanFactory() throws BeansException {
//同步方式判断是否有其他线程已经初始化了BeanFactory,如果有,则销毁。
if (hasBeanFactory()) {
destroyBeans();
closeBeanFactory();
}
try {
// 生成一个内部的新的DefaultListableBeanFactory类
DefaultListableBeanFactory beanFactory = createBeanFactory();
beanFactory.setSerializationId(getId());
// 客户化参数设置:allowBeanDefinitionOverriding:允许覆写;allowCircularReferences:允许循环引用。
customizeBeanFactory(beanFactory);
// 重点:调用子类的实现,解析XML配置文件为BeanDefinition,并加入到beanFactory的集合参数中。
loadBeanDefinitions(beanFactory);
//同步方式把新加载的beanFactory赋值给容器的内部beanFactory
synchronized (this.beanFactoryMonitor) {
this.beanFactory = beanFactory;
}
}
catch (IOException ex) {
throw new ApplicationContextException("I/O error parsing bean definition source for " + getDisplayName(), ex);
}
}
5. 对于模板方法loadBeanDefinitions(beanFactory)的实现,一般由具体的特性实现类实现,比较常用的实现是 AbstractXmlApplicationContext(基于XML配置文件的程序,包括常用的基于Classpath和Filesystem加载XML的实现:ClassPathXmlApplicationContext和FileSystemXmlApplicationContext),XmlWebApplicationContext(WEB项目直接配置)和AnnotationConfigWebApplicationContext(包扫描方式),这里以AbstractXmlApplicationContext为例进行分析
AbstractXmlApplicationContext:
protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException {
// Create a new XmlBeanDefinitionReader for the given BeanFactory.
XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);
// Configure the bean definition reader with this context's
// resource loading environment.
beanDefinitionReader.setEnvironment(this.getEnvironment());
// resourceLoader同于加载配置文件为Resource
beanDefinitionReader.setResourceLoader(this);
// 设置用于XML配置文件验证的实体分解器,后续或介绍
beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this));
// Allow a subclass to provide custom initialization of the reader,
// then proceed with actually loading the bean definitions.
initBeanDefinitionReader(beanDefinitionReader);
// 调用XmlBeanDefinitionReader的loadBeanDefinitions方法开始加载配置文件
loadBeanDefinitions(beanDefinitionReader);
}
根据代码可以看出,配置文件的加载是托管给 XmlBeanDefinitionReader 来实现的。
6. XmlBeanDefinitionReader是 BeanDefinitionReader接口的实现,该接口定义使用ResourceLoader加载配置文件,然后使用解析文件,并通过BeanDefinitionRegistry注册到beanFactory中。在XmlBeanDefinitionReader实现中,loadBeanDefinitions(EncodedResource encodedResource)实现真正的加载处理。代码如下:
public int loadBeanDefinitions(EncodedResource encodedResource) throws BeanDefinitionStoreException {
……
try {
// 获取配置文件输入流
InputStream inputStream = encodedResource.getResource().getInputStream();
try {
// 构建JAXP的解析输入
InputSource inputSource = new InputSource(inputStream);
if (encodedResource.getEncoding() != null) {
inputSource.setEncoding(encodedResource.getEncoding());
}
// 调用加载处理
return doLoadBeanDefinitions(inputSource, encodedResource.getResource());
}
finally {
inputStream.close();
}
}
……
}
protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource)
throws BeanDefinitionStoreException {
try {
// 判断XML文档内定义的验证类型(DTD和XSD),其实就是逐行判断文档里面是否有“DOCTYPE”,有则是DTD,否则XSD
int validationMode = getValidationModeForResource(resource);
// 通过DefaultDocumentLoader,使用JAXP加载XML为w3c的Document
Document doc = this.documentLoader.loadDocument(
inputSource, getEntityResolver(), this.errorHandler, validationMode, isNamespaceAware());
return registerBeanDefinitions(doc, resource);
}
……
}
7. XML的加载是通过JAXP实现,并根据XML文档定义的验证方式对XML文档的语法进行合法性验证。这里使用EntityResolver(DelegatingEntityResolver)实现对文档验证实体的转换,可以自动实现转换http..形式的DTD和XSD文件到本地,对无互联网环境的进行支持。转换的mapping映射关系保存在各个Jar包的META-INF\spring.schema文件中;使用ErrorHandler(SimpleSaxErrorHandler)进行错误回调处理。关于JAXP的具体介绍请参考:JAXP使用及理解
http\://www.springframework.org/schema/context/spring-context-2.5.xsd=org/springframework/context/config/spring-context-2.5.xsd http\://www.springframework.org/schema/context/spring-context-3.0.xsd=org/springframework/context/config/spring-context-3.0.xsd http\://www.springframework.org/schema/context/spring-context-3.1.xsd=org/springframework/context/config/spring-context-3.1.xsd ……
public Document loadDocument(InputSource inputSource, EntityResolver entityResolver,
ErrorHandler errorHandler, int validationMode, boolean namespaceAware) throws Exception {
//调用内部方法创建JAXP的DocumentBuilderFactory,并设置打开名字空间支持和验证
DocumentBuilderFactory factory = createDocumentBuilderFactory(validationMode, namespaceAware);
if (logger.isDebugEnabled()) {
logger.debug("Using JAXP provider [" + factory.getClass().getName() + "]");
}
// 创建JAXP-DOM方式解析的DocumentBuilder
DocumentBuilder builder = createDocumentBuilder(factory, entityResolver, errorHandler);
// JAXP 解析并返回解析结果Document
return builder.parse(inputSource);
}
protected DocumentBuilderFactory createDocumentBuilderFactory(int validationMode, boolean namespaceAware)
throws ParserConfigurationException {
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
factory.setNamespaceAware(namespaceAware);
if (validationMode != XmlValidationModeDetector.VALIDATION_NONE) {
factory.setValidating(true);
if (validationMode == XmlValidationModeDetector.VALIDATION_XSD) {
// Enforce namespace aware for XSD...
factory.setNamespaceAware(true);
try {
//注意:这里是设置XSD的样子采用W3C标准验证方式:http://www.w3.org/2001/XMLSchema
factory.setAttribute(SCHEMA_LANGUAGE_ATTRIBUTE, XSD_SCHEMA_LANGUAGE);
}
catch (IllegalArgumentException ex) {
ParserConfigurationException pcex = new ParserConfigurationException(
"Unable to validate using XSD: Your JAXP provider [" + factory +
"] does not support XML Schema. Are you running on Java 1.4 with Apache Crimson? " +
"Upgrade to Apache Xerces (or Java 1.5) for full XSD support.");
pcex.initCause(ex);
throw pcex;
}
}
}
return factory;
}
- protected DocumentBuilder createDocumentBuilder(
- DocumentBuilderFactory factory, EntityResolver entityResolver, ErrorHandler errorHandler)
- throws ParserConfigurationException {
- DocumentBuilder docBuilder = factory.newDocumentBuilder();
- // 设置XML验证文件XSD的本地映射转换的实体分解器(http://www.springframework.org/schema/context/spring-context-3.0.xsd 转换为 包路径下org/springframework/context/config/spring-context-3.0.xsd)
- if (entityResolver != null) {
- docBuilder.setEntityResolver(entityResolver);
- }
- // 设置JAXP解析的错误处理回调
- if (errorHandler != null) {
- docBuilder.setErrorHandler(errorHandler);
- }
- return docBuilder;
- }
8. 回到第6步,我们在第7步使用JAXP解析并验证完成XML配置文件后得到的Document对象,现在可以用于注册BeanDefinition。在第6步的最后调用registerBeanDefinitions(doc, resource)完成注册。
整个解析过程托管给BeanDefinitionDocumentReader完成注册,代码如下:
public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException { // Read document based on new BeanDefinitionDocumentReader SPI. // 实际的实现类是:DefaultBeanDefinitionDocumentReader BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader(); // 获取BeanFactory已经注册的BeanDefinition数量 int countBefore = getRegistry().getBeanDefinitionCount(); // 核心方法:通过BeanDefinitionDocumentReader注册doc中定义的bean到BeanFactory中 documentReader.registerBeanDefinitions(doc, createReaderContext(resource)); // 返回本次注册的BeanDefinition的数量=目前BeanFactory中的总数-countBefore return getRegistry().getBeanDefinitionCount() - countBefore; }
ReaderContext接口及XmlReaderContext类
代码中createReaderContext(resource)返回一个XmlReaderContext类,是解析和注册过程的会话类,该类中持有本次解析会话的实例包括:
- Resrouce,错误处理ProblemReporter(FailFastProblemReporter)
- 事件处理ReaderEventListener(EmptyReaderEventListener使用的是空实现),
- 源抽取器SourceExtractor(NullSourceExtractor空实现)
- XmlBeanDefinitionReader(里面保存了BeanFactoryRegiester的实现DefaultListableBeanFactory,也就是ICO容器的内部beanFactory,注册的BeanDefenition就放在里面)
- NamespaceHandlerResolver:XML配置文件中的各种名字空间(如:context)定义的节点(如: context:property-placeholder)的对应解析器的分解器。实现通过Namespace SystemId找到对应的解析器的类路径。主要通过读取各个JAR文件的META-INF/spring.handlers文件实现。
Spring-context-3.x.x.jar的META-INF/spring.handlers文件内容:
http\://www.springframework.org/schema/context=org.springframework.context.config.ContextNamespaceHandler
http\://www.springframework.org/schema/jee=org.springframework.ejb.config.JeeNamespaceHandler
http\://www.springframework.org/schema/lang=org.springframework.scripting.config.LangNamespaceHandler
http\://www.springframework.org/schema/task=org.springframework.scheduling.config.TaskNamespaceHandler
9. 可以开始真正的解析了。对Document的解析分为两种情况,一种是默认的名字空间beans(http://www.springframework.org/schema/beans, 无前缀的配置如:bean)和其他名字空间的节点的解析(有前缀,如:context:property-placeholder)。
- 无前缀的beans默认名字空间节点:采用BeanDefinitionParserDelegate(解析的工具类)完成节点的解析。
- 有前缀的其他名字空间节点:使用解析框架完成解析,具体逻辑为首先使用Namespace的SystemId(就是URL全路径)通过NamespaceHandlerResolver找到对应NamespaceHandler,然后通过具体的NamespaceHandler的parse方法解析节点。在NamespaceHandler内部通节点名称找到对应BeanDefinitionParser解析器完成节点的解析并返回BeanDefinition。
public void registerBeanDefinitions(Document doc, XmlReaderContext readerContext) {
this.readerContext = readerContext;
logger.debug("Loading bean definitions");
Element root = doc.getDocumentElement();
// 创建解析的委托处理工具类
BeanDefinitionParserDelegate delegate = createHelper(readerContext, root);
// 解析前置处理,这里是空实现
preProcessXml(root);
// 解析整个文档,轮训各个子节点分别解析,下面有代码分析
parseBeanDefinitions(root, delegate);
//解析后置处理,也是空实现
postProcessXml(root);
}
/**
* Parse the elements at the root level in the document:
* "import", "alias", "bean".
* @param root the DOM root element of the document
*/
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;
// 如果是默认名字空间(beans),则直接使用解析
if (delegate.isDefaultNamespace(ele)) {
parseDefaultElement(ele, delegate);
}
else {
// 如果是非默认空间,这使用解析框架完成。
delegate.parseCustomElement(ele);
}
}
}
}
else {
delegate.parseCustomElement(root);
}
}
//BeanDefinitionParserDelegate. parseCustomElement(…)实现对非默认名字空间节点的通用解析框架
public BeanDefinition parseCustomElement(Element ele, BeanDefinition containingBd) {
String namespaceUri = getNamespaceURI(ele);
NamespaceHandler handler = this.readerContext.getNamespaceHandlerResolver().resolve(namespaceUri);
if (handler == null) {
error("Unable to locate Spring NamespaceHandler for XML schema namespace [" + namespaceUri + "]", ele);
return null;
}
return handler.parse(ele, new ParserContext(this.readerContext, this, containingBd));
}
context:property-placeholder节点在解析过程中使用的NamespaceHandler和BeanDefinitionParser.
通过前面查看Spring-context-3.x.x.jar的META-INF/spring.handlers文件内容(http\://www.springframework.org/schema/context=org.springframework.context.config.ContextNamespaceHandler
),可以知道context名字空间使用的是ContextNamespaceHandler,代码如下:
public class ContextNamespaceHandler extends NamespaceHandlerSupport {
public void init() {
// context:property-placeholder节点使用PropertyPlaceholderBeanDefinitionParser
registerBeanDefinitionParser("property-placeholder", new PropertyPlaceholderBeanDefinitionParser());
registerBeanDefinitionParser("property-override", new PropertyOverrideBeanDefinitionParser());
registerBeanDefinitionParser("annotation-config", new AnnotationConfigBeanDefinitionParser());
registerBeanDefinitionParser("component-scan", new ComponentScanBeanDefinitionParser());
registerBeanDefinitionParser("load-time-weaver", new LoadTimeWeaverBeanDefinitionParser());
registerBeanDefinitionParser("spring-configured", new SpringConfiguredBeanDefinitionParser());
registerBeanDefinitionParser("mbean-export", new MBeanExportBeanDefinitionParser());
registerBeanDefinitionParser("mbean-server", new MBeanServerBeanDefinitionParser());
}
}
可以看出在初始化方法里面定义了各个节点对应的解析器。其中context:property-placeholder节点使用PropertyPlaceholderBeanDefinitionParser。
以上基本是ICO中对XML配置文件的整个解析和注册过程,到此只是完成了SPRING容器初始化的第一步:创建内部的beanFactory,加载解析XML文件并注册BeanDefinition到内部的BeanFactory中。后续继续学习通过BeanDefinition实例化Bean,设置依赖关系等。