1. 程式人生 > >Spring原始碼分析3 — spring XML配置檔案的解析流程

Spring原始碼分析3 — spring XML配置檔案的解析流程

1 介紹

建立並初始化spring容器中,關鍵一步就是讀取並解析spring XML配置檔案。這個過程比較複雜,本文將詳細分析整個流程。先看涉及到的關鍵類。

XmlWebApplicationContext:web應用的預設Spring容器

XmlBeanDefinitionReader:讀取XML並解析xml檔案

DocumentLoader:檔案先被讀取為了原始的輸入流InputStream,然後封裝為InputSource。DocumentLoader載入inputSource,解析後得到Document物件

Document:代表一個XML或者HTML標記檔案,包含docType,各種element節點等。

BeanDefinition:XML中bean在spring容器中的表示。Document會被解析為BeanDefinition。在Bean建立和初始化中它們會大展拳腳。

BeanDefinitionDocumentReader:解析Document中的節點元素Element,轉換為BeanDefinition,並註冊他們到BeanDefinition登錄檔中。預設實現類為DefaultBeanDefinitionDocumentReader

BeanDefinitionParserDelegate:實際解析Document中的節點元素,採用了代理模式。

2 流程

2.1 obtainFreshBeanFactory

初始化spring容器中的refresh()方法中,會呼叫obtainFreshBeanFactory()方法,它是讀取並解析spring xml配置檔案的入口。詳細過程可以參看上一篇文章。下面從這個方法開始分析。

// obtainFreshBeanFactory 載入spring XML配置檔案
protected ConfigurableListableBeanFactory obtainFreshBeanFactory() {
   // 關鍵所在,後面分析
   refreshBeanFactory();
   // log之類的東西,不是很關鍵了
   ConfigurableListableBeanFactory beanFactory = getBeanFactory();
   if
(logger.isDebugEnabled()) { logger.debug("Bean factory for " + getDisplayName() + ": " + beanFactory); } return beanFactory; }
protected final void refreshBeanFactory() throws BeansException {
   // BeanFactory已存在,則先銷燬它
   if (hasBeanFactory()) {
      destroyBeans();
      closeBeanFactory();
   }
   try {
     // new DefaultListableBeanFactory,建立容器,設定id,個性化配置等
      DefaultListableBeanFactory beanFactory = createBeanFactory();
      beanFactory.setSerializationId(getId());
      customizeBeanFactory(beanFactory);
     // 載入xml配置檔案,具體子ApplicationContext會實現它。不同子類實現會不同。重點節點,後面分析
      loadBeanDefinitions(beanFactory);
      synchronized (this.beanFactoryMonitor) {
         this.beanFactory = beanFactory;
      }
   }
   catch (IOException ex) {
      throw new ApplicationContextException("I/O error parsing bean definition source for " + getDisplayName(), ex);
   }
}

2.2 loadBeanDefinitions

下面我們來分析web應用中預設的spring容器,也就是XmlWebApplicationContext,中的loadBeanDefinitions。

通過XmlBeanDefinitionReader來讀取xml配置檔案。

protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException {
   // 建立XmlBeanDefinitionReader,用它來讀取XML配置檔案
   XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);

   // 配置beanDefinitionReader的環境和屬性
   beanDefinitionReader.setEnvironment(getEnvironment());
  // ApplicationContext也繼承了ResourceLoader介面
   beanDefinitionReader.setResourceLoader(this);
  // entityResolver在parse時會用到
   beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this));

   // 初始化beanDefinitionReader,子類可以實現這個方法,做一些個性化配置和初始化
   initBeanDefinitionReader(beanDefinitionReader);

  // 開始load xml檔案,這一步開始才是真正讀取XML檔案了。前面都是做一些環境配置之類的事情
   loadBeanDefinitions(beanDefinitionReader);
}

loadBeanDefinitions()中先建立beanDefinitionReader,然後配置它的環境,設定成員屬性,最後才是真正幹活的,也就是讀取XML配置檔案。我們來看真正讀取XML的這一步。

protected void loadBeanDefinitions(XmlBeanDefinitionReader reader) throws IOException {
  // 獲取XML配置檔案的地址,還記得前面講到過的web.xml中的contextConfigLocation元素吧,它指明瞭XML配置檔案的地址。如果web.xml中沒有配置,則讀取預設的地址,參看後面分析
   String[] configLocations = getConfigLocations();
   if (configLocations != null) {
     // 遍歷讀取每個配置檔案
      for (String configLocation : configLocations) {
         reader.loadBeanDefinitions(configLocation);
      }
   }
}

2.3 getConfigLocations()

我們先分析下getConfigLocations方法, 它會獲取到spring XML配置檔案的地址。

// 獲取配置檔案地址,從web.xml中讀取。讀取不到,則使用預設地址
protected String[] getConfigLocations() {
        return (this.configLocations != null ? this.configLocations : getDefaultConfigLocations());
}

// 獲取預設的xml配置檔案地址
public static final String DEFAULT_CONFIG_LOCATION_PREFIX = "/WEB-INF/";
public static final String DEFAULT_CONFIG_LOCATION_SUFFIX = ".xml";
public static final String DEFAULT_CONFIG_LOCATION = "/WEB-INF/applicationContext.xml";

protected String[] getDefaultConfigLocations() {
   if (getNamespace() != null) {
     // 設定了容器namespace時,返回/WEB-INF/ + ApplicationContext的namespace + .xml
      return new String[] {DEFAULT_CONFIG_LOCATION_PREFIX + getNamespace() + DEFAULT_CONFIG_LOCATION_SUFFIX};
   }
   else {
     // 沒有設定namespace時,直接返回 /WEB-INF/applicationContext.xml
      return new String[] {DEFAULT_CONFIG_LOCATION};
   }
}

我們先獲取XML檔案地址,然後再讀取XML檔案,下面重點分析reader如何讀取xml檔案的

2.4 XmlBeanDefinitionReader.loadBeanDefinitions()

// XmlBeanDefinitionReader讀取XML檔案
public int loadBeanDefinitions(EncodedResource encodedResource) throws BeanDefinitionStoreException {
    // 一段log,省略

   // 將Resource物件新增到hashSet中,不是很關鍵
   Set<EncodedResource> currentResources = this.resourcesCurrentlyBeingLoaded.get();
   if (currentResources == null) {
      currentResources = new HashSet<>(4);
      this.resourcesCurrentlyBeingLoaded.set(currentResources);
   }
   if (!currentResources.add(encodedResource)) {
      throw new BeanDefinitionStoreException(
            "Detected cyclic loading of " + encodedResource + " - check your import definitions!");
   }
   try {
     // 獲取Resource物件的輸入流
      InputStream inputStream = encodedResource.getResource().getInputStream();
      try {
         // 將inputStream封裝到InputSource物件中,並設定編碼格式
         InputSource inputSource = new InputSource(inputStream);
         if (encodedResource.getEncoding() != null) {
            inputSource.setEncoding(encodedResource.getEncoding());
         }
         // 載入封裝好的inputSource物件,讀取XML配置檔案。關鍵步驟,後面分析
         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();
      }
   }
}

XmlBeanDefinitionReader做了一些成員變數設定後,獲取傳入的Resource物件的輸入流,封裝成InputSource後,開始真正解析配置檔案。下面來看doLoadBeanDefinitions()如何解析XML檔案。

protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource)
      throws BeanDefinitionStoreException {
   try {
      // 載入並解析XML檔案,解析方案為社群通用方法,不是Spring所特有的
      Document doc = doLoadDocument(inputSource, resource);

      return registerBeanDefinitions(doc, resource);
   }
   // 各種異常處理,省略
   catch (BeanDefinitionStoreException ex) {
   }
}

先利用documentLoader載入XML配置檔案,然後註冊beans。

2.4.1 doLoadDocument 載入xml檔案,將InputSource輸入流轉換為Document物件

// 載入XML配置檔案
protected Document doLoadDocument(InputSource inputSource, Resource resource) throws Exception {
   // 獲取entityResolver,errorHandler, validationMode,namespaceAware,它們都有預設值,也可以由使用者來設定
  // entityResolver: 解析器,預設ResourceEntityResolver
  // errorHandler: 解析XML時錯誤處理,預設SimpleSaxErrorHandler
  // validationMode: xml驗證模式,預設VALIDATION_XSD
  // namespaceAware: XML名稱空間是否敏感,預設false
   return this.documentLoader.loadDocument(inputSource, getEntityResolver(), this.errorHandler,
         getValidationModeForResource(resource), isNamespaceAware());
}

// DefaultDocumentLoader的loadDocument方法
public Document loadDocument(InputSource inputSource, EntityResolver entityResolver,
            ErrorHandler errorHandler, int validationMode, boolean namespaceAware) throws Exception {
    // 建立DocumentBuilderFactory,對應多個實現類,預設com.sun.org.apache.xerces.internal.jaxp.DocumentBuilderFactoryImpl
    DocumentBuilderFactory factory = createDocumentBuilderFactory(validationMode, namespaceAware);
    if (logger.isDebugEnabled()) {
        logger.debug("Using JAXP provider [" + factory.getClass().getName() + "]");
    }
    // 建立DocumentBuilder,通過factory建立
    DocumentBuilder builder = createDocumentBuilder(factory, entityResolver, errorHandler);
    // 解析輸入流,並返回一個Document物件。解析採用的是通用的DocumentBuilderImpl物件,使用DomParser解析xml檔案。解析這一步是通用的,不是Spring特有的方法。比較複雜,不展開了。只要知道通過parse後得到了Document物件就可以了。
    return builder.parse(inputSource);
}

2.4.2 registerBeanDefinitions 將讀取XML後的Document轉換為BeanDefinition

public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException {
   // 反射建立documentReader例項,預設為DefaultBeanDefinitionDocumentReader
   BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader();
   // 獲取容器中當前beans數量,已經註冊的BeanDefinition會儲存在一個Map中,獲取Map的size即可。
   int countBefore = getRegistry().getBeanDefinitionCount();
   // 註冊beanDefinition,這是關鍵所在,後面分析
   documentReader.registerBeanDefinitions(doc, createReaderContext(resource));
   // 返回本次註冊的數量
   return getRegistry().getBeanDefinitionCount() - countBefore;
}

// 反射建立documentReader,預設為DefaultBeanDefinitionDocumentReader
private Class<?> documentReaderClass = DefaultBeanDefinitionDocumentReader.class;
protected BeanDefinitionDocumentReader createBeanDefinitionDocumentReader() {
        return BeanDefinitionDocumentReader.class.cast(BeanUtils.instantiateClass(this.documentReaderClass));
    }

registerBeanDefinitions的作用就是將上一步中,輸入流轉換為的Document物件,轉換為BeanDefinition物件。主要工作在documentReader.registerBeanDefinitions()中,下面來分析。

public void registerBeanDefinitions(Document doc, XmlReaderContext readerContext) {
   this.readerContext = readerContext;
   logger.debug("Loading bean definitions");
   // root為<beans />標籤
   Element root = doc.getDocumentElement();
   doRegisterBeanDefinitions(root);
}

// 採用代理進行解析,代理為BeanDefinitionParserDelegate
protected void doRegisterBeanDefinitions(Element root) {
         // 建立代理
        BeanDefinitionParserDelegate parent = this.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)) {
                    return;
                }
            }
        }

         // 解析前的處理,DefaultBeanDefinitionDocumentReader沒有實現它,子類可以實現,來擴充套件功能
        preProcessXml(root);
         // 解析root內的XML標籤,如<import> <alias> <bean>等
        parseBeanDefinitions(root, this.delegate);
         // 解析後的處理,同樣沒有實現它,子類可以實現。
        postProcessXml(root);

        this.delegate = parent;
    }

registerBeanDefinitions() 解析下的XML標籤,通過BeanDefinitionParserDelegate代理來進行。具體工作在parseBeanDefinitions()中。這裡是本篇文章中比較關鍵的地方。

protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) {
   if (delegate.isDefaultNamespace(root)) {
      // 獲取<beans>的子節點
      NodeList nl = root.getChildNodes();
      // 遍歷子節點
      for (int i = 0; i < nl.getLength(); i++) {
         Node node = nl.item(i);
         if (node instanceof Element) {
            // 子節點是Element物件,預設的子節點,如<import>都是Element物件
            Element ele = (Element) node;
            if (delegate.isDefaultNamespace(ele)) {
               // 在預設的名稱空間url中的元素,是預設定義好的節點,採用parseDefaultElement方法解析
               parseDefaultElement(ele, delegate);
            }
            else {
               // 使用者自定義的名稱空間url中的元素,採用parseCustomElement方法解析
               delegate.parseCustomElement(ele);
            }
         }
      }
   }
   else {
      // 子節點不是標準的Element元素,比如使用者自定義的,採用parseCustomElement方法解析
      delegate.parseCustomElement(root);
   }
}

parseBeanDefinitions()方法會迴圈遍歷的子節點。如果是預設名稱空間內的Element,則採用parseDefaultElement()方法解析,否則採用parseCustomElement()方法。DefaultElement包括、、、巢狀的,其餘都為CustomElement,如。

2.4.2.1 parseDefaultElement() 解析DefaultElement

public static final String IMPORT_ELEMENT = "import";
public static final String ALIAS_ATTRIBUTE = "alias";
public static final String BEAN_ELEMENT = "bean";
public static final String NESTED_BEANS_ELEMENT = "beans";

private void parseDefaultElement(Element ele, BeanDefinitionParserDelegate delegate) {
   // 解析<import>
   if (delegate.nodeNameEquals(ele, IMPORT_ELEMENT)) {
      importBeanDefinitionResource(ele);
   }
   // 解析<alias>
   else if (delegate.nodeNameEquals(ele, ALIAS_ELEMENT)) {
      processAliasRegistration(ele);
   }
   // 解析<bean>
   else if (delegate.nodeNameEquals(ele, BEAN_ELEMENT)) {
      processBeanDefinition(ele, delegate);
   }
   // 解析<beans>
   else if (delegate.nodeNameEquals(ele, NESTED_BEANS_ELEMENT)) {
      // recurse
      doRegisterBeanDefinitions(ele);
   }
}

下面我們重點分析下processBeanDefinition(),因為這個和spring息息相關。這個過程十分複雜,所以我們不進行很細緻的分析,抓住主要流程就OK了。也不建議非要去了解每行程式碼做了什麼事情,避免過度陷入其中,而忽略了主流程。

protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) {
   // 解析Element為BeanDefinition,這是重點,後面詳細分析
   BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele);
   if (bdHolder != null) {
      bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder);
      try {
         // 將BeanDefinition註冊到BeanDefinitionMap中,key為beanName
         BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry());
      }
      catch (BeanDefinitionStoreException ex) {
         getReaderContext().error("Failed to register bean definition with name '" +
               bdHolder.getBeanName() + "'", ele, ex);
      }
      // 傳送註冊的訊息,相應的監聽器就會收到並處理訊息了
      getReaderContext().fireComponentRegistered(new BeanComponentDefinition(bdHolder));
   }
}
public BeanDefinitionHolder parseBeanDefinitionElement(Element ele, @Nullable BeanDefinition containingBean) {
   // 獲取bean的id屬性
   String id = ele.getAttribute(ID_ATTRIBUTE);
   // 獲取bean的name屬性
   String nameAttr = ele.getAttribute(NAME_ATTRIBUTE);

   // 解析name屬性,支援多個name
   List<String> aliases = new ArrayList<>();
   if (StringUtils.hasLength(nameAttr)) {
      String[] nameArr = StringUtils.tokenizeToStringArray(nameAttr, MULTI_VALUE_ATTRIBUTE_DELIMITERS);
      aliases.addAll(Arrays.asList(nameArr));
   }

   // id屬性賦值到beanName變數中,注意不是name屬性。如果沒有id屬性,則使用name屬性的第一個值
   String beanName = id;
   if (!StringUtils.hasText(beanName) && !aliases.isEmpty()) {
      beanName = aliases.remove(0);
   }

   // 校驗beanname的唯一性,這也是為啥id屬性值必須唯一的原因
   if (containingBean == null) {
      checkNameUniqueness(beanName, aliases, ele);
   }

   // 解析bean節點為GenericBeanDefinition,後面詳細分析
   AbstractBeanDefinition beanDefinition = parseBeanDefinitionElement(ele, beanName, containingBean);
   // 後面不是很重要了
   if (beanDefinition != null) {
      if (!StringUtils.hasText(beanName)) {
         try {
            if (containingBean != null) {
               beanName = BeanDefinitionReaderUtils.generateBeanName(
                     beanDefinition, this.readerContext.getRegistry(), true);
            }
            else {
               beanName = this.readerContext.generateBeanName(beanDefinition);
               // Register an alias for the plain bean class name, if still possible,
               // if the generator returned the class name plus a suffix.
               // This is expected for Spring 1.2/2.0 backwards compatibility.
               String beanClassName = beanDefinition.getBeanClassName();
               if (beanClassName != null &&
                     beanName.startsWith(beanClassName) && beanName.length() > beanClassName.length() &&
                     !this.readerContext.getRegistry().isBeanNameInUse(beanClassName)) {
                  aliases.add(beanClassName);
               }
            }

         }
         catch (Exception ex) {
            error(ex.getMessage(), ele);
            return null;
         }
      }
      String[] aliasesArray = StringUtils.toStringArray(aliases);
      return new BeanDefinitionHolder(beanDefinition, beanName, aliasesArray);
   }

   return null;
}
public static final String PARENT_ATTRIBUTE = "parent";
public static final String CLASS_ATTRIBUTE = "class";

public AbstractBeanDefinition parseBeanDefinitionElement(Element ele, String beanName, @Nullable BeanDefinition containingBean) {
    this.parseState.push(new BeanEntry(beanName));
    // 獲取class和parent屬性
    String className = null;
    if (ele.hasAttribute(CLASS_ATTRIBUTE)) {
        className = ele.getAttribute(CLASS_ATTRIBUTE).trim();
    }
    String parent = null;
    if (ele.hasAttribute(PARENT_ATTRIBUTE)) {
        parent = ele.getAttribute(PARENT_ATTRIBUTE);
    }

    try {
         // 反射例項化bean為GenericBeanDefinition
        AbstractBeanDefinition bd = createBeanDefinition(className, parent);
        // 解析節點中其他屬性,如scope, singleton等。後面詳細分析
        parseBeanDefinitionAttributes(ele, beanName, containingBean, bd);
        bd.setDescription(DomUtils.getChildElementValueByTagName(ele, DESCRIPTION_ELEMENT));

         // 解析子節點meta屬性,如果有的話
        parseMetaElements(ele, bd);
         // 解析子節點lookup-method屬性
        parseLookupOverrideSubElements(ele, bd.getMethodOverrides());
         // 解析子節點replaced-method屬性
        parseReplacedMethodSubElements(ele, bd.getMethodOverrides());

         // 解析constructor-arg屬性
        parseConstructorArgElements(ele, bd);
         // 解析property屬性
        parsePropertyElements(ele, bd);
         // 解析qualifier屬性
        parseQualifierElements(ele, bd);

        bd.setResource(this.readerContext.getResource());
        bd.setSource(extractSource(ele));

        return bd;
    }
     // 各種異常處理
    catch (ClassNotFoundException ex) {
        error("Bean class [" + className + "] not found", ele, ex);
    }
    ...
    return null;
}
// 反射例項化,建立GenericBeanDefinition物件
public static AbstractBeanDefinition createBeanDefinition(
      @Nullable String parentName, @Nullable String className, @Nullable ClassLoader classLoader) throws ClassNotFoundException {

   GenericBeanDefinition bd = new GenericBeanDefinition();
   bd.setParentName(parentName);
   if (className != null) {
      if (classLoader != null) {
         bd.setBeanClass(ClassUtils.forName(className, classLoader));
      }
      else {
         bd.setBeanClassName(className);
      }
   }
   return bd;
}
// 解析<bean>中的各種屬性,比如scope,lazy-init等
public AbstractBeanDefinition parseBeanDefinitionAttributes(Element ele, String beanName,
      @Nullable BeanDefinition containingBean, AbstractBeanDefinition bd) {

   // 解析singleton
   if (ele.hasAttribute(SINGLETON_ATTRIBUTE)) {
      error("Old 1.x 'singleton' attribute in use - upgrade to 'scope' declaration", ele);
   }
   // 解析scope
   else if (ele.hasAttribute(SCOPE_ATTRIBUTE)) {
      bd.setScope(ele.getAttribute(SCOPE_ATTRIBUTE));
   }
   else if (containingBean != null) {
      // Take default from containing bean in case of an inner bean definition.
      bd.setScope(containingBean.getScope());
   }
   // 解析abstract
   if (ele.hasAttribute(ABSTRACT_ATTRIBUTE)) {
      bd.setAbstract(TRUE_VALUE.equals(ele.getAttribute(ABSTRACT_ATTRIBUTE)));
   }
   // 解析lazy-init
   String lazyInit = ele.getAttribute(LAZY_INIT_ATTRIBUTE);
   if (DEFAULT_VALUE.equals(lazyInit)) {
      lazyInit = this.defaults.getLazyInit();
   }
   bd.setLazyInit(TRUE_VALUE.equals(lazyInit));
   // 解析autowire
   String autowire = ele.getAttribute(AUTOWIRE_ATTRIBUTE);
   bd.setAutowireMode(getAutowireMode(autowire));
   // 解析depends-on
   if (ele.hasAttribute(DEPENDS_ON_ATTRIBUTE)) {
      String dependsOn = ele.getAttribute(DEPENDS_ON_ATTRIBUTE);
      bd.setDependsOn(StringUtils.tokenizeToStringArray(dependsOn, MULTI_VALUE_ATTRIBUTE_DELIMITERS));
   }
   // 解析autowire-candidate
   String autowireCandidate = ele.getAttribute(AUTOWIRE_CANDIDATE_ATTRIBUTE);
   if ("".equals(autowireCandidate) || DEFAULT_VALUE.equals(autowireCandidate)) {
      String candidatePattern = this.defaults.getAutowireCandidates();
      if (candidatePattern != null) {
         String[] patterns = StringUtils.commaDelimitedListToStringArray(candidatePattern);
         bd.setAutowireCandidate(PatternMatchUtils.simpleMatch(patterns, beanName));
      }
   }
   else {
      bd.setAutowireCandidate(TRUE_VALUE.equals(autowireCandidate));
   }
   // 解析primary
   if (ele.hasAttribute(PRIMARY_ATTRIBUTE)) {
      bd.setPrimary(TRUE_VALUE.equals(ele.getAttribute(PRIMARY_ATTRIBUTE)));
   }
   // 解析init-method
   if (ele.hasAttribute(INIT_METHOD_ATTRIBUTE)) {
      String initMethodName = ele.getAttribute(INIT_METHOD_ATTRIBUTE);
      if (!"".equals(initMethodName)) {
         bd.setInitMethodName(initMethodName);
      }
   }
   else if (this.defaults.getInitMethod() != null) {
      bd.setInitMethodName(this.defaults.getInitMethod());
      bd.setEnforceInitMethod(false);
   }
   // 解析destroy-method
   if (ele.hasAttribute(DESTROY_METHOD_ATTRIBUTE)) {
      String destroyMethodName = ele.getAttribute(DESTROY_METHOD_ATTRIBUTE);
      bd.setDestroyMethodName(destroyMethodName);
   }
   else if (this.defaults.getDestroyMethod() != null) {
      bd.setDestroyMethodName(this.defaults.getDestroyMethod());
      bd.setEnforceDestroyMethod(false);
   }

   // 解析factory-method
   if (ele.hasAttribute(FACTORY_METHOD_ATTRIBUTE)) {
      bd.setFactoryMethodName(ele.getAttribute(FACTORY_METHOD_ATTRIBUTE));
   }
   // 解析factory-bean
   if (ele.hasAttribute(FACTORY_BEAN_ATTRIBUTE)) {
      bd.setFactoryBeanName(ele.getAttribute(FACTORY_BEAN_ATTRIBUTE));
   }

   return bd;
}

2.4.2.2 parseCustomElement() 解析CustomElement

public BeanDefinition parseCustomElement(Element ele, @Nullable BeanDefinition containingBd) {
   String namespaceUri = getNamespaceURI(ele);
   if (namespaceUri == null) {
      return null;
   }
   // 根據名稱空間url獲取具體的NamespaceHandler,比如<context:component-scan>對應的就是使用者自定義的ContextNamespaceHandler。
   NamespaceHandler handler = this.readerContext.getNamespaceHandlerResolver().resolve(namespaceUri);
   if (handler == null) {
      error("Unable to locate Spring NamespaceHandler for XML schema namespace [" + namespaceUri + "]", ele);
      return null;
   }
   // 交給使用者自定義的NamespaceHandler來解析使用者自定義的CustomElement
   return handler.parse(ele, new ParserContext(this.readerContext, this, containingBd));
}

parseCustomElement()首先根據自定義標籤的名稱空間,生成具體的NamespaceHandler。一般要由使用者自己定義。然後呼叫parse方法進行解析,這個也是使用者自定義的。這裡就不展開分析了。

3 流程圖

相關推薦

Spring原始碼分析3spring XML配置檔案解析流程

1 介紹 建立並初始化spring容器中,關鍵一步就是讀取並解析spring XML配置檔案。這個過程比較複雜,本文將詳細分析整個流程。先看涉及到的關鍵類。 XmlWebApplicationContext:web應用的預設Spring容器 XmlBean

1、Spring原始碼分析1之讀取配置檔案

1、XMLBeanFcatory BeanFactory bf = new XmlBeanFactory(new ClassPa

轉:ssm spring+springmvc+mybatis中的xml配置檔案詳解

這幾天一直在整合SSM框架,雖然網上有很多已經整合好的,但是對於裡面的配置檔案並沒有進行過多的說明,很多人知其然不知其所以然,經過幾天的搜尋和整理,今天總算對其中的XML配置檔案有了一定的瞭解,所以拿出來一起分享一下,希望有不足的地方大家批評指正~~~ 首先   這篇文章暫時只對框架中所要用到的配

Spring依賴注入(基於XML配置檔案和Annotation的方式完成屬性裝配)

依賴注入的方式(手工裝配): 1.使用bean的構造器注入—基於XML方式 2.使用屬性setter方法注入—基於XML方式 3.使用field注入—基於Annotation方式 注入依賴物件可

Spring入門學習(使用XML配置檔案方式來配置AOP) 第十七節

Spring入門學習(使用XML配置檔案方式來配置AOP) xml配置檔案配置AOP xml配置檔案配置AOP 使用之前建立的類ArithmeticCalculator和ArithmeticCalculatorImpl 去掉Log

spring在程式碼裡面獲取xml配置檔案的屬性

    我們通常在xml裡面配置一些經常會變化的引數,達到簡單配置的目的。有的時候我們需要在程式中獲取到這些配置的屬性,這個時候就要用到PropertyPlaceHolder相關的類了。    首先來個xml檔案 test.xmluse=aaa pass=bbb然後寫個配置類

ssm spring+springmvc+mybatis中的xml配置檔案詳解

這幾天一直在整合SSM框架,雖然網上有很多已經整合好的,但是對於裡面的配置檔案並沒有進行過多的說明,很多人知其然不知其所以然,經過幾天的搜尋和整理,今天總算對其中的XML配置檔案有了一定的瞭解,所以拿出來一起分享一下,希望有不足的地方大家批評指正~~~ 首先   這篇文章暫時只對框架中所要用到的

spring如何使用多個xml配置檔案

1, 在web.xml中定義 contextConfigLocation引數.spring會使用這個引數載入.所有逗號分割的xml.如果沒有這個引數,spring預設載入web-inf/applicationContext.xml檔案. <context-param

3Spring原始碼分析3之載入Bean

1、Bean的載入 // 前面兩篇已經分析了讀取配置檔案,並註冊BeanDefinition BeanFactory bf = n

spring原始碼分析spring生命週期二

接著上一篇我們看看具體是哪裡的程式碼執行了。 1.初始化BeanFactoryPostProcessor invokeBeanFactoryPostProcessors(beanFactory);--> PostProcessorRegistrationDelegate.

spring原始碼分析spring生命週期

最近在看springboot自動配置,看到了@Conditional,@ConditionalOnMissingBean等等。這些註解,一直研究他們是如何實現springboot的條件註解的。由他們回到了@Configuration,回到了ConfigurationClassPostPr

Spring原始碼分析Spring中的後置處理器BeanPostProcessor

BeanPostProcessor簡介 BeanPostProcessor是Spring IOC容器給我們提供的一個擴充套件介面,如果我們需要在容器完成Bean的例項、初始化方法前後新增一些自己的邏輯處理,那麼定義一個BeanPostProcessor介面的實現類。

Spring如何通過定義在META-INF/spring.handlers中的屬性進行配置檔案解析

毫無疑問這些東西肯定在Spring的入口函式refresh()之中進行的。AbstractApplicationContextConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();//獲得

Spring原始碼分析4 — spring bean建立和初始化

1 介紹 建立並初始化spring容器中,refresh()方法中解析xml配置檔案,註冊容器後處理器,bean後處理器,初始化MessageSource,ApplicationEventMulticaster廣播器,註冊完ApplicationListene

spring原始碼分析spring-jdbc模組詳解

0 概述 Spring將替我們完成所有使用JDBC API進行開發的單調乏味的、底層細節處理工作。下表描述了哪些是spring幫助我們做好的,哪些是我們要做的。 Action  Spring  You Define connection parameters.  X

Spring原始碼分析Spring MVC之HanderMapping請求對映處理

AbstractHandlerMappig呼叫getHandler() /** * Look up a handler for the given request, falling back to the default * handler if no speci

MyBatis 3XML配置檔案詳解

Mybatis-config.xml的配置檔案:<?xml version="1.0" encoding="UTF-8" ?> <!--DOCTYPE標籤是一種標準通用標記語言的文件型別宣告,它的目的是要告訴標準通用標記語言解析器,它應該使用什麼樣的文件型別

spring原始碼分析spring-messaging模組詳解

0 概述 spring-messaging模組為整合messaging api和訊息協議提供支援。 其程式碼結構為: 其中base定義了訊息Message(MessageHeader和body)、訊息處理MessageHandler、傳送訊息MessageChann

spring原始碼分析-web容器初始化過程解析1

   在之前的“”中分析了spring mvc的初始化過程,接下來將分析其請求處理過程。         在找請求處理的入口時,我們需要先知道Servlet的程式設計規範,對應不同的請求(如POST、GET等)的實現方法在FrameworkServlet中,分別是doP

Mybatis原始碼分析(1)—— Mapper檔案解析

感覺CSDN對markdown的支援不夠友好,總是伴隨各種問題,很惱火! xxMapper.xml的解析主要由XMLMapperBuilder類完成,parse方法來完成解析: public void parse() { if (!configuration.isRes