【02】spring源碼解析 XML配置文件的讀取以及轉化爲Bean對象的過程解析

 本文檔針對spring4.2.x版本 java

Spring IOC容器初始化的過程,分爲定位,載入解析以及註冊,接下來本文主要分析的是spring如何去解析BeanBeanDefinition對象的,這個只對xml聲明的Bean進行分析,對於經過spring註解掃描的方式之後再作分析 node

spring定位到springxml文件之後,將xml讀做爲文件流的形式,做爲InputSourceResource對象傳遞給文檔解析器進行解析,文檔解析的開始是XmlBeanDefinitionReaderdoLoadBeanDefinitions方法。 spring

inputSource是SAX的InputSource,
resource對象是對xml文件描述的一個對象
 
protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource)
         throws BeanDefinitionStoreException {
      try {
         /**
 
    傳遞inputSource對象進入doLoadDocument方法,此方法中設置了xml的版本,xml解析的模式等相關信息,而後input    Source文件流讀做爲Document對象返回,把Document對象傳遞給registerBeanDefinitions方法,這個方法纔是真正的把Document對象解析爲BeanDefinition對象的具體實現
         **/
         Document doc = doLoadDocument(inputSource, resource);
         return registerBeanDefinitions(doc, resource);
      } catch (BeanDefinitionStoreException ex) {
         throw ex;
     } catch
        …
}

public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException {
      BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader();
      int countBefore = getRegistry().getBeanDefinitionCount();
      documentReader.registerBeanDefinitions(doc, createReaderContext(resource));
      return getRegistry().getBeanDefinitionCount() - countBefore;
  }


先實例化出來一個BeanDefinitionDocumentReader,這個對象是Java經過本身封裝工具類BeanUtils.instantiateClass 經過反射的方式實例化出來的,而後記錄一下在註冊以前BeanDefinition中對象的個數,接着開始去解析documentspring自己設計很是靈活,BeanDefinitionDocumentReader registerBeanDefinitions方法是一個抽象方法,spring自身實現一個默認的BeanDefinitionDocumentReader的一個註冊器,DefaultBeanDefinitionDocumentReader,在這個子類中去實現具體的解析工做。 ide

@Override
   public void registerBeanDefinitions(Document doc, XmlReaderContext readerContext) {
      this.readerContext = readerContext;
      logger.debug("Loading bean definitions");
      Element root = doc.getDocumentElement();
      doRegisterBeanDefinitions(root);
  }
 
protected void doRegisterBeanDefinitions(Element root) {
 
BeanDefinitionParserDelegate對象描述了spring中bean節點中定義的所全部屬性和子節點
 
//全部的內嵌<beans>節點,都會被遞歸這個方法中經過遞歸的方式找到
//<beans>節點的默認屬性都會在此方法中設置,
//保存當前對象(或者父對象)BeanDefinitionParserDelegate軌跡,可能爲空,可是會建立
//子類的屬性描述(BeanDefinitionParserDelegate)會持有父類的一個引用
      
      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;
            }
         }
      }
      //在xml解析的預處理,客戶端能夠本身定義一些本身的節點屬性,用以特殊的做用,或者加強,此方法spring默認實現爲空
      preProcessXml(root);
//把Document對象解析爲BeanDefinition對象
      parseBeanDefinitions(root, this.delegate);
//在xml解析爲BeanDefinition以後作一些後置處理,客戶端能夠在解析完xml以後,作一些本身的業務邏輯,目前spring的默認實現爲空
      postProcessXml(root);
      this.delegate = parent;
   }

接下來分析parseBeanDefinition方法 工具

protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) {
//校驗是否是spring的默認命名空間,默認命名空間爲http://www.springframework.org/schema/beans,若是是默認命名空間,則按照默認命名空間來解析,不然則按照客戶自定義程序來解析
      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;
 
//循環xml節點,看delegate對象是不是默認命名空間,若是是則按照默認命名空間來解析節點,不然則按照客戶自定義來解析
                if (delegate.isDefaultNamespace(ele)) {
                   parseDefaultElement(ele, delegate);
                }
                else {
                   delegate.parseCustomElement(ele);
                }
            }
         }
      }
      else {
         delegate.parseCustomElement(root);
      }
  }

下面對parseDefaultElement 方法進行分析 post

此方法會查詢文檔中的import 標記,alias標記,以及beanbeans標記,根據這些標記進行分別匹配作相應的解析工做 this

private void parseDefaultElement(Element ele, BeanDefinitionParserDelegate delegate) {
      if (delegate.nodeNameEquals(ele, IMPORT_ELEMENT)) {
         importBeanDefinitionResource(ele);
      }
      else if (delegate.nodeNameEquals(ele, ALIAS_ELEMENT)) {
         processAliasRegistration(ele);
      }
      else if (delegate.nodeNameEquals(ele, BEAN_ELEMENT)) {
         processBeanDefinition(ele, delegate);
      }
      else if (delegate.nodeNameEquals(ele, NESTED_BEANS_ELEMENT)) {
         // recurse
         doRegisterBeanDefinitions(ele);
      }
  }

一、     Import節點的解析 spa

首先要求import節點中必須有resource屬性,而後根據resource的值判斷是相對路徑仍是絕對路徑。 debug

而且對於resource中的佔位符進行解析,例如"${user.dir}" 解析爲真正的路徑 設計

而後調用XmlBeanDefinitionReaderloadBeanDefinitions方法,把import節點映射爲BeanDefinition對象,初始化了一個4個元素大小的LinkHashSet對象,準備好相關容器和資源,而後調用本類中的doLoadBeanDefinitions方法,作真正的映射工做 ,這是一個遞歸的過程,它須要把全部的import節點中的xml文件遞歸的找出來,而後作的均是bean節點的解析

二、     Alias節點解析

三、     Bean節點解析

使用list存儲bean信息

/** Map of bean definition objects, keyed by bean name */

private final Map<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<String, BeanDefinition>(256);

/** List of bean definition names, in registration order */

private volatile List<String> beanDefinitionNames = new ArrayList<String>(256);

 /** List of names of manually registered singletons, in registration order */

private volatile Set<String> manualSingletonNames = new LinkedHashSet<String>(16);

把以上信息進行存儲完之後逐個轉化爲BeanDefinition的屬性

四、     Beans節點解析

 最終轉化爲bean節點的解析

相關文章
相關標籤/搜索