1. Xml配置文件解析预处理顺序图

1.1. xml配置文件解析预处理顺序

xml-parse-flow-dialog

上图展示Spring框架解析xml配置文件,并解析用户在xml配置文件中定义的bean的整个过程:

  1. 所有的ApplicationContext容器(如ClassPathXmlApplicationContextXmlWebApplicationContext等)启动后,都会执行到AbstractApplicationContextrefresh()模板方法,此方法调用obtainFreshBeanFactory()会调用子类的refreshBeanFactory()方法来获取bean工厂并完成所有bean的解析处理;

    public abstract class AbstractApplicationContext extends DefaultResourceLoader
         implements ConfigurableApplicationContext, DisposableBean {
     // 步骤1, 调用refresh方法
     @Override
     public void refresh() throws BeansException, IllegalStateException {
         synchronized (this.startupShutdownMonitor) {
             // Prepare this context for refreshing.
             prepareRefresh();
    
             // Tell the subclass to refresh the internal bean factory.
             // 步骤2,调用本类的obtainFreshBeanFactory方法
             ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
    
             // Prepare the bean factory for use in this context.
             prepareBeanFactory(beanFactory);
    
             try {
                 // 此处省略........
             }
             catch (BeansException ex) {
                 // 此处省略........
             }
             finally {
                 // 此处省略........
             }
         }
     }
    
     protected ConfigurableListableBeanFactory obtainFreshBeanFactory() {
         // 步骤3,调用抽象方法,运行时会调用其子类实现的此方法来加载Bean定义
         refreshBeanFactory();
         ConfigurableListableBeanFactory beanFactory = getBeanFactory();
         if (logger.isDebugEnabled()) {
             logger.debug("Bean factory for " + getDisplayName() + ": " + beanFactory);
         }
         return beanFactory;
     }
    
     protected abstract void refreshBeanFactory() throws BeansException, IllegalStateException;
    }
    
  2. refreshBeanFactory()方法在AbstractApplicationContext的子类AbstractRefreshableApplicationContext中实现,实现的过程是:

    1. 创建一个DefaultListableBeanFactory对象,这是Spring框架的核心类,Spring 的 Bean 解析和实例化以及后续的使用都跟该类有非常密切的关系。
    2. 调用loadBeanDefinitions(beanFactory)方法加载Bean定义,此方法是一个抽象方法,参数为上一步创建的beanFactory对象,其实现类有XmlWebApplicationContextAnnotationConfigWebApplicationContextAbstractXmlApplicationContextGroovyWebApplicationContext,根据容器的不同,会调用不同子类的实现。上图中使用的是XmlWebApplicationContext类,其它类似。

      public abstract class AbstractRefreshableApplicationContext extends AbstractApplicationContext {
       @Override
       protected final void refreshBeanFactory() throws BeansException {
           if (hasBeanFactory()) {
               destroyBeans();
               closeBeanFactory();
           }
           try {
               // 步骤4:创建一个DefaultListableBeanFactory对象
               DefaultListableBeanFactory beanFactory = createBeanFactory();
               beanFactory.setSerializationId(getId());
               customizeBeanFactory(beanFactory);
               // 步骤5:调用子类的loadBeanDefinitions(beanFactory)方法来加载bean定义
               loadBeanDefinitions(beanFactory);
               synchronized (this.beanFactoryMonitor) {
                   this.beanFactory = beanFactory;
               }
           }
           catch (IOException ex) {
               throw new ApplicationContextException("I/O error parsing bean definition source for " + getDisplayName(), ex);
           }
       }
      
       protected DefaultListableBeanFactory createBeanFactory() {
           return new DefaultListableBeanFactory(getInternalParentBeanFactory());
       }
      
       protected abstract void loadBeanDefinitions(DefaultListableBeanFactory beanFactory)
               throws BeansException, IOException;
      }
      

      接下来我们以XmlWebApplicationContext类为例,来看一下loadBeanDefinitions(beanFactory)是如何解析并加载bean定义的。

  3. XmlWebApplicationContext类的loadBeanDefinitions方法会首先创建一个XmlBeanDefinitionReader对象用于XML文件的解析。XmlWebApplicationContext 对象还负责设置 XML 配置文件名,默认为 /WEB-INF/applicationContext.xml。如果是开发人员定义的配置文件,则可能为多个。loadBeanDefinitions方法会循环所有的配置文件名,使用xmlBeanDefinitionReader来加载Bean定义。

public class XmlWebApplicationContext extends AbstractRefreshableWebApplicationContext {

    /** Default config location for the root context */
    public static final String DEFAULT_CONFIG_LOCATION = "/WEB-INF/applicationContext.xml";

    /** Default prefix for building a config location for a namespace */
    public static final String DEFAULT_CONFIG_LOCATION_PREFIX = "/WEB-INF/";

    /** Default suffix for building a config location for a namespace */
    public static final String DEFAULT_CONFIG_LOCATION_SUFFIX = ".xml";

    @Override
    protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException {
        // 步骤6: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(getEnvironment());
        beanDefinitionReader.setResourceLoader(this);
        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);

        // 步骤7:调用本类loadBeanDefinitions方法
        loadBeanDefinitions(beanDefinitionReader);
    }

    protected void loadBeanDefinitions(XmlBeanDefinitionReader reader) throws IOException {
        String[] configLocations = getConfigLocations();
        if (configLocations != null) {
            // 步骤8:循环配置文件路径,调用XmlBeanDefinitionReader.loadBeanDefinitions方法加载Bean定义
            for (String configLocation : configLocations) {
                reader.loadBeanDefinitions(configLocation);
            }
        }
    }

    /**
     * The default location for the root context is "/WEB-INF/applicationContext.xml",
     * and "/WEB-INF/test-servlet.xml" for a context with the namespace "test-servlet"
     * (like for a DispatcherServlet instance with the servlet-name "test").
     */
    @Override
    protected String[] getDefaultConfigLocations() {
        if (getNamespace() != null) {
            return new String[] {DEFAULT_CONFIG_LOCATION_PREFIX + getNamespace() + DEFAULT_CONFIG_LOCATION_SUFFIX};
        }
        else {
            return new String[] {DEFAULT_CONFIG_LOCATION};
        }
    }
  1. beanDefinitionReader 会通过 JAXP 方式解析 XML 配置文件为 Document 对象 doc,Spring 框架对 XML 配置文件的处理即转化为对 Java 对象(Document) 的处理。

    XmlBeanDefinitionReader会创建两个对象:DefaultBeanDefinitionDocumentReader 对象 documentReaderXmlReaderContext 对象 readerContext,最终通过documentReader.registerBeanDefinitions(doc, createReaderContext(resource));方法来注册Bean

     public class XmlBeanDefinitionReader extends AbstractBeanDefinitionReader {
         /**
          * Load bean definitions from the specified XML file.
          * @param resource the resource descriptor for the XML file
          * @return the number of bean definitions found
          * @throws BeanDefinitionStoreException in case of loading or parsing errors
          */
         @Override
         public int loadBeanDefinitions(Resource resource) throws BeanDefinitionStoreException {
             return loadBeanDefinitions(new EncodedResource(resource));
         }
    
         public int loadBeanDefinitions(EncodedResource encodedResource) throws BeanDefinitionStoreException {
             Assert.notNull(encodedResource, "EncodedResource must not be null");
             if (logger.isInfoEnabled()) {
                 logger.info("Loading XML bean definitions from " + encodedResource.getResource());
             }
    
             Set<EncodedResource> currentResources = this.resourcesCurrentlyBeingLoaded.get();
             if (currentResources == null) {
                 currentResources = new HashSet<EncodedResource>(4);
                 this.resourcesCurrentlyBeingLoaded.set(currentResources);
             }
             if (!currentResources.add(encodedResource)) {
                 throw new BeanDefinitionStoreException(
                         "Detected cyclic loading of " + encodedResource + " - check your import definitions!");
             }
             try {
                 InputStream inputStream = encodedResource.getResource().getInputStream();
                 try {
                     InputSource inputSource = new InputSource(inputStream);
                     if (encodedResource.getEncoding() != null) {
                         inputSource.setEncoding(encodedResource.getEncoding());
                     }
                     return doLoadBeanDefinitions(inputSource, encodedResource.getResource());
                 }
                 finally {
                     inputStream.close();
                 }
             }
             catch (IOException ex) {
                 throw new BeanDefinitionStoreException(
                         "IOException parsing XML document from " + encodedResource.getResource(), ex);
             }
             finally {
                 currentResources.remove(encodedResource);
                 if (currentResources.isEmpty()) {
                     this.resourcesCurrentlyBeingLoaded.remove();
                 }
             }
         }
    
         protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource)
                 throws BeanDefinitionStoreException {
             try {
                 // 步骤9:创建一个Document对象
                 Document doc = doLoadDocument(inputSource, resource);
    
                 // 步骤10:注册BeanDefinition
                 return registerBeanDefinitions(doc, resource);
             }
             // 省略一堆的catch语句
    
         }
    
         public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException {
             // 步骤11:创建`DefaultBeanDefinitionDocumentReader` 对象 `documentReader`
             BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader();
             int countBefore = getRegistry().getBeanDefinitionCount();
             // 步骤13:调用`DefaultBeanDefinitionDocumentReader`类的`registerBeanDefinitions`方法来加载Bean
             documentReader.registerBeanDefinitions(doc, createReaderContext(resource));
             return getRegistry().getBeanDefinitionCount() - countBefore;
         }
    
         // 步骤12:创建`XmlReaderContext`对象
         public XmlReaderContext createReaderContext(Resource resource) {
             return new XmlReaderContext(resource, this.problemReporter, this.eventListener,
                     this.sourceExtractor, this, getNamespaceHandlerResolver());
         }
    
     }
    
  2. DefaultBeanDefinitionDocumentReader类注册Bean定义步骤:
    1. 获取Document的root节点
    2. 调用方法doRegisterBeanDefinitions(root)
    3. 创建一个BeanDefinitionParserDelegate对象
    4. 调用方法parseBeanDefinitions(root, this.delegate);解析xml节点
    5. 循环所有root子节点,如果是默认命名空间,调用`解析节点,否则调用delegate.parseCustomElement(ele)`解析自定义命名空间。
```java
public class DefaultBeanDefinitionDocumentReader implements BeanDefinitionDocumentReader {

    @Override
    public void registerBeanDefinitions(Document doc, XmlReaderContext readerContext) {
        this.readerContext = readerContext;
        logger.debug("Loading bean definitions");
        // 步骤14:创建xml root元素
        Element root = doc.getDocumentElement();
        // 步骤15:调用doRegisterBeanDefinitions方法
        doRegisterBeanDefinitions(root);
    }

    protected void doRegisterBeanDefinitions(Element root) {
        BeanDefinitionParserDelegate parent = this.delegate;
        // 步骤16:创建BeanDefinitionParserDelegate对象delegate
        this.delegate = createDelegate(getReaderContext(), root, parent);

        if (this.delegate.isDefaultNamespace(root)) {
            String profileSpec = root.getAttribute(PROFILE_ATTRIBUTE);
            if (StringUtils.hasText(profileSpec)) {
                String[] specifiedProfiles = StringUtils.tokenizeToStringArray(
                        profileSpec, BeanDefinitionParserDelegate.MULTI_VALUE_ATTRIBUTE_DELIMITERS);
                if (!getReaderContext().getEnvironment().acceptsProfiles(specifiedProfiles)) {
                    if (logger.isInfoEnabled()) {
                        logger.info("Skipped XML bean definition file due to specified profiles [" + profileSpec +
                                "] not matching: " + getReaderContext().getResource());
                    }
                    return;
                }
            }
        }

        preProcessXml(root);
        // 步骤18:调用parseBeanDefinitions(root, this.delegate)
        parseBeanDefinitions(root, this.delegate);
        postProcessXml(root);

        this.delegate = parent;
    }

    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;
                    // 步骤19:默认命名空间解析
                    if (delegate.isDefaultNamespace(ele)) {
                        parseDefaultElement(ele, delegate);
                    }
                    // 步骤20:非默认命名空间解析
                    else {
                        delegate.parseCustomElement(ele);
                    }
                }
            }
        }
        else {
            delegate.parseCustomElement(root);
        }
    }

    // <bean/>元素定义
    public static final String BEAN_ELEMENT = BeanDefinitionParserDelegate.BEAN_ELEMENT;

    public static final String NESTED_BEANS_ELEMENT = "beans";

    public static final String ALIAS_ELEMENT = "alias";

    public static final String NAME_ATTRIBUTE = "name";

    public static final String ALIAS_ATTRIBUTE = "alias";

    public static final String IMPORT_ELEMENT = "import";

    public static final String RESOURCE_ATTRIBUTE = "resource";

    public static final String PROFILE_ATTRIBUTE = "profile";
```

ClassPathXmlApplicationContext FileSystemXmlApplicationContext XmlWebApplicationContext AnnotationConfigWebApplicationContext

Copyright © wychuan.com 2017 all right reserved,powered by Gitbook该文件修订时间: 2018-01-09 08:05:07

results matching ""

    No results matching ""