摘要:在上一篇文章中,分析了容器的創建,加載資源文件,將資源文件讀取為。將文件中的注冊定義的對象。在中對屬性的解析委托給這個代理類來實現的。首先,獲取節點。
在上一篇文章中,分析了ApplicationContext容器的創建,加載資源文件,將資源文件讀取為Document。spring將xml文件中的Bean注冊spring定義的BeanDefinition對象。在DefaultBeanDefinitionDocumentReader中對Document屬性的解析委托給BeanDefinitionParserDelegate這個代理類來實現的。
Bean注冊前的準備DefaultBeanDefinitionDocumentReader的registerBeanDefinitions方法實現如下,首先獲取Document的根元素,接著調用doRegisterBeanDefinitions(root)進行注冊
public void registerBeanDefinitions(Document doc, XmlReaderContext readerContext) { this.readerContext = readerContext; //獲取根元素 Element root = doc.getDocumentElement(); //注冊BeanDefinition doRegisterBeanDefinitions(root); }
doRegisterBeanDefinitions(root)方法的實現如下:
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); //是否有profile屬性 if (StringUtils.hasText(profileSpec)) { String[] specifiedProfiles = StringUtils.tokenizeToStringArray( profileSpec, BeanDefinitionParserDelegate.MULTI_VALUE_ATTRIBUTE_DELIMITERS); if (!getReaderContext().getEnvironment().acceptsProfiles(specifiedProfiles)) { return; } } } preProcessXml(root); //解析BeanDefinition parseBeanDefinitions(root, this.delegate); postProcessXml(root); this.delegate = parent; }根據不同節點名進行解析
parseBeanDefinitions(root, this.delegate)方法是解析根源素下定義的每一個bean。首先,獲取節點List。其次,判斷每個元素是否為默認的命名空間中的元素,然后交給不同的方法去解析,具體的實現如下:
protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) { //如果根元素為默認命名空間中的元素 if (delegate.isDefaultNamespace(root)) { //獲取字元素List 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; //判斷此元素是否為默認命名空間的元素 if (delegate.isDefaultNamespace(ele)) { parseDefaultElement(ele, delegate); } else { delegate.parseCustomElement(ele); } } } } else { delegate.parseCustomElement(root); } }spring默認命名空間節點的解析
下面,我們首先看spring默認命名空間元素的解析過程,parseDefaultElement(ele, delegate)方法的實現如下:
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); } }
import節點的解析
import元素是引入其他的配置文件,resource屬性是配置文件的路徑,importBeanDefinitionResource(ele)方法的實現如下,省略了異常處理代碼:
protected void importBeanDefinitionResource(Element ele) { //獲取resource屬性值,即其他配置文件的路徑 String location = ele.getAttribute(RESOURCE_ATTRIBUTE); // 解析路徑,如"${user.dir}" 這樣的路徑是從在propertie文件中加載的 location = getReaderContext().getEnvironment().resolveRequiredPlaceholders(location); SetactualResources = new LinkedHashSet<>(4); // 判斷location 是絕對路徑還是相對路徑 boolean absoluteLocation = false; absoluteLocation = ResourcePatternUtils.isUrl(location) || ResourceUtils.toURI(location).isAbsolute(); // 絕對路徑 if (absoluteLocation) { //調用loadBeanDefinitions(location, actualResources)方法解析此配置文件 int importCount = getReaderContext().getReader().loadBeanDefinitions(location, actualResources); } } //相對路徑 else { int importCount; Resource relativeResource = getReaderContext().getResource().createRelative(location); if (relativeResource.exists()) { //調用loadBeanDefinitions(relativeResource)方法解析此配置文件 importCount = getReaderContext().getReader().loadBeanDefinitions(relativeResource); actualResources.add(relativeResource); } else { String baseLocation = getReaderContext().getResource().getURL().toString(); importCount = getReaderContext().getReader().loadBeanDefinitions( StringUtils.applyRelativePath(baseLocation, location), actualResources); } } } //廣播Import元素處理事件 Resource[] actResArray = actualResources.toArray(new Resource[0]); getReaderContext().fireImportProcessed(location, actResArray, extractSource(ele)); }
alias節點的解析
下面介紹alias元素的方法,processAliasRegistration(ele)方法的實現如下:
protected void processAliasRegistration(Element ele) { //獲取name屬性 String name = ele.getAttribute(NAME_ATTRIBUTE); //獲取alias屬性 String alias = ele.getAttribute(ALIAS_ATTRIBUTE); boolean valid = true; if (!StringUtils.hasText(name)) { getReaderContext().error("Name must not be empty", ele); valid = false; } if (!StringUtils.hasText(alias)) { getReaderContext().error("Alias must not be empty", ele); valid = false; } if (valid) { //調用SimpleAliasRegistry類的registerAlias(name, alias)進行注冊 getReaderContext().getRegistry().registerAlias(name, alias); getReaderContext().fireAliasRegistered(name, alias, extractSource(ele)); } }
在SimpleAliasRegistry中定義了aliasMap來存儲alias和name的關系,具體實現如下:
private final MapaliasMap = new ConcurrentHashMap<>(16); public void registerAlias(String name, String alias) { Assert.hasText(name, ""name" must not be empty"); Assert.hasText(alias, ""alias" must not be empty"); synchronized (this.aliasMap) { //如果alias和name相等,將此關系移除 if (alias.equals(name)) { this.aliasMap.remove(alias); } else { //先從aliasMap獲取key為alias的beanName String registeredName = this.aliasMap.get(alias); if (registeredName != null) { //如果已存在,return if (registeredName.equals(name)) { // An existing alias - no need to re-register return; } //如果不存在,判斷alias是否可以繼承,默認是true if (!allowAliasOverriding()) { throw new IllegalStateException("Cannot register alias "" + alias + "" for name "" + name + "": It is already registered for name "" + registeredName + ""."); } } //檢查是否存在循環依賴 checkForAliasCircle(name, alias); //注冊alias和name this.aliasMap.put(alias, name); } } }
bean節點的解析
processBeanDefinition(ele, delegate)實現如下:
protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) { //解析bean元素,創建BeanDefinitionHolder實例 BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele); if (bdHolder != null) { //完成必須的裝配 bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder); try { // 進行最終的注冊bean BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry()); } catch (BeanDefinitionStoreException ex) { getReaderContext().error("Failed to register bean definition with name "" + bdHolder.getBeanName() + """, ele, ex); } // Send registration event. getReaderContext().fireComponentRegistered(new BeanComponentDefinition(bdHolder)); } }
bean元素的解析和注冊相對復雜,在下一節中討論。
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/71125.html
摘要:在上一篇文章中,分析了容器的創建,加載資源文件,將資源文件讀取為。將文件中的注冊定義的對象。在中對屬性的解析委托給這個代理類來實現的。首先,獲取節點。 在上一篇文章中,分析了ApplicationContext容器的創建,加載資源文件,將資源文件讀取為Document。spring將xml文件中的Bean注冊spring定義的BeanDefinition對象。在DefaultBeanD...
摘要:在上一篇文章中,分析了容器的創建,加載資源文件,將資源文件讀取為。將文件中的注冊定義的對象。在中對屬性的解析委托給這個代理類來實現的。首先,獲取節點。 在上一篇文章中,分析了ApplicationContext容器的創建,加載資源文件,將資源文件讀取為Document。spring將xml文件中的Bean注冊spring定義的BeanDefinition對象。在DefaultBeanD...
摘要:在的方法中,遍歷每一個節點,判斷是否為默認命名空間中的節點,如果是非默認命名空間的,調用方法進行處理。在學習自定義標簽解析之前,先寫一個自定義標簽的。 在DefaultBeanDefinitionDocumentReader的parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate)方法中,遍歷每一...
摘要:在的方法中,遍歷每一個節點,判斷是否為默認命名空間中的節點,如果是非默認命名空間的,調用方法進行處理。在學習自定義標簽解析之前,先寫一個自定義標簽的。 在DefaultBeanDefinitionDocumentReader的parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate)方法中,遍歷每一...
摘要:在的方法中,遍歷每一個節點,判斷是否為默認命名空間中的節點,如果是非默認命名空間的,調用方法進行處理。在學習自定義標簽解析之前,先寫一個自定義標簽的。 在DefaultBeanDefinitionDocumentReader的parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate)方法中,遍歷每一...
閱讀 2045·2023-04-26 02:23
閱讀 1789·2021-09-03 10:30
閱讀 1351·2019-08-30 15:43
閱讀 1191·2019-08-29 16:29
閱讀 531·2019-08-29 12:28
閱讀 2332·2019-08-26 12:13
閱讀 2169·2019-08-26 12:01
閱讀 2400·2019-08-26 11:56