读书人

Spring源码-ICO-配置文件解析跟注册过

发布时间: 2012-11-15 15:16:15 作者: rapoo

Spring源码-ICO-配置文件解析和注册过程分析

本篇主要学习和分析整个Spring-ICO容器初始化中的第一步,对XML配置文件进行加载和解析为BeanDefinition对象,并注册到内部的BeanFactory中。


Spring源码-ICO-配置文件解析跟注册过程分析

?重点包括:(1).使用JAXP加载,验证和解析XML配置文件。(2).学习Spring使用JAR文件中的META-INF中的配置文件(处理器映射文件),通过定义解析框架完成各个XML配置文件的解析。
    通过 AbstractApplicationContext 类的模板方法refresh()开始整个加载和初始化过程。prepareRefresh();方法是容器的初始化,并记录启动时间。obtainFreshBeanFactory()内调用模板方法refreshBeanFactory()的子类的实现开始XML配置文件的解析和加载为BeanDefinitions(这个类是解析加载和ICO容器初始化的桥梁),这是整个ICO容器初始化的第一步。AbstractRefreshableApplicationContext.refreshBeanFactory()是模板方法的实现,如下:
@Overrideprotected 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赋值给容器的内部beanFactorysynchronized (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同于加载配置文件为ResourcebeanDefinitionReader.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,否则XSDint validationMode = getValidationModeForResource(resource);// 通过DefaultDocumentLoader,使用JAXP加载XML为w3c的DocumentDocument doc = this.documentLoader.loadDocument(inputSource, getEntityResolver(), this.errorHandler, validationMode, isNamespaceAware());return registerBeanDefinitions(doc, resource);}…… }

7. XML的加载是通过JAXP实现,并根据XML文档定义的验证方式对XML文档的语法进行合法性验证。这里使用EntityResolver—elegatingEntityResolver)实现对文档验证实体的转换,可以自动实现转换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.xsdhttp\://www.springframework.org/schema/context/spring-context-3.0.xsd=org/springframework/context/config/spring-context-3.0.xsdhttp\://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方式解析的DocumentBuilderDocumentBuilder builder = createDocumentBuilder(factory, entityResolver, errorHandler);// JAXP 解析并返回解析结果Documentreturn 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/XMLSchemafactory.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;}
?public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException {// Read document based on new BeanDefinitionDocumentReader SPI.// 实际的实现类是:DefaultBeanDefinitionDocumentReaderBeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader();// 获取BeanFactory已经注册的BeanDefinition数量int countBefore = getRegistry().getBeanDefinitionCount();// 核心方法:通过BeanDefinitionDocumentReader注册doc中定义的bean到BeanFactory中documentReader.registerBeanDefinitions(doc, createReaderContext(resource));// 返回本次注册的BeanDefinition的数量=目前BeanFactory中的总数-countBefore return getRegistry().getBeanDefinitionCount() - countBefore;}??

?

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));                  }
?

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

?

?

读书人网 >开源软件

热点推荐