1. 程式人生 > >Spring系列(五) 容器初始化過程原始碼

Spring系列(五) 容器初始化過程原始碼

IoC/DI 的概念

容器是Spring的核心之一(另一個核心是AOP). 有了容器, IOC才可能實現.

  • 什麼使IoC? IoC就是將類自身管理的與其由依賴關係的物件的建立/關聯和管理交予容器實現, 容器按照配置(比如xml檔案)來組織應用物件的建立和關聯.

  • 什麼使DI? DI是IoC的實現方式, 由容器在程式初始化的時候將類的依賴物件注入進去.

  • IoC和DI的關係? IoC(Inversion of Control)是一種設計原則, 可以減少程式碼的耦合度, DI(Dependency Injection)是IOC的具體實現方式, 還有其他的實現方式如 DL(Dependency Lookup).

Spring 容器

ClassPathXmlApplicationContext類應該都比較熟悉, 從熟悉的事物開始尋找線索.

下載Spring原始碼後用idea開啟, 找到類ClassPathXmlApplicationContext, idea可以使用 ctrl+N 輸入類名搜尋, 開啟原始檔, 按 ctrl+Alt+U 可以生成類圖.

BeanFactoryResourceLoader是兩個頂層介面. BeanFactory是Bean的工廠,定義了IoC基本的功能. ResourceLoader是資源載入的策略介面,定義了載入資源的基本規範, ApplicationContext

需要此介面的功能.

BeanFactory提供了容器最基本的功能, 其中定義的方法會頻繁使用, 介面定義如下:

public interface BeanFactory {
    // 一個標記, 帶有此標記開頭的類不是bean, 而是工廠本身
    String FACTORY_BEAN_PREFIX = "&";
    // 下面幾個方法是各種獲取bean的方式
    Object getBean(String name) throws BeansException;
    <T> T getBean(String name, @Nullable Class<T> requiredType) throws BeansException;
    Object getBean(String name, Object... args) throws BeansException;
    <T> T getBean(Class<T> requiredType) throws BeansException;
    <T> T getBean(Class<T> requiredType, Object... args) throws BeansException;
    // 判斷bean是否存在
    boolean containsBean(String name);
    // bean作用域是否單例
    boolean isSingleton(String name) throws NoSuchBeanDefinitionException;
    // bean作用域是否原型
    boolean isPrototype(String name) throws NoSuchBeanDefinitionException;
    // bean是否與給定解析型別匹配
    boolean isTypeMatch(String name, ResolvableType typeToMatch) throws NoSuchBeanDefinitionException;
    boolean isTypeMatch(String name, @Nullable Class<?> typeToMatch) throws NoSuchBeanDefinitionException;
    // 獲取bean型別
    @Nullable
    Class<?> getType(String name) throws NoSuchBeanDefinitionException;
    //獲取bean別名陣列
    String[] getAliases(String name);

}

ApplicationContext 擴充套件了BeanFactory的功能, 除了作為工廠外, 它還提供了訊息國際化(MessageSource), 獲取環境bean(EnvironmentCapable), 容器訊息釋出(ApplicationEventPublisher)等功能. 因為它包含了容器以外的這些功能, 所以對了解容器來說多少會產生干擾. 事實上, 檢視BeanFactory的子類(在類圖上選中類,或者在原始碼檢視中按Ctrl+Alt+B)能從它的實現中找到DefaultListableBeanFactory, 從名稱上二者在繼承該關係上應該比較近, 功能也比較純粹, 沒有類似ApplicationContext的其他干擾.

DefaultListableBeanFactory 類是最基本的容器實現類, 它的繼承關係如下圖. 作為bean的工廠, 它的職責就是生產bean, 基本功能正是頂級介面BeanFactory定義的那些方法. 它上級的介面擴充套件了自動裝配的能力(AutowireCapableBeanFactory), 註冊和獲取等操作BeanDefinition例項的能力(BeanDefinitionRegistry).

BeanDefinition

BeanDefinition 用來抽象bean定義在spring中的抽象, 最終spring將外部配置的bean轉化為BeanDefinition的例項儲存.

容器初始化過程

容器初始化過程分為三步, 資源Resource定位, 解析載入, 註冊.

DefaultListableBeanFactory是工廠, 繼承它的子類只有一個XmlBeanFactory, 它被標註為@Deprecated.所以不應該在應用中使用該類, 但它可以作為了解原始碼的入口. 它有個XmlBeanDefinitionReader的私有變數直接new初始化, 引數this將工廠例項傳給這個物件, 這樣它就有了工廠的引用, 方便內部處理.

public class XmlBeanFactory extends DefaultListableBeanFactory {

    private final XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(this);


    public XmlBeanFactory(Resource resource) throws BeansException {
        this(resource, null);
    }

    public XmlBeanFactory(Resource resource, BeanFactory parentBeanFactory) throws BeansException {
        super(parentBeanFactory);
        // 這個是呼叫實際載入資源的方法
        this.reader.loadBeanDefinitions(resource);
    }

大概串一下初始化的執行流程:

  1. 獲得一個Resource例項resource, 其實就是xml檔案生成的輸入流
  2. 例項化DefaultListableBeanFactory工廠beanFactory, 將resource作為構造引數傳入
  3. beanFactory例項化, 生成XmlBeanDefinitionReader的例項reader, 並將beanFactory的引用傳遞給他
  4. 在beanFactory建構函式中呼叫reader的方法載入resource, 解析生成一系列BeanDefinition的例項, 因為readere有工廠的例項, 所以這些例項可以註冊到工廠中

載入XML Bean的關鍵程式碼

下面按照呼叫關係跟蹤程式碼, 忽略其他的xml元素, 最終目標是找到載入註冊bean的機制.

XmlBeanDefinitionReader --> loadBeanDefinitions --> doLoadBeanDefinitions --> registerBeanDefinitions DefaultBeanDefinitionDocumentReader: BeanDefinitionDocumentReader --> registerBeanDefinitions --> doRegisterBeanDefinitions --> parseBeanDefinitions --> parseDefaultElement / processBeanDefinition BeanDefinitionParserDelegate --> parseBeanDefinitionElement BeanDefinitionReaderUtils --> registerBeanDefinition DetaultListableBeanFactory --> registerBeanDefinition

一. XmlBeanDefinitionReader.loadBeanDefinitions(EncodedResource encodedResource) 生成InputSource物件(用來初始化XML Dom物件)

InputStream inputStream = encodedResource.getResource().getInputStream();
try {
    // 生成例項, 後面用來載入dom
    InputSource inputSource = new InputSource(inputStream);
    if (encodedResource.getEncoding() != null) {
        inputSource.setEncoding(encodedResource.getEncoding());
    }
    // 生成InputSource後,呼叫這個方法
    return doLoadBeanDefinitions(inputSource, encodedResource.getResource());
}
finally {
    inputStream.close();
}

二. XmlBeanDefinitionReader.doLoadBeanDefinitions(InputSource inputSource, Resource resource) 載入生成Xml Document物件

// 生成doc例項
Document doc = doLoadDocument(inputSource, resource);
return registerBeanDefinitions(doc, resource);

三. XmlBeanDefinitionReader.registerBeanDefinitions(Document doc, Resource resource)

// 生成BeanDefinitionDocumentReader的例項, 預設實現為生成DefaultBeanDefinitionDocumentReader類的例項, 通過BeanUtil工具的例項化方法生成
BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader();
int countBefore = getRegistry().getBeanDefinitionCount();
// 傳入doc和資源的上下文物件, 註冊bean
documentReader.registerBeanDefinitions(doc, createReaderContext(resource));

四. registerBeanDefinitions(Document doc, XmlReaderContext readerContext) BeanDefinitionDocumentReader是介面, 實現類為DefaultBeanDefinitionDocumentReader

@Override
public void registerBeanDefinitions(Document doc, XmlReaderContext readerContext) {
    this.readerContext = readerContext;
    logger.debug("Loading bean definitions");
    // 獲取根節點
    Element root = doc.getDocumentElement();
    // 從根節點開始, 呼叫的這個方法會遞迴子節點
    doRegisterBeanDefinitions(root);
}

五. doRegisterBeanDefinitions(Element root) 類為DefaultBeanDefinitionDocumentReader

protected void doRegisterBeanDefinitions(Element root) {
    //任何巢狀的<beans>元素都將導致此方法的遞迴。為了正確傳播和保留<beans> default- *屬性,請跟蹤當前(父)委託,該委託可以為null。建立新的(子)委託,引用父項以進行回退,然後最終將this.delegate重置為其原始(父)引用。此行為模擬了一堆代理,而實際上並不需要一個代理。
    BeanDefinitionParserDelegate parent = this.delegate;
    this.delegate = createDelegate(getReaderContext(), root, parent);

    if (this.delegate.isDefaultNamespace(root)) {
        // 下面這一塊程式碼主要是做profile檢查, 沒有啟用profile的bean不載入, 將直接return
        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)) {
                if (logger.isInfoEnabled()) {
                    logger.info("Skipped XML bean definition file due to specified profiles [" + profileSpec +
                            "] not matching: " + getReaderContext().getResource());
                }
                return;
            }
        }
    }

    preProcessXml(root);
    // 具體解析的方法, pre和post的在這個類中為空方法
    parseBeanDefinitions(root, this.delegate);
    postProcessXml(root);

    this.delegate = parent;
}

六. parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) 類為DefaultBeanDefinitionDocumentReader

/**
* 解析文件中的根節點
* "import", "alias", "bean".
*/
protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) {
    // 是根節點,就獲取子節點, 遍歷,如果是根"import", "alias", "bean", 就呼叫parseDefaultElement, 否則parseCustomElement
    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;
                if (delegate.isDefaultNamespace(ele)) {
                    parseDefaultElement(ele, delegate);
                }
                else {
                    delegate.parseCustomElement(ele);
                }
            }
        }
    }
    else {
        delegate.parseCustomElement(root);
    }
}

七. parseDefaultElement(Element ele, BeanDefinitionParserDelegate delegate) 類為DefaultBeanDefinitionDocumentReader

private void parseDefaultElement(Element ele, BeanDefinitionParserDelegate delegate) {
        if (delegate.nodeNameEquals(ele, IMPORT_ELEMENT)) {
            // 解析"import"元素, 這個方法會定位import的資源位置並重復第一步開始的步驟
            importBeanDefinitionResource(ele);
        }
        else if (delegate.nodeNameEquals(ele, ALIAS_ELEMENT)) {
            // 別名"alias"註冊
            processAliasRegistration(ele);
        }
        else if (delegate.nodeNameEquals(ele, BEAN_ELEMENT)) {
            // 前方高能... 處理bean元素
            processBeanDefinition(ele, delegate);
        }
        else if (delegate.nodeNameEquals(ele, NESTED_BEANS_ELEMENT)) {
            // 遞迴"beans"
            doRegisterBeanDefinitions(ele);
        }
    }

八. processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) 類為DefaultBeanDefinitionDocumentReader

/**
*  處理bean元素的定義, 並且註冊
*/
protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) {
    // 獲取bean的包裝物件,程式碼見第九步
    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));
    }
}

九. parseBeanDefinitionElement(Element ele, @Nullable BeanDefinition containingBean) 類為 BeanDefinitionParserDelegate 關注兩個例項化過程,一個是BeanDefinition, 一個是其裝飾物件BeanDefinitionHolder的例項

/**
* 解析bean元素, 可能會返回null, 如果有錯誤則報告給
* {@link org.springframework.beans.factory.parsing.ProblemReporter}.
*/
@Nullable
public BeanDefinitionHolder parseBeanDefinitionElement(Element ele, @Nullable BeanDefinition containingBean) {
    String id = ele.getAttribute(ID_ATTRIBUTE);
    String nameAttr = ele.getAttribute(NAME_ATTRIBUTE);

    List<String> aliases = new ArrayList<>();
    if (StringUtils.hasLength(nameAttr)) {
        String[] nameArr = StringUtils.tokenizeToStringArray(nameAttr, MULTI_VALUE_ATTRIBUTE_DELIMITERS);
        aliases.addAll(Arrays.asList(nameArr));
    }

    String beanName = 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) {
        checkNameUniqueness(beanName, aliases, ele);
    }
    // 例項化一個 BeanDefination 例項
    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);
                    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);
        return new BeanDefinitionHolder(beanDefinition, beanName, aliasesArray);
    }

    return null;
}

十. registerBeanDefinition(BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry) 是類的靜態方法BeanDefinitionReaderUtils ;給registry物件呼叫

// 註冊bean的最終方法
public static void registerBeanDefinition( BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry) throws BeanDefinitionStoreException {

    // 使用首要名稱註冊bean
    String beanName = definitionHolder.getBeanName();
    // 註冊bean, 具體實現在類DetaultListableBeanFactory中
    registry.registerBeanDefinition(beanName, definitionHolder.getBeanDefinition());

    // 註冊bean的別名
    String[] aliases = definitionHolder.getAliases();
    if (aliases != null) {
        for (String alias : aliases) {
            registry.registerAlias(beanName, alias);
        }
    }
}

十一. registerBeanDefinition(String beanName, BeanDefinition beanDefinition) 在類DetaultListableBeanFactory

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

    BeanDefinition existingDefinition = this.beanDefinitionMap.get(beanName);
    if (existingDefinition != null) {
        ....
        // 存到map裡面
        this.beanDefinitionMap.put(beanName, beanDefinition);
    }
    else {
        if (hasBeanCreationStarted()) {
            // Cannot modify startup-time collection elements anymore (for stable iteration)
            synchronized (this.beanDefinitionMap) {
                this.beanDefinitionMap.put(beanName, beanDefinition);
                List<String> updatedDefinitions = new ArrayList<>(this.beanDefinitionNames.size() + 1);
                updatedDefinitions.addAll(this.beanDefinitionNames);
                updatedDefinitions.add(beanName);
                this.beanDefinitionNames = updatedDefinitions;
                if (this.manualSingletonNames.contains(beanName)) {
                    Set<String> updatedSingletons = new LinkedHashSet<>(this.manualSingletonNames);
                    updatedSingletons.remove(beanName);
                    this.manualSingletonNames = updatedSingletons;
                }
            }
        }
        else {
            // Still in startup registration phase
            this.beanDefinitionMap.put(beanName, beanDefinition);
            this.beanDefinitionNames.add(beanName);
            this.manualSingletonNames.remove(beanName);
        }
        this.frozenBeanDefinitionNames = null;
    }

    if (existingDefinition != null || containsSingleton(beanName)) {
        resetBeanDefinition(beanName);
    }
}

到目前為止, bean就註冊到工廠裡面去了, 實際上工廠裡面儲存了BeanDefinition的一個對映Map, 這樣有助於Spring做一些驗證, 當獲取bean的時候也可以方便實現懶載入.