看這篇文章以前能夠先了解以前的跟蹤流程,https://www.jianshu.com/p/4934233f0eadjava
代碼過寬,能夠shift + 鼠標滾輪 左右滑動查看node
AbstractBeanDefinitionReader類中loadBeanDefinitions方法,該方法會對DOM文檔對象進行解析,生成BeanDefinition對象,這篇文章只講這個方法。web
/** * Load bean definitions from the specified resources. * @param resources the resource descriptors 資源處理器,也叫作資源描述符 * @return the number of bean definitions found 返回發現的bean definition數量 * * 從指定資源中加載bean definitions */ @Override public int loadBeanDefinitions(Resource... resources) throws BeanDefinitionStoreException { Assert.notNull(resources, "Resource array must not be null"); int counter = 0; //由於個人web.xml中配置contextConfigLocation無論classpath前綴仍是後半部分 //都沒有使用 * 或者 ?等表達式,因此只有一個Resource--資源描述符,或者 //稱做資源處理器 for (Resource resource : resources) { //調用的子類XmlBeanDefinitionReader方法,進入查看 counter += loadBeanDefinitions(resource); } return counter; } /** * 此方法在AbstractBeanDefinitionReader的子類XmlBeanDefinitionReader中 * * Load bean definitions from the specified XML file. * 從指定的xml文件中加載bean definitions */ @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()); } //resourcesCurrentlyBeingLoaded是一個ThreadLocal,裏面存放Resource包裝類的set集合 Set<EncodedResource> currentResources = this.resourcesCurrentlyBeingLoaded.get(); if (currentResources == null) { currentResources = new HashSet<EncodedResource>(4); this.resourcesCurrentlyBeingLoaded.set(currentResources); } //若是set中已有這個元素則返回false,進入該條件拋出異常 if (!currentResources.add(encodedResource)) { throw new BeanDefinitionStoreException( //檢測到循環加載某個Resource,須要檢查導入的definitions "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 { //資源加載完畢,移除該Resource currentResources.remove(encodedResource); //若是集合中已經沒有了其餘Resource,移除集合 if (currentResources.isEmpty()) { this.resourcesCurrentlyBeingLoaded.remove(); } } }
此方法在XmlBeanDefinitionReader類中,分兩部分跟蹤spring
/** * Actually load bean definitions from the specified XML file. * * 真正的從指定xml文件中加載bean definitions */ protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource) throws BeanDefinitionStoreException { try { //根據不一樣的xml約束(dtd,xsd等),將xml文件生成對應的文檔對象 //1.這個方法裏面涉及xml的解析 Document doc = doLoadDocument(inputSource, resource); //2.主要就是看這個方法,bean definitions的註冊 return registerBeanDefinitions(doc, resource); } //看下拋出異常的提示,都是項目運行時可能出現的錯誤 catch (BeanDefinitionStoreException ex) { throw ex; } catch (SAXParseException ex) { throw new XmlBeanDefinitionStoreException(resource.getDescription(), "Line " + ex.getLineNumber() + " in XML document from " + resource + " is invalid", ex); } catch (SAXException ex) { throw new XmlBeanDefinitionStoreException(resource.getDescription(), "XML document from " + resource + " is invalid", ex); } catch (ParserConfigurationException ex) { throw new BeanDefinitionStoreException(resource.getDescription(), "Parser configuration exception parsing XML from " + resource, ex); } catch (IOException ex) { throw new BeanDefinitionStoreException(resource.getDescription(), "IOException parsing XML document from " + resource, ex); } catch (Throwable ex) { throw new BeanDefinitionStoreException(resource.getDescription(), "Unexpected exception parsing XML document from " + resource, ex); } }
跟蹤標記1的方法app
此方法在XmlBeanDefinitionReader類中ide
//1.這個方法裏面涉及xml的解析 Document doc = doLoadDocument(inputSource, resource); /** * Actually load the specified document using the configured DocumentLoader. * @param inputSource the SAX InputSource to read from --從中讀取的SAX輸入源 * @param resource the resource descriptor for the XML file --xml文件的資源描述符 * @return the DOM Document DOM文檔對象 * * 使用配置好的DocumentLoader文檔加載器加載指定的文檔 */ protected Document doLoadDocument(InputSource inputSource, Resource resource) throws Exception { return this.documentLoader.loadDocument(inputSource, getEntityResolver(), this.errorHandler, getValidationModeForResource(resource), isNamespaceAware()); }
上面方法入參說明一下:post
inputSource 由上一層方法傳遞過來。ui
getEntityResolver() 方法返回 XmlBeanDefinitionReader 類的 entityResolver 屬性。
entityResolver 屬性在 loadBeanDefinitions(DefaultListableBeanFactory beanFactory) 方法中被賦值。this
beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this));
this拿的是根web應用上下文,查看ResourceEntityResolver的構造方法編碼
/** * Create a ResourceEntityResolver for the specified ResourceLoader * (usually, an ApplicationContext). * @param resourceLoader the ResourceLoader (or ApplicationContext) * to load XML entity includes with * * 爲指定的ResourceLoade(一般是應用上下文)r建立一個ResourceEntityResolver */ public ResourceEntityResolver(ResourceLoader resourceLoader) { super(resourceLoader.getClassLoader()); //此處解析器拿到了上下文的引用 this.resourceLoader = resourceLoader; }
調用了父類構造,再跟進一層
/** * Create a new DelegatingEntityResolver that delegates to * a default {@link BeansDtdResolver} and a default {@link PluggableSchemaResolver}. * <p>Configures the {@link PluggableSchemaResolver} with the supplied * {@link ClassLoader}. * @param classLoader the ClassLoader to use for loading * (can be {@code null}) to use the default ClassLoader) */ public DelegatingEntityResolver(ClassLoader classLoader) { //這兩個解析器和約束的類型有關,DTD this.dtdResolver = new BeansDtdResolver(); //可插拔的Schema解析器,拿的上下文的類加載器 this.schemaResolver = new PluggableSchemaResolver(classLoader); }
再看doLoadDocument方法的另一個入參 this.errorHandler,這個屬性隨着XmlBeanDefinitionReader類被初始化而初始化
private ErrorHandler errorHandler = new SimpleSaxErrorHandler(logger);
而後是getValidationModeForResource(resource)入參。
/** * Gets the validation mode for the specified {@link Resource}. If no explicit * validation mode has been configured then the validation mode is * {@link #detectValidationMode detected}. * <p>Override this method if you would like full control over the validation * mode, even when something other than {@link #VALIDATION_AUTO} was set. * * 經過給定Resource給出驗證模式。若是沒有明確配置驗證模式,那麼調用detectValidationMode方法去檢測。 */ protected int getValidationModeForResource(Resource resource) { //默認自動驗證,爲1 int validationModeToUse = getValidationMode(); //若是有給出具體驗證方式,則返回結果 if (validationModeToUse != VALIDATION_AUTO) { return validationModeToUse; } //檢測驗證模式,進入這個方法 int detectedMode = detectValidationMode(resource); if (detectedMode != VALIDATION_AUTO) { return detectedMode; } // Hmm, we didn't get a clear indication... Let's assume XSD, // since apparently no DTD declaration has been found up until // detection stopped (before finding the document's root tag). // 若是實在不能判斷驗證模式是那種就使用XSD方式, // 由於檢測完後仍是沒有發現DTD模式的聲明(在查找document的根標籤以前)。 // 值爲3 return VALIDATION_XSD; }
進入下面這個方法,這個方法由XmlBeanDefinitionReader實現
int detectedMode = detectValidationMode(resource); /** * Detects which kind of validation to perform on the XML file identified * by the supplied {@link Resource}. If the file has a {@code DOCTYPE} * definition then DTD validation is used otherwise XSD validation is assumed. * <p>Override this method if you would like to customize resolution * of the {@link #VALIDATION_AUTO} mode. * * 檢測執行xml文件時該用哪一種驗證方式,這個xml由Resource對象提供 * 若是這個文件有DOCTYPE聲明,那麼就用DTD驗證,不然就假定使用XSD。 * 若是你想要自定義自動驗證模式的解決方式,你能夠覆蓋這個方法 */ protected int detectValidationMode(Resource resource) { //默認false if (resource.isOpen()) { throw new BeanDefinitionStoreException( "Passed-in Resource [" + resource + "] contains an open stream: " + "cannot determine validation mode automatically. Either pass in a Resource " + "that is able to create fresh streams, or explicitly specify the validationMode " + "on your XmlBeanDefinitionReader instance."); } InputStream inputStream; try { inputStream = resource.getInputStream(); } catch (IOException ex) { throw new BeanDefinitionStoreException( "Unable to determine validation mode for [" + resource + "]: cannot open InputStream. " + "Did you attempt to load directly from a SAX InputSource without specifying the " + "validationMode on your XmlBeanDefinitionReader instance?", ex); } try { //進入這個方法查看 return this.validationModeDetector.detectValidationMode(inputStream); } catch (IOException ex) { throw new BeanDefinitionStoreException("Unable to determine validation mode for [" + resource + "]: an error occurred whilst reading from the InputStream.", ex); } }
XmlBeanDefinitionReader的validationModeDetector屬性有默認實現
private final XmlValidationModeDetector validationModeDetector = new XmlValidationModeDetector();
validationModeDetector調用detectValidationMode方法
/** * Detect the validation mode for the XML document in the supplied {@link InputStream}. * Note that the supplied {@link InputStream} is closed by this method before returning. * * 在提供的InputStream中檢測XML文檔的驗證模式 * 注意,提供的InputStream在這個方法return以前會被關閉 */ public int detectValidationMode(InputStream inputStream) throws IOException { // Peek into the file to look for DOCTYPE. // 查找文件的DOCTYPE BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream)); try { boolean isDtdValidated = false; String content; while ((content = reader.readLine()) != null) { //讀一行字符串就幹掉字符串裏面的註釋,若是全是註釋全乾掉 //主要爲了剝離註釋,由於非註釋內容要麼是DOCTYPE聲明要麼是文檔的根元素對象 content = consumeCommentTokens(content); //剝離註釋後徹底沒內容就繼續循環 if (this.inComment || !StringUtils.hasText(content)) { continue; } //有DOCTYPE聲明,就跳出去 if (hasDoctype(content)) { isDtdValidated = true; break; } //註釋不能進去。開頭是"<",後面第一個字符是字母,就進入。 //好比'<beans xmlns="http://www.springframework.org/schema/beans"' //進去後跳出循環 if (hasOpeningTag(content)) { // End of meaningful data... break; } } //當遍歷到名稱空間了也就是"<beans xmlns=...>"尚未DOCTYPE聲明, //那麼就斷定他爲XSD驗證 return (isDtdValidated ? VALIDATION_DTD : VALIDATION_XSD); } catch (CharConversionException ex) { // Choked on some character encoding... // Leave the decision up to the caller. return VALIDATION_AUTO; } finally { //關流 reader.close(); } }
我這麼裏用的XSD驗證。也就是值爲3,傳遞給loadDocument的入參
最後一個入參,isNamespaceAware()
/** * Return whether or not the XML parser should be XML namespace aware. * * XML解析器是否支持XML名稱空間 */ public boolean isNamespaceAware() { return this.namespaceAware; }
默認false
看完五個參數後進入正主,loadDocument方法。
return this.documentLoader.loadDocument(inputSource, getEntityResolver(), this.errorHandler, getValidationModeForResource(resource), isNamespaceAware());
documentLoader屬性默認實現:
private DocumentLoader documentLoader = new DefaultDocumentLoader();
進入DefaultDocumentLoader類的loadDocument方法
/** * Load the {@link Document} at the supplied {@link InputSource} using the standard JAXP-configured * XML parser. * * 使用標準JAXP配置XML解析器加載InputSource的Document對象 */ @Override public Document loadDocument(InputSource inputSource, EntityResolver entityResolver, ErrorHandler errorHandler, int validationMode, boolean namespaceAware) throws Exception { //建立文檔構建器工廠對象,並初始化一些屬性 //若是驗證模式爲XSD,那麼強制支持XML名稱空間,並加上schema屬性 DocumentBuilderFactory factory = createDocumentBuilderFactory(validationMode, namespaceAware); if (logger.isDebugEnabled()) { logger.debug("Using JAXP provider [" + factory.getClass().getName() + "]"); } //建立一個JAXP文檔構建器 DocumentBuilder builder = createDocumentBuilder(factory, entityResolver, errorHandler); //按照XML文檔解析給定inputSource的內容,而後返回一個新的DOM對象 return builder.parse(inputSource); }
這樣就拿到了Document對象
回到XmlBeanDefinitionReader類的doLoadBeanDefinitions方法
跟蹤標記2的方法
//2.主要就是看這個方法,bean definitions的註冊 return registerBeanDefinitions(doc, resource); /** * Register the bean definitions contained in the given DOM document. * Called by {@code loadBeanDefinitions}. * <p>Creates a new instance of the parser class and invokes * {@code registerBeanDefinitions} on it. * * 註冊包含在給定DOM文檔對象中的 bean definition * 被loadBeanDefinitions方法所調用 * 解析class後建立一個新的實例,並調用registerBeanDefinitions方法 */ public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException { BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader(); //getRegistry()方法拿的是bean工廠對象,beanDefinition註冊在工廠中 //這個方法就是返回已經被註冊在工廠中的beanDefinitions數量 int countBefore = getRegistry().getBeanDefinitionCount(); //進入這個方法查看 documentReader.registerBeanDefinitions(doc, createReaderContext(resource)); //返回上個方法真正註冊在工廠中的beanDefinition數量 return getRegistry().getBeanDefinitionCount() - countBefore; } /** * 這個方法在剛建立的DefaultBeanDefinitionDocumentReader中 * * This implementation parses bean definitions according to the "spring-beans" XSD * (or DTD, historically). * <p>Opens a DOM Document; then initializes the default settings * specified at the {@code <beans/>} level; then parses the contained bean definitions. * * 根據「spring-beans"的XSD(或者DTD)去解析bean definition * 打開一個DOM文檔,而後初始化在<beans/>層級上指定的默認設置,而後解析包含在其中的bean definitions */ @Override public void registerBeanDefinitions(Document doc, XmlReaderContext readerContext) { //入參時建立的XmlReaderContext對象 this.readerContext = readerContext; logger.debug("Loading bean definitions"); //拿到了xml文檔對象的根元素 Element root = doc.getDocumentElement(); //進入這個方法進行查看 doRegisterBeanDefinitions(root); }
查看DefaultBeanDefinitionDocumentReader類的doRegisterBeanDefinitions方法。
/** * Register each bean definition within the given root {@code <beans/>} element. * * 在給定的根元素對象<beans/>中,註冊每個bean definition */ protected void doRegisterBeanDefinitions(Element root) { // Any nested <beans> elements will cause recursion in this method. In // order to propagate and preserve <beans> default-* attributes correctly, // keep track of the current (parent) delegate, which may be null. Create // the new (child) delegate with a reference to the parent for fallback purposes, // then ultimately reset this.delegate back to its original (parent) reference. // this behavior emulates a stack of delegates without actually necessitating one. // 任何被嵌套的<beans>元素都會致使此方法的遞歸。爲了正確的傳播和保存<beans>的默認屬性、 // 保持當前(父)代理的跟蹤,它可能爲null // 爲了可以回退,新的(子)代理具備父的引用,最終會重置this.delegate回到它的初始(父)引用。 // 這個行爲模擬了一堆代理,但實際上並不須要一個代理 BeanDefinitionParserDelegate parent = this.delegate; //2.1建立一個新的代理,並初始化一些默認值 this.delegate = createDelegate(getReaderContext(), root, parent); //默認名稱空間是"http://www.springframework.org/schema/beans" if (this.delegate.isDefaultNamespace(root)) { String profileSpec = root.getAttribute(PROFILE_ATTRIBUTE); //個人xml文件中沒有設置"profile",因此跳過 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; } } } //xml預處理,子類沒有重寫裏面就是空實現 preProcessXml(root); //2.2生成BeanDefinition,並註冊在工廠中 parseBeanDefinitions(root, this.delegate); //xml後處理,子類沒有重寫裏面就是空實現 postProcessXml(root); this.delegate = parent; }
跟蹤標記2.1的方法
此方法在DefaultBeanDefinitionDocumentReader類中
入參 getReaderContext() 方法返回的是先前建立的 XmlReaderContext 對象
//2.1建立一個新的代理,並初始化一些默認值 this.delegate = createDelegate(getReaderContext(), root, parent); protected BeanDefinitionParserDelegate createDelegate( XmlReaderContext readerContext, Element root, BeanDefinitionParserDelegate parentDelegate) { //用來解析XML bean definition的有狀態代理類,用來被主解析器和其餘擴展使用 BeanDefinitionParserDelegate delegate = new BeanDefinitionParserDelegate(readerContext); //進入此方法 delegate.initDefaults(root, parentDelegate); return delegate; }
進入BeanDefinitionParserDelegate類的initDefaults方法
/** * Initialize the default lazy-init, autowire, dependency check settings, * init-method, destroy-method and merge settings. Support nested 'beans' * element use cases by falling back to the given parent in case the * defaults are not explicitly set locally. * * 初始化默認值 default :·······等 * 經過使用 parent default,來解決嵌套的'beans'元素狀況,以防 default 在局部設定不明確 */ public void initDefaults(Element root, BeanDefinitionParserDelegate parent) { //進入此方法 populateDefaults(this.defaults, (parent != null ? parent.defaults : null), root); //默認沒作任何實現 this.readerContext.fireDefaultsRegistered(this.defaults); }
populateDefaults方法跟蹤
入參this.defaults有默認實現
private final DocumentDefaultsDefinition defaults = new DocumentDefaultsDefinition();
第二個入參:當第一次進入此方法時,parent爲null。
進入方法
/** * Populate the given DocumentDefaultsDefinition instance with the default lazy-init, * autowire, dependency check settings, init-method, destroy-method and merge settings. * Support nested 'beans' element use cases by falling back to <literal>parentDefaults</literal> * in case the defaults are not explicitly set locally. * * 用默認的值填充DocumentDefaultsDefinition實例 * 經過使用parentDefaults(父代理的default屬性),來解決嵌套的'beans'元素狀況,以防默認值在局部設定不明確 */ protected void populateDefaults(DocumentDefaultsDefinition defaults, DocumentDefaultsDefinition parentDefaults, Element root) { //根元素上若是沒有設定值,則返回"default"字符串 String lazyInit = root.getAttribute(DEFAULT_LAZY_INIT_ATTRIBUTE); //若是爲"default",先看parentDefaults有沒有,有用它的,沒有用"false" if (DEFAULT_VALUE.equals(lazyInit)) { // Potentially inherited from outer <beans> sections, otherwise falling back to false. // 可能從外部<beans>繼承,不然返回false lazyInit = (parentDefaults != null ? parentDefaults.getLazyInit() : FALSE_VALUE); } defaults.setLazyInit(lazyInit); String merge = root.getAttribute(DEFAULT_MERGE_ATTRIBUTE); if (DEFAULT_VALUE.equals(merge)) { // Potentially inherited from outer <beans> sections, otherwise falling back to false. merge = (parentDefaults != null ? parentDefaults.getMerge() : FALSE_VALUE); } defaults.setMerge(merge); String autowire = root.getAttribute(DEFAULT_AUTOWIRE_ATTRIBUTE); if (DEFAULT_VALUE.equals(autowire)) { // Potentially inherited from outer <beans> sections, otherwise falling back to 'no'. autowire = (parentDefaults != null ? parentDefaults.getAutowire() : AUTOWIRE_NO_VALUE); } defaults.setAutowire(autowire); // Don't fall back to parentDefaults for dependency-check as it's no longer supported in // <beans> as of 3.0. Therefore, no nested <beans> would ever need to fall back to it. // 依賴檢查不會採用parentDefaults,由於在3.0以後已再也不支持。所以,嵌套的<beans>不會使用parentDefaults defaults.setDependencyCheck(root.getAttribute(DEFAULT_DEPENDENCY_CHECK_ATTRIBUTE)); if (root.hasAttribute(DEFAULT_AUTOWIRE_CANDIDATES_ATTRIBUTE)) { defaults.setAutowireCandidates(root.getAttribute(DEFAULT_AUTOWIRE_CANDIDATES_ATTRIBUTE)); } else if (parentDefaults != null) { defaults.setAutowireCandidates(parentDefaults.getAutowireCandidates()); } if (root.hasAttribute(DEFAULT_INIT_METHOD_ATTRIBUTE)) { defaults.setInitMethod(root.getAttribute(DEFAULT_INIT_METHOD_ATTRIBUTE)); } else if (parentDefaults != null) { defaults.setInitMethod(parentDefaults.getInitMethod()); } if (root.hasAttribute(DEFAULT_DESTROY_METHOD_ATTRIBUTE)) { defaults.setDestroyMethod(root.getAttribute(DEFAULT_DESTROY_METHOD_ATTRIBUTE)); } else if (parentDefaults != null) { defaults.setDestroyMethod(parentDefaults.getDestroyMethod()); } //extractSource方法這裏沒有作任何實現,默認返回null defaults.setSource(this.readerContext.extractSource(root)); }
跟蹤標記2.2的方法
在DefaultBeanDefinitionDocumentReader類中
//2.2生成BeanDefinition,並註冊在工廠中 parseBeanDefinitions(root, this.delegate); /** * Parse the elements at the root level in the document: * "import", "alias", "bean". * * 解析在文檔中根層級的元素:"import", "alias", "bean". */ protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) { //默認名稱空間是"http://www.springframework.org/schema/beans" //進入條件 if (delegate.isDefaultNamespace(root)) { //獲取根元素下的子Node,注意,Node不必定是子標籤,多是回車,多是註釋 NodeList nl = root.getChildNodes(); for (int i = 0; i < nl.getLength(); i++) { Node node = nl.item(i); if (node instanceof Element) { //拿到了<beans>下的子標籤 Element ele = (Element) node; if (delegate.isDefaultNamespace(ele)) { //3.若是該標籤屬於beans的名稱空間,則進入這個方法 //xmlns="http://www.springframework.org/schema/beans" parseDefaultElement(ele, delegate); } else { //4.若是該標籤屬於其餘的名稱空間好比:context,aop等 //xmlns:aop="http://www.springframework.org/schema/aop" //xmlns:context="http://www.springframework.org/schema/context" delegate.parseCustomElement(ele); } } } } else { delegate.parseCustomElement(root); } }
一個名稱空間對應一個處理器。
好比在spring的配置文件applicatinoContext.xml中加入了這行代碼:
<!-- 啓用註解 --> <context:annotation-config />
能夠知道,標籤annotation-config屬於名稱空間context。
那麼context就對應着一個處理器。
解析的時候會先獲取context對應的處理器。處理器中又會初始化一些解析器出來,一個解析器就對應着一個標籤,像annotation-config就有對應本身的解析器。
不一樣的名稱空間、不一樣的標籤致使解析的方式很是的多,這裏只拿兩個常見的配置進行跟蹤。
由於字數超過了限制,因此分紅了三篇,點擊下篇繼續閱讀
https://www.jianshu.com/p/46e27afd7d96
——————————————————————————————————
beans
默認名稱空間的元素解析,和其餘自定義元素解析。通常來講,一個名稱空間對應一個處理器,處理器中又會初始化一些解析器出來。一個解析器就對應着一個標籤,能夠對不一樣的狀況進行解析。