国产xxxx99真实实拍_久久不雅视频_高清韩国a级特黄毛片_嗯老师别我我受不了了小说

資訊專欄INFORMATION COLUMN

Spring解密 - 默認標簽的解析

snowLu / 432人閱讀

Spring是一個開源的設計層面框架,解決了業務邏輯層和其他各層的松耦合問題,將面向接口的編程思想貫穿整個系統應用,同時它也是Java工作中必備技能之一...

前言

緊跟上篇 Spring解密 - XML解析 與 Bean注冊 ,我們接著往下分析源碼

解密

Spring 的 XML 配置里面有兩大類聲明,一個是默認的如 ,另一類就是自定義的如,兩種標簽的解析方式差異是非常大的。parseBeanDefinitions 方法就是用來區分不同標簽所使用的解析方式。通過 node.getNamespaceURI() 方法獲取命名空間,判斷是默認命名空間還是自定義命名空間,并與 Spring 中固定的命名空間 http://www.springframework.org/schema/beans 進行比對,如果一致則采用parseDefaultElement(ele, delegate);否則就是delegate.parseCustomElement(ele);

默認標簽的解析

parseDefaultElement 對 4 種不同的標簽 import、alias、bean、beans 做了不同的處理,其中 bean 標簽的解析最為復雜也最為重要,所以我們將從 bean 開始深入分析,如果能理解此標簽的解析過程,其他標簽的解析自然會迎刃而解。上一篇中只是簡單描述了一下,本篇我們圍繞解析模塊詳細的探討一下

public class DefaultBeanDefinitionDocumentReader implements BeanDefinitionDocumentReader {

    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);
        }
        // import 標簽解析
        else if (delegate.nodeNameEquals(ele, NESTED_BEANS_ELEMENT)) {
            // beans標簽解析 遞歸方式 
            doRegisterBeanDefinitions(ele);
        }
    }

}

首先我們來分析下當類中的 processBeanDefinition(ele, delegate)

protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) {
    // 委托BeanDefinitionDelegate類的parseBeanDefinitionElement方法進行元素解析
    BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele);
    if (bdHolder != null) {
        // 當返回的bdHolder不為空的情況下若存在默認標簽的子節點下再有自定義屬性,還需要再次對自定義標簽進行解析
        bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder);
        try {
            // 解析完成后需要對解析后的bdHolder進行注冊,注冊操作委托給了BeanDefinitionReaderUtils的registerBeanDefinition方法
            BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry());
        }
        catch (BeanDefinitionStoreException ex) {
            getReaderContext().error("Failed to register bean definition with name "" +
                    bdHolder.getBeanName() + """, ele, ex);
        }
        // 最后發出響應事件,通知相關監聽器這個bean已經被加載
        getReaderContext().fireComponentRegistered(new BeanComponentDefinition(bdHolder));
    }
}

這段代碼中:

首先委托 BeanDefinitionParseDelegate 對節點做了解析,并返回了一個 BeanDefinitionHolder 的實例,在這個實例中已經包含了配置文件中配置的各種屬性了

如果在當前子節點中存在自定義屬性,則還需要對自定義標簽進行解析

解析完成后,需要對解析后的 bdHolder 進行注冊,同樣注冊操作委托給了 BeanDefinitionReaderUtils

最后發出響應事件,通知相關監聽器這個 bean 已經被加載

下面我們詳細分析下,Spring 是如何解析各個標簽和節點的

bean 標簽解析
public class BeanDefinitionParserDelegate {

    @Nullable
    public BeanDefinitionHolder parseBeanDefinitionElement(Element ele, @Nullable BeanDefinition containingBean) {
        // 獲取Bean標簽的ID屬性
        String id = ele.getAttribute(ID_ATTRIBUTE);
        // 獲取Bean標簽的Name屬性
        String nameAttr = ele.getAttribute(NAME_ATTRIBUTE);

        List aliases = new ArrayList<>();
        if (StringUtils.hasLength(nameAttr)) {
            // 將name屬性的值通過,; 進行分割 轉為字符串數字(即在配置文件中如配置多個name 在此做處理)
            String[] nameArr = StringUtils.tokenizeToStringArray(nameAttr, MULTI_VALUE_ATTRIBUTE_DELIMITERS);
            aliases.addAll(Arrays.asList(nameArr));
        }

        String beanName = id;

        // 如果ID為空 使用配置的第一個name屬性作為ID
        if (!StringUtils.hasText(beanName) && !aliases.isEmpty()) {
            beanName = aliases.remove(0);
            if (logger.isDebugEnabled()) {
                logger.debug("No XML "id" specified - using "" + beanName + "" as bean name and " + aliases + " as aliases");
            }
        }
    
        if (containingBean == null) {
            // 校驗beanName和aliases的唯一性
            // 內部核心為使用usedNames集合保存所有已經使用了的beanName和alisa
            checkNameUniqueness(beanName, aliases, ele);
        }
    
        // 進一步解析其他所有屬性到GenericBeanDefinition對象中
        AbstractBeanDefinition beanDefinition = parseBeanDefinitionElement(ele, beanName, containingBean);

        if (beanDefinition != null) {
            // 如果bean沒有指定beanName 那么使用默認規則為此Bean生成beanName
            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);
                        }
                    }
                    if (logger.isDebugEnabled()) {
                        logger.debug("Neither XML "id" nor "name" specified - " + "using generated bean name [" + beanName + "]");
                    }
                } catch (Exception ex) {
                    error(ex.getMessage(), ele);
                    return null;
                }
            }
            String[] aliasesArray = StringUtils.toStringArray(aliases);
            // 將信息封裝到BeanDefinitionHolder對象中
            return new BeanDefinitionHolder(beanDefinition, beanName, aliasesArray);
        }
    
        return null;
    }

}

該方法主要處理了 id、name、alias 等相關屬性,生成了 beanName,并且在重載函數 parseBeanDefinitionElement(ele, beanName, containingBean)方法中完成核心的標簽解析

接下來重點分析parseBeanDefinitionElement(Element ele, String beanName, @Nullable BeanDefinition containingBean)
看下它是如何完成標簽解析操作的

bean 節點與屬性解析
@Nullable
public AbstractBeanDefinition parseBeanDefinitionElement(
        Element ele, String beanName, @Nullable BeanDefinition containingBean) {

    this.parseState.push(new BeanEntry(beanName));

    // 獲取Bean標簽的class屬性
    String className = null;
    if (ele.hasAttribute(CLASS_ATTRIBUTE)) {
        className = ele.getAttribute(CLASS_ATTRIBUTE).trim();
    }

    // 獲取Bean標簽的parent屬性
    String parent = null;
    if (ele.hasAttribute(PARENT_ATTRIBUTE)) {
        parent = ele.getAttribute(PARENT_ATTRIBUTE);
    }

    try {

        // 創建用于承載屬性的AbstractBeanDefinition
        AbstractBeanDefinition bd = createBeanDefinition(className, parent);

        // 獲取bean標簽各種屬性
        parseBeanDefinitionAttributes(ele, beanName, containingBean, bd);
        // 解析description標簽
        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);
    }
    catch (NoClassDefFoundError err) {
        error("Class that bean class [" + className + "] depends on not found", ele, err);
    }
    catch (Throwable ex) {
        error("Unexpected failure during bean definition parsing", ele, ex);
    }
    finally {
        this.parseState.pop();
    }

    return null;
}

進一步解析其他屬性和元素(元素和屬性很多,所以這是一個龐大的工作量)并統一封裝至 GenericBeanDefinition 中, 解析完成這些屬性和元素之后,如果檢測到 bean 沒有指定的 beanName,那么便使用默認的規則為 bean 生成一個 beanName

// BeanDefinitionParserDelegate.java
protected AbstractBeanDefinition createBeanDefinition(@Nullable String className, @Nullable String parentName)
        throws ClassNotFoundException {

    return BeanDefinitionReaderUtils.createBeanDefinition(
            parentName, className, this.readerContext.getBeanClassLoader());
}

public class BeanDefinitionReaderUtils {

    public static AbstractBeanDefinition createBeanDefinition(
            @Nullable String parentName, @Nullable String className, @Nullable ClassLoader classLoader) throws ClassNotFoundException {

        GenericBeanDefinition bd = new GenericBeanDefinition();
        // parentName可能為空
        bd.setParentName(parentName);

        // 如果classLoader不為空 
        // 則使用傳入的classLoader同一虛擬機加載類對象 否則只記錄classLoader

        if (className != null) {
            if (classLoader != null) {
                bd.setBeanClass(ClassUtils.forName(className, classLoader));
            }
            else {
                bd.setBeanClassName(className);
            }
        }
        return bd;
    }
}

BeanDefinition 在容器中的內部表示形式,BeanDefinition 是一一對應的。同時 BeanDefinition 會被注冊到 BeanDefinitionRegistry 中,BeanDefinitionRegistry 就像 Spring 配置信息的內存數據庫。

至此 createBeanDefinition(className, parent); 已經說完了,而且我們也獲得了 用于承載屬性的AbstractBeanDefinition,接下來看看 parseBeanDefinitionAttributes(ele, beanName, containingBean, bd); 是如何解析 bean 中的各種標簽屬性的

public class BeanDefinitionParserDelegate {

    public AbstractBeanDefinition parseBeanDefinitionAttributes(Element ele, String beanName,
            @Nullable BeanDefinition containingBean, AbstractBeanDefinition bd) {

        // ...省略詳細代碼,該部分代碼主要就是通過 if else 判斷是否含有指定的屬性,如果有就 bd.set(attribute);

        return bd;
    }

}

bean 標簽的完整解析到這就已經全部結束了,其中 bean 標簽下的元素解析都大同小異,有興趣的可以自己跟蹤一下源代碼看看 qualifier、lookup-method 等解析方式(相對 bean 而言不復雜)。自定義標簽內容較多會在下一章詳細介紹。

最后將獲取到的信息封裝到 BeanDefinitionHolder 實例中

// BeanDefinitionParserDelegate.java
@Nullable
public BeanDefinitionHolder parseBeanDefinitionElement(Element ele, @Nullable BeanDefinition containingBean) {
    // ...
    return new BeanDefinitionHolder(beanDefinition, beanName, aliasesArray);
}
注冊解析的 BeanDefinition

在解析完配置文件后我們已經獲取了 bean 的所有屬性,接下來就是對 bean 的注冊了

public class BeanDefinitionReaderUtils {

    public static void registerBeanDefinition(
            BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry)
            throws BeanDefinitionStoreException {
    
        // 使用 beanName 做唯一標識符
        String beanName = definitionHolder.getBeanName();
    
        // 注冊bean的核心代碼
        registry.registerBeanDefinition(beanName, definitionHolder.getBeanDefinition());
    
        // 為bean注冊所有的別名
        String[] aliases = definitionHolder.getAliases();
        if (aliases != null) {
            for (String alias : aliases) {
                registry.registerAlias(beanName, alias);
            }
        }
    }

}

以上代碼主要完成兩個功能,一是使用 beanName 注冊 beanDefinition,二是完成了對別名的注冊

BeanName 注冊 BeanDefinition
public class DefaultListableBeanFactory {

    @Override
    public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition)
            throws BeanDefinitionStoreException {

        Assert.hasText(beanName, "Bean name must not be empty");
        Assert.notNull(beanDefinition, "BeanDefinition must not be null");

        if (beanDefinition instanceof AbstractBeanDefinition) {
            try {

                // 注冊前的最后一次校驗,這里的校驗不同于XML文件校驗
                // 主要是對于AbstractBeanDefinition屬性中的methodOverrides校驗
                // 校驗methodOverrides是否與工廠方法并存或者methodOverrides對于的方法根本不存在
                ((AbstractBeanDefinition) beanDefinition).validate();
            }
            catch (BeanDefinitionValidationException ex) {
                throw new BeanDefinitionStoreException(beanDefinition.getResourceDescription(), beanName,
                        "Validation of bean definition failed", ex);
            }
        }

        BeanDefinition oldBeanDefinition;
        // 獲取緩存中的 beanDefinition
        oldBeanDefinition = this.beanDefinitionMap.get(beanName);
        if (oldBeanDefinition != null) {
            // 如果緩存中存在 判斷是否允許覆蓋
            if (!isAllowBeanDefinitionOverriding()) {
                throw new BeanDefinitionStoreException(beanDefinition.getResourceDescription(), beanName,
                        "Cannot register bean definition [" + beanDefinition + "] for bean "" + beanName +
                        "": There is already [" + oldBeanDefinition + "] bound.");
            }
            else if (oldBeanDefinition.getRole() < beanDefinition.getRole()) {
                // e.g. was ROLE_APPLICATION, now overriding with ROLE_SUPPORT or ROLE_INFRASTRUCTURE
                if (this.logger.isWarnEnabled()) {
                    this.logger.warn("Overriding user-defined bean definition for bean "" + beanName +
                            "" with a framework-generated bean definition: replacing [" +
                            oldBeanDefinition + "] with [" + beanDefinition + "]");
                }
            }
            else if (!beanDefinition.equals(oldBeanDefinition)) {
                if (this.logger.isInfoEnabled()) {
                    this.logger.info("Overriding bean definition for bean "" + beanName +
                            "" with a different definition: replacing [" + oldBeanDefinition +
                            "] with [" + beanDefinition + "]");
                }
            }
            else {
                if (this.logger.isDebugEnabled()) {
                    this.logger.debug("Overriding bean definition for bean "" + beanName +
                            "" with an equivalent definition: replacing [" + oldBeanDefinition +
                            "] with [" + beanDefinition + "]");
                }
            }
            // 如果允許覆蓋,保存beanDefinition到beanDefinitionMap中
            this.beanDefinitionMap.put(beanName, beanDefinition);
        }
        else {
            // 判斷是否已經開始創建bean
            if (hasBeanCreationStarted()) {
                // Cannot modify startup-time collection elements anymore (for stable iteration)
                synchronized (this.beanDefinitionMap) {

                    // 保存beanDefinition到beanDefinitionMap中
                    this.beanDefinitionMap.put(beanName, beanDefinition);

                    // 更新已經注冊的beanName
                    List updatedDefinitions = new ArrayList<>(this.beanDefinitionNames.size() + 1);
                    updatedDefinitions.addAll(this.beanDefinitionNames);
                    updatedDefinitions.add(beanName);
                    this.beanDefinitionNames = updatedDefinitions;
                    if (this.manualSingletonNames.contains(beanName)) {
                        Set updatedSingletons = new LinkedHashSet<>(this.manualSingletonNames);
                        updatedSingletons.remove(beanName);
                        this.manualSingletonNames = updatedSingletons;
                    }
                }
            }
            else {
                // 還沒開始創建bean
                this.beanDefinitionMap.put(beanName, beanDefinition);
                this.beanDefinitionNames.add(beanName);
                this.manualSingletonNames.remove(beanName);
            }
            this.frozenBeanDefinitionNames = null;
        }

        if (oldBeanDefinition != null || containsSingleton(beanName)) {
            // 重置beanName對應的緩存
            resetBeanDefinition(beanName);
        }
    }

}

AbstractBeanDefinition 的校驗,主要是針對 AbstractBeanDefinitionmethodOverrides 屬性的

beanName 已經注冊的情況的處理,如果設置了不允許 bean 的覆蓋,則需要拋出異常,否則直接覆蓋

使用 beanName 作為 key,beanDefinition 為 Value 加入 beanDefinitionMap 存儲

如果緩存中已經存在,并且該 bean 為單例模式則清楚 beanName 對應的緩存

注冊別名

注冊好了 beanDefinition,接下來就是注冊 alias。注冊的 aliasbeanName 的對應關系存放在了 aliasMap 中,沿著類的繼承鏈會發現 registerAlias 的方法是在 SimpleAliasRegistry 中實現的

public class SimpleAliasRegistry {

    /** Map from alias to canonical name */
    private final Map aliasMap = 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");
        if (alias.equals(name)) {
            // 如果beanName與alias相同的話不記錄alias 并刪除對應的alias
            this.aliasMap.remove(alias);
        } else {
            String registeredName = this.aliasMap.get(alias);
            if (registeredName != null) {
                if (registeredName.equals(name)) {
                    // 如果別名已經注冊過并且指向的name和當前name相同 不做任何處理
                    return;
                }
                // 如果alias不允許被覆蓋則拋出異常
                if (!allowAliasOverriding()) {
                    throw new IllegalStateException("Cannot register alias "" + alias + "" for name "" + name + "": It is already registered for name "" + registeredName + "".");
                }
            }
            // 校驗循環指向依賴 如A->B B->C C->A則出錯
            checkForAliasCircle(name, alias);
            this.aliasMap.put(alias, name);
        }
    }

}

通過 checkForAliasCircle() 方法來檢查 alias 循環依賴,當 A -> B 存在時,若再次出現 A -> C -> B 則會拋出異常:

protected void checkForAliasCircle(String name, String alias) {
    if (hasAlias(alias, name)) {
        throw new IllegalStateException("Cannot register alias "" + alias +
                "" for name "" + name + "": Circular reference - "" +
                name + "" is a direct or indirect alias for "" + alias + "" already");
    }
}

public boolean hasAlias(String name, String alias) {
    for (Map.Entry entry : this.aliasMap.entrySet()) {
        String registeredName = entry.getValue();
        if (registeredName.equals(name)) {
            String registeredAlias = entry.getKey();
            return (registeredAlias.equals(alias) || hasAlias(registeredAlias, alias));
        }
    }
    return false;
}

至此,注冊別名也完成了,主要完成了以下幾個工作

如果 beanNamealias 相同的話不記錄 alias 并刪除對應的 alias

如果別名已經注冊過并且指向的name和當前name相同 不做任何處理

如果別名已經注冊過并且指向的name和當前name不相同 判斷是否允許被覆蓋

校驗循環指向依賴 如A->B B->C C->A則出錯

發送通知

通知監聽器解析及注冊完成

//DefaultBeanDefinitionDocumentReader.java
protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) {
    // Send registration event.
    getReaderContext().fireComponentRegistered(new BeanComponentDefinition(bdHolder));
}

通過 fireComponentRegistered 方法進行通知監聽器解析及注冊完成工作,這里的實現只為擴展,當程序開發人員需要對注冊 BeanDefinition事件進行監聽時,可以通過注冊監聽器的方式并將處理邏輯寫入監聽器中,目前 Spring 中并沒有對此事件做任何處理

其中 ReaderContext 是在類 XmlBeanDefinitionReader 中調用 createReaderContext 生成的,然后調用 fireComponentRegistered()

alias 標簽解析

Spring 提供了 方式來進行別名的配置,該標簽解析是在 processAliasRegistration(Element ele) 方法中完成的

public class DefaultBeanDefinitionDocumentReader {

    protected void processAliasRegistration(Element ele) {
        // 獲取 alisa 標簽 name 屬性
        String name = ele.getAttribute(NAME_ATTRIBUTE);
        // 獲取 alisa 標簽 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) {
            try {
                // 進行別名注冊
                getReaderContext().getRegistry().registerAlias(name, alias);
            } catch (Exception ex) {
                getReaderContext().error("Failed to register alias "" + alias +
                        "" for bean with name "" + name + """, ele, ex);
            }
            // 別名注冊后告知監聽器做相應處理
            getReaderContext().fireAliasRegistered(name, alias, extractSource(ele));
        }
    }

}

首先對 alias 標簽屬性進行提取校驗,校驗通過后進行別名注冊,別名注冊和 bean 標簽解析中的別名注冊一直,此處不再贅述

import 標簽解析
public class DefaultBeanDefinitionDocumentReader {

    protected void importBeanDefinitionResource(Element ele) {
        // 獲取import標簽的resource屬性
        String location = ele.getAttribute(RESOURCE_ATTRIBUTE);
        // 如果不存在則不做任何處理
        if (!StringUtils.hasText(location)) {
            getReaderContext().error("Resource location must not be empty", ele);
            return;
        }
    
        // 解析占位符屬性 格式如"${user.dir}"
        location = getReaderContext().getEnvironment().resolveRequiredPlaceholders(location);
    
        Set actualResources = new LinkedHashSet<>(4);
    
        // 判斷資源是絕對路徑還是相對路徑
        boolean absoluteLocation = false;
        try {
            absoluteLocation = ResourcePatternUtils.isUrl(location) || ResourceUtils.toURI(location).isAbsolute();
        } catch (URISyntaxException ex) {
            // cannot convert to an URI, considering the location relative
            // unless it is the well-known Spring prefix "classpath*:"
        }
    
        // 如果是絕對路徑則直接根據地址加載對應的配置文件
        if (absoluteLocation) {
            try {
                int importCount = getReaderContext().getReader().loadBeanDefinitions(location, actualResources);
                if (logger.isDebugEnabled()) {
                    logger.debug("Imported " + importCount + " bean definitions from URL location [" + location + "]");
                }
            } catch (BeanDefinitionStoreException ex) {
                getReaderContext().error("Failed to import bean definitions from URL location [" + location + "]", ele, ex);
            }
        } else {
            try {
                int importCount;
                // 根據相對路徑加載資源
                Resource relativeResource = getReaderContext().getResource().createRelative(location);
                if (relativeResource.exists()) {
                    importCount = getReaderContext().getReader().loadBeanDefinitions(relativeResource);
                    actualResources.add(relativeResource);
                } else {
                    String baseLocation = getReaderContext().getResource().getURL().toString();
                    importCount = getReaderContext().getReader().loadBeanDefinitions(StringUtils.applyRelativePath(baseLocation, location), actualResources);
                }
                if (logger.isDebugEnabled()) {
                    logger.debug("Imported " + importCount + " bean definitions from relative location [" + location + "]");
                }
            } catch (IOException ex) {
                getReaderContext().error("Failed to resolve current resource location", ele, ex);
            } catch (BeanDefinitionStoreException ex) {
                getReaderContext().error("Failed to import bean definitions from relative location [" + location + "]", ele, ex);
            }
        }
        // 解析后進行監聽器激活處理
        Resource[] actResArray = actualResources.toArray(new Resource[actualResources.size()]);
        getReaderContext().fireImportProcessed(location, actResArray, extractSource(ele));
    }

}

完成了對 import 標簽的處理,首先就是獲取 resource 屬性所表示的路徑,接著解析路徑中的屬性占位符 如 ${user.dir},然后判定 location 是絕對路徑還是相對路徑,如果是絕對路徑則遞歸調用 bean 的解析過程(loadBeanDefinitions(location, actualResources);),進行另一次解析,如果是相對路徑則計算出絕對路徑并進行解析,最后通知監聽器,解析完成

總結

熬過幾個無人知曉的秋冬春夏,撐過去一切都會順著你想要的方向走...

說點什么

全文代碼:https://gitee.com/battcn/battcn-spring-source/tree/master/Chapter1

個人QQ:1837307557

battcn開源群(適合新手):391619659

微信公眾號:battcn(歡迎調戲)

文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。

轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/68201.html

相關文章

  • Spring解密 - 自定義標簽解析

    摘要:自定義標簽在講解自定義標簽解析之前,先看下如何自定義標簽定義文件定義一個文件描述組件內容聲明命名空間值得注意的是與可以是不存在,只要映射到指定就行了。 Spring是一個開源的設計層面框架,解決了業務邏輯層和其他各層的松耦合問題,將面向接口的編程思想貫穿整個系統應用,同時它也是Java工作中必備技能之一... 前言 在 上一節 Spring解密 - 默認標簽的解析 中,重點分析了...

    Taste 評論0 收藏0
  • Spring解密 - XML解析 與 Bean注冊

    摘要:解密是注冊及加載的默認實現,整個模板中它可以稱得上始祖。中是這樣介紹的自動裝配時忽略給定的依賴接口,比如通過其他方式解析上下文注冊依賴,類似于通過進行的注入或者通過進行的注入。解析是資源文件讀取解析注冊的實現,要重點關注該類。 Spring是一個開源的設計層面框架,解決了業務邏輯層和其他各層的松耦合問題,將面向接口的編程思想貫穿整個系統應用,同時它也是Java工作中必備技能之一......

    cncoder 評論0 收藏0
  • Spring Cloud 參考文檔(Spring Cloud Config Server)

    摘要:,這是標記配置文件集版本化的服務器端特性。要配置對稱密鑰,需要將設置為秘密字符串或使用環境變量將其排除在純文本配置文件之外。 Spring Cloud Config Server Spring Cloud Config Server為外部配置提供基于HTTP資源的API(名稱—值對或等效的YAML內容),通過使用@EnableConfigServer注解,服務器可嵌入Spring Bo...

    harryhappy 評論0 收藏0
  • Spring解密 - Bean加載流程

    摘要:判斷調用哪個構造方法的過程會采用緩存機制,如果已經解析過則不需要重復解析而是從中的屬性緩存的值去取,否則需再次解析。 Spring是一個開源的設計層面框架,解決了業務邏輯層和其他各層的松耦合問題,將面向接口的編程思想貫穿整個系統應用,同時它也是Java工作中必備技能之一... 前言 在 Spring解密 - XML解析 與 Bean注冊 中,講了 Bean的解析,本章將詳細講解Sp...

    bawn 評論0 收藏0
  • Spring Cloud 參考文檔(Spring Cloud Context:應用程序上下文服務)

    摘要:它們的優先級低于或以及作為創建應用程序過程的正常部分添加到子級的任何其他屬性源。為引導配置類使用單獨的包名稱,并確保或注解的配置類尚未涵蓋該名稱。在這種情況下,它會在刷新時重建,并重新注入其依賴項,此時,它們將從刷新的重新初始化。 Spring Cloud Context:應用程序上下文服務 Spring Boot有一個關于如何使用Spring構建應用程序的主見,例如,它具有通用配置文...

    魏明 評論0 收藏0

發表評論

0條評論

snowLu

|高級講師

TA的文章

閱讀更多
最新活動
閱讀需要支付1元查看
<