- 通过 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,设置依赖关系等。