Spring之容器的實現

對於經常使用spring框架的同學,對於下面的這段代碼肯定不會陌生

ApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext.xml");

(1)讀取配置文件applicationContext
(2)找到配置文件中定義的配置並實例化

以上是Spring實現容器的基礎,雖然只有短短的一行,但是裏面卻包含了複雜的邏輯。爲了分析裏面包含的邏輯,我們可以使用他的父類BeanFactory來分析。下面代碼是Spring比較原始的實現容器的方法。

BeanFactory bf = new XmlBeanFactory(new ClassPathResource("beanFactory.xml"));

代碼實現的邏輯爲:

1.使用ClassPathResource將beanFactory.xml封裝成Resource
2.完成XmlBeanFactory的初始化

ClassPathResource

在 Java中,將不同來源的資源抽象成URL,通過註冊不同的handler(URLStreamHandler)來處理不同來源間的資源讀取邏輯。而 URL中卻沒有提供一些基本方法來實現自己的抽象結構。因而Spring對其內部資源,使用了自己的抽象結構:Resource接口來封裝。而 ClassPathResource實現類即是對Resource的實現。而Resource實現了InputStreamSource。

public interface InputStreamSource {

    InputStream getInputStream() throws IOException;

}

public interface Resource extends InputStreamSource {


    boolean exists();


    boolean isReadable();


    boolean isOpen();


    URL getURL() throws IOException;


    URI getURI() throws IOException;


    File getFile() throws IOException;


    long contentLength() throws IOException;


    long lastModified() throws IOException;


    Resource createRelative(String relativePath) throws IOException;


    String getFilename();


    String getDescription();
}

不同的Resource實現代表了不同來源的資源,對於來自ClassPath的資源就是用ClassPathResource來實現。

XmlBeanDefinitionReader

將配置文件封裝好以後就需要對其讀取,這個工作由XmlBeanDefinitionReader來完成。XmlBeanDefinitionReader中由loadBeanDefinitions方法實現讀取的功能。loadBeanDefinitions方法的源代碼如下:

public int loadBeanDefinitions(Resource resource) throws BeanDefinitionStoreException {
        //loadBeanDefinitions的具體實現,而EncodedResource主要用於對資源文件的處理
        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());
        }
//通過屬性來記錄已經加載的資源

        。。。。。。。。。。

        // 調用DefaultResourceLoader的getResources方法完成具體的Resource定位 
        try {
                //從EncodedResource中獲取已經封裝的Resource對象並再次從Resource中獲取inputStream 
                InputStream inputStream = encodedResource.getResource().getInputStream();
                try {
                        InputSource inputSource = new InputSource(inputStream);
                        if (encodedResource.getEncoding() != null) {
                                inputSource.setEncoding(encodedResource.getEncoding());
                        }
                         //真正的實現核心
                        return doLoadBeanDefinitions(inputSource, encodedResource.getResource());   
                }
                。。。。。。。。。。。。。
}

對於上述源碼,只需要知道實現邏輯:
(1)對於resource做一次封裝,爲了考慮到resource可能存在的編碼問題,我們使用了new EncodedResource(resource)來實現。
(2)準備獲得InputSource對象。
(3)將封裝好的resource和InputSource傳遞給真正的核心部分:doLoadBeanDefinitions(inputSource, encodedResource.getResource());

doLoadBeanDefinitions

我們看一看doLoadBeanDefinitions(inputSource, encodedResource.getResource())中的實現方法:

protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource)
            throws BeanDefinitionStoreException {

    //這裏略去了冗餘的代碼,值需要注意三個方法
    //獲得驗證模式
    int validationMode = getValidationModeForResource(Resource resource)
    //獲得document
    Document document = loadDocument(InputSource inputSource, EntityResolver entityResolver,
ErrorHandler errorHandler, int validationMode, boolean namespaceAware)
    //註冊Bean信息
    registerBeanDefinitions(Document doc, XmlReaderContext readerContext)            
}

doLoadBeanDefinitions只做了三件事:
1.獲得驗證模式
2.獲得document
3.註冊Bean信息

獲得驗證模式

驗證模式就兩種,DTD和XSD。如果xml文件中有則表示使用DTD驗證模式,否則使用XSD。源代碼如下,比較簡單。

protected int getValidationModeForResource(Resource resource) {
        int validationModeToUse = getValidationMode();
        //如果手動指定了驗證模式則使用指定的驗證模式
        if (validationModeToUse != VALIDATION_AUTO) {
                return validationModeToUse;
        }
        //如果沒有指定,則自動檢測
        int detectedMode = detectValidationMode(resource);    //自動檢測主要是在detectValidationMode(Resource resource)完成的
        if (detectedMode != VALIDATION_AUTO) {
                return detectedMode;
        }
        return VALIDATION_XSD;
}

獲得Document

public Document loadDocument(InputSource inputSource, EntityResolver entityResolver,
                ErrorHandler errorHandler, int validationMode, boolean namespaceAware) throws Exception {

        DocumentBuilderFactory factory = createDocumentBuilderFactory(validationMode, namespaceAware);
        if (logger.isDebugEnabled()) {
                logger.debug("Using JAXP provider [" + factory.getClass().getName() + "]");
        }
        DocumentBuilder builder = createDocumentBuilder(factory, entityResolver, errorHandler);
        return builder.parse(inputSource);
}

邏輯:
1.創建DocumentBuilderFactory
2.通過DocumentBuilderFactory創建DocumentBuilder
3.DocumentBuilder解析inputSource返回一個Document對象。

registerBeanDefinitions

獲得了XML文檔文件的Document以後,就會執行BeanDefinitions的解析和註冊。

public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException {
        // 得到BeanDefinitionDocumentReader來對XML的BeanDefinition進行解析 
        BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader();
        int countBefore = getRegistry().getBeanDefinitionCount();

        // 具體的解析過程在BeanDefinitionDocumentReader的registerBeanDefinitions方法中完成
        documentReader.registerBeanDefinitions(doc, createReaderContext(resource));
        return getRegistry().getBeanDefinitionCount() - countBefore;    
}

registerBeanDefinitions對前一步獲得的Document進行了處理,實現註冊和解析由方法documentReader.registerBeanDefinitions(doc, createReaderContext(resource))實現。

documentReader中的registerBeanDefinitions方法如下:

public void registerBeanDefinitions(Document doc, XmlReaderContext readerContext) {
        this.readerContext = readerContext;
        logger.debug("Loading bean definitions");
        Element root = doc.getDocumentElement();    // 獲得Document的根元素
        //核心邏輯
        doRegisterBeanDefinitions(root);
}

真正的實現XML文件解析和加載的核心部分——doRegisterBeanDefinitions方法

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);
                if (StringUtils.hasText(profileSpec)) {
                        String[] specifiedProfiles = StringUtils.tokenizeToStringArray(
                                profileSpec, BeanDefinitionParserDelegate.MULTI_VALUE_ATTRIBUTE_DELIMITERS);
                        if (!getReaderContext().getEnvironment().acceptsProfiles(specifiedProfiles)) {
                                return;
                        }
                }
        }

        preProcessXml(root);    // 解析Bean定義之前, 增強解析過程的可擴展性 
        parseBeanDefinitions(root, this.delegate);    // 從Document的根元素開始進行Bean定義的Document對象  
        postProcessXml(root);    // 解析Bean定義之前, 增強解析過程的可擴展性  

        this.delegate = parent;
}

核心代碼邏輯如下:
1.對profile屬性的處理。不同的profile屬性可以理解爲:代表了xml中beans的不同namespace,一個namespace代表一套的配置方案。

2.preProcessXml方法。在解析之前,我們可以根據需要實現一些邏輯,需要我們自己實現。

3.parseBeanDefinitions方法,對xml配置文件的讀取和解析。

4.postProcessXml,在解析完成之後,我們可以根據需要實現一些邏輯,需要我們自己實現。

總結

BeanFactory bf = new XmlBeanFactory(new ClassPathResource("beanFactory.xml"));

1.beanFactory.xml→ClassPathResource
2.使用XmlBeanDefinitionReader的loadBeanDefinitions(Resource resource)讀取Resource。

loadBeanDefinitions(resource)的實現邏輯:
2.1.封裝資源,new EncodeResource(resource);
2.2.獲得輸入流,並構造inputsource。
2.3.調用doLoadBeanDefinitions(inputsource,resource)。

doLoadBeanDefinitions(inputsource,resource)的實現:
2.3.1.獲得XML驗證模式:
方法:getValidationModeForResource
2.3.2.加載XML文件,獲得Document類。
方法:loadDocument
2.3.3.根據Document獲得Bean。
方法:registerBeanDefinitions(document,resource);

registerBeanDefinitions(document,resource)中的doRegisterBeanDefinitions方法是真正的核心實現。

doRegisterBeanDefinitions實現邏輯:
1.處理profile
2.preProcessXml
3.parseBeanDefinitions方法,對xml配置文件的讀取和解析
4.postProcessXml

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章