1. 程式人生 > >spring beans原始碼解讀之--XmlBeanFactory

spring beans原始碼解讀之--XmlBeanFactory

導讀:

XmlBeanFactory繼承自DefaultListableBeanFactory,擴充套件了從xml文件中讀取bean definition的能力。從本質上講,XmlBeanFactory等同於DefaultListableBeanFactory+XmlBeanDefinitionReader ,如果有更好的需求,可以考慮使用DefaultListableBeanFactory+XmlBeanDefinitionReader方案,因為該方案可以從多個xml檔案讀取資源,並且在解析xml上具有更靈活的可配置性。

1 XmlBeanFactory的使用

典型構造方法:

    public
XmlBeanFactory(Resource resource, BeanFactory parentBeanFactory) throws BeansException { super(parentBeanFactory); this.reader.loadBeanDefinitions(resource); }

其中,

reader是XmlBeanDefinitionReader的例項,XmlBeanDefinitionReader繼承自AbstractBeanDefinitionReader(http://www.cnblogs.com/davidwang456/p/4190428.html

已經介紹過)。

 Resource 介面封裝了各種可能的資源型別,也就是對使用者來說遮蔽了檔案型別的不同。這樣所有的資源都被可以通過 InputStream 這個類來獲取,所以也遮蔽了資源的提供者。另外還有一個問題就是載入資源的問題,也就是資源的載入者要統一,從上圖中可以看出這個任務是由 ResourceLoader 介面完成,他遮蔽了所有的資源載入者的差異,只需要實現這個介面就可以載入所有的資源,他的預設實現是 DefaultResourceLoader。

 1.1 Resource 介面繼承了 InputStreamSource 介面,這個介面中有個 getInputStream 方法,返回的是 InputStream 類。

public interface InputStreamSource {
    InputStream getInputStream() throws IOException;
}
getInputStream設計希望每次請求都返回一個新的流。這種要求在一個場合非常重要,例如javamail api,它在建立多個郵件附件時需要多次讀取到流,在這中情況下,它就要求每個getInputStream請求能返回一個新的流。

1.2 抽象資源類AbstractResource繼承自Source介面,實現了部分方法。
exists方法檢查一個檔案或者輸入量是否可以開啟;
isOpen 通常返回false;
  getUrl和getFile 丟擲異常;
toString返回描述資訊。

另外有一個有用的方法:lastModified()返回檔案的最後修改時間。呼叫了File.lastModified() 返回此抽象路徑名錶示的檔案最後一次被修改的時間。
    @Override
    public long lastModified() throws IOException {
        long lastModified = getFileForLastModifiedCheck().lastModified();
        if (lastModified == 0L) {
            throw new FileNotFoundException(getDescription() +
                    " cannot be resolved in the file system for resolving its last-modified timestamp");
        }
        return lastModified;
    }

1.3 Resource的實現子類:

AbstractResource的直接子類有:AbstractFileResolvingResource, ByteArrayResource,BeanDefinitionResource, DescriptiveResource, FileSystemResource, InputStreamResource, PathResource, VfsResource。
其中,AbstractFileResolvingResource的直接子類有:ClassPathResource, PortletContextResource, ServletContextResource, UrlResource
比較重要的實現類為實現了ContextResource介面的資源類,ContextResource介面繼承了Resource介面,通過從一個封裝的context中載入資源來擴充套件Resource介面的功能。例如:可以從ServletContext,PortletContext,普通的classpth路徑或者檔案系統的相對路徑(特別是沒有指明字首,因此適用於本地ResourceLoader的context),其子類實現有:PortletContextResource, ServletContextResource,ClassPathContextResource,ClassRelativeContextResource,FileSystemContextResource.

1.4 資源的載入ResourceLoader,ResourceLoader是一個載入資源的策略介面,可以從classpth或者檔案系統中,ApplicationContext需要載入資源功能,使用了其擴充套件類ResourcePatternReolver。DefaultResourceLoader是該介面的標準實現,適用於一個ApplicationContext外部,可以用在ResourceEditor中。在application允許過程中,使用特定的context子原因載入策略,可以將type資源和資源組通過string的形式設定bean的屬性。

(來源:http://www.ibm.com/developerworks/cn/java/j-lo-spring-principle/)

2. Bean的解析與註冊詳細過程分析

    (來源:http://www.ibm.com/developerworks/cn/java/j-lo-spring-principle/)

   首先, XmlBeanFactory建構函式this.reader.loadBeanDefinitions(resource);

  1. XmlBeanDefinitionReader載入bean定義:loadBeanDefinitions方法

    2. XmlBeanDefinitionReader解析bean定義,並註冊:doLoadBeanDefinitions方法

Document doc = doLoadDocument(inputSource, resource);
return registerBeanDefinitions(doc, resource);

  3.1. 使用標準的JAXP配置的xml解析器從Resource中載入到Document。

    /**
     * Load the {@link Document} at the supplied {@link InputSource} using the standard JAXP-configured
     * XML parser.
     */
    @Override
    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);
    }

   3.2.根據載入的Document 註冊Bean definition。

    public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException {
        BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader();
        documentReader.setEnvironment(getEnvironment());
        int countBefore = getRegistry().getBeanDefinitionCount();
        documentReader.registerBeanDefinitions(doc, createReaderContext(resource));
        return getRegistry().getBeanDefinitionCount() - countBefore;
    }

3.2.1 建立BeanDefinitionDocument的解析器BeanUtils.instantiateClass(this.documentReaderClass)

3.2.2 BeanDefinitionDocument解析器註冊bean definition,從根節點<beans>開始

    /**
     * Register each bean definition within the given root {@code <beans/>} element.
     */
    protected void doRegisterBeanDefinitions(Element root) {
        String profileSpec = root.getAttribute(PROFILE_ATTRIBUTE);
        if (StringUtils.hasText(profileSpec)) {
            String[] specifiedProfiles = StringUtils.tokenizeToStringArray(
                    profileSpec, BeanDefinitionParserDelegate.MULTI_VALUE_ATTRIBUTE_DELIMITERS);
            if (!getEnvironment().acceptsProfiles(specifiedProfiles)) {
                return;
            }
        }

        // 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.
        BeanDefinitionParserDelegate parent = this.delegate;
        this.delegate = createDelegate(this.readerContext, root, parent);

        preProcessXml(root);
        parseBeanDefinitions(root, this.delegate);
        postProcessXml(root);

        this.delegate = parent;
    }
/**
     * Parse the elements at the root level in the document:
     * "import", "alias", "bean".
     * @param root the DOM root element of the document
     */
    protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) {
        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);
        }
    }

    private void parseDefaultElement(Element ele, BeanDefinitionParserDelegate delegate) {
        if (delegate.nodeNameEquals(ele, IMPORT_ELEMENT)) {
            importBeanDefinitionResource(ele);
        }
        else if (delegate.nodeNameEquals(ele, ALIAS_ELEMENT)) {
            processAliasRegistration(ele);
        }
        else if (delegate.nodeNameEquals(ele, BEAN_ELEMENT)) {
            processBeanDefinition(ele, delegate);
        }
        else if (delegate.nodeNameEquals(ele, NESTED_BEANS_ELEMENT)) {
            // recurse
            doRegisterBeanDefinitions(ele);
        }
    }

4. documentReader.registerBeanDefinitions(doc, createReaderContext(resource));--->

    public XmlReaderContext createReaderContext(Resource resource) {
        return new XmlReaderContext(resource, this.problemReporter, this.eventListener,
                this.sourceExtractor, this, getNamespaceHandlerResolver());
    }

5.呼叫:

    protected NamespaceHandlerResolver createDefaultNamespaceHandlerResolver() {
        return new DefaultNamespaceHandlerResolver(getResourceLoader().getClassLoader());
    }

6.BeanDefinitionReaderUtils.registerBeanDefinition

    /**
     * Process the given bean element, parsing the bean definition
     * and registering it with the registry.
     */
    protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) {
        BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele);
        if (bdHolder != null) {
            bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder);
            try {
                // Register the final decorated instance.
                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));
        }
    }

7.處理給定的Bean元素,並解析到BeanDefinition。

    /**
     * Process the given bean element, parsing the bean definition
     * and registering it with the registry.
     */
    protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) {
        BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele);
        if (bdHolder != null) {
            bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder);
            try {
                // Register the final decorated instance.
                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));
        }
    }

8.

    public static void registerBeanDefinition(
            BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry)
            throws BeanDefinitionStoreException {

        // Register bean definition under primary name.
        String beanName = definitionHolder.getBeanName();
        registry.registerBeanDefinition(beanName, definitionHolder.getBeanDefinition());

        // Register aliases for bean name, if any.
        String[] aliases = definitionHolder.getAliases();
        if (aliases != null) {
            for (String aliase : aliases) {
                registry.registerAlias(beanName, aliase);
            }
        }
    }

9.將解析的Bean定義放入BeanDefinitionNames和BeanDefinitionMap儲存已經aliasMap等

    @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 {
                ((AbstractBeanDefinition) beanDefinition).validate();
            }
            catch (BeanDefinitionValidationException ex) {
                throw new BeanDefinitionStoreException(beanDefinition.getResourceDescription(), beanName,
                        "Validation of bean definition failed", ex);
            }
        }

        BeanDefinition oldBeanDefinition;

        synchronized (this.beanDefinitionMap) {
            oldBeanDefinition = this.beanDefinitionMap.get(beanName);
            if (oldBeanDefinition != null) {
                if (!this.allowBeanDefinitionOverriding) {
                    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 (this.logger.isInfoEnabled()) {
                        this.logger.info("Overriding bean definition for bean '" + beanName +
                                "': replacing [" + oldBeanDefinition + "] with [" + beanDefinition + "]");
                    }
                }
            }
            else {
                this.beanDefinitionNames.add(beanName);
                this.frozenBeanDefinitionNames = null;
            }
            this.beanDefinitionMap.put(beanName, beanDefinition);
        }

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

3. 一個非常重要的部分就是建立 Bean 物件例項之間的關係,這也是 Spring 框架的核心競爭力,何時、如何建立他們之間的關係請看下面的時序圖:

    來源:http://www.ibm.com/developerworks/cn/java/j-lo-spring-principle/

4. 小結:

  從spring 3.1版本後,xmlBeanFactory被表明為Deprecated. 推薦使用DefaultListableBeanFactory和XmlBeanDefinitionReader替換。

本文從xmlBeanFactory擴充套件到XmlBeanDefinitionReader,再到Resource介面。

一些情節還沒有理順,顯得有些雜亂,後續會修改(寫的很累,休整一下)。寫這篇部落格的時候,這句話一直在我腦邊回想:

"寫書就像冒險。一開始它是玩具和娛樂。然後她成了你的情婦。然後是你的主人。然後變成一個暴君。最後你終於認命的時候,它死了,然後給拖到外面遊街。" ----溫斯頓.丘吉爾

參考文獻:

1. http://www.ibm.com/developerworks/cn/java/j-lo-spring-principle/

相關推薦

spring beans原始碼解讀--XmlBeanFactory

導讀: XmlBeanFactory繼承自DefaultListableBeanFactory,擴充套件了從xml文件中讀取bean definition的能力。從本質上講,XmlBeanFactory等同於DefaultListableBeanFactory+XmlBeanDefinitionReader

spring beans原始碼解讀 ioc容器始祖--DefaultListableBeanFactory

spring Ioc容器的實現,從根源上是beanfactory,但真正可以作為一個可以獨立使用的ioc容器還是DefaultListableBeanFactory,因此可以這麼說, DefaultListableBeanFactory 是整個spring ioc的始祖,研究透它

spring beans原始碼解讀--bean definiton解析器

spring提供了有兩種方式的bean definition解析器:PropertiesBeanDefinitionReader和XmLBeanDefinitionReader即屬性檔案格式的bean definition解析器和xml檔案格式的bean definition解析器。 我們先從簡單的Prop

spring beans原始碼解讀--Bean的註解(annotation)

隨著spring註解的引入,越來越多的開發者開始使用註解,這篇文章將對註解的機制進行串聯式的講解,不求深入透徹,但求串起spring beans註解的珍珠,展示給大家。 1. spring beans常用的註解:   public @interface Autowired:可以對成員變數、方法和建構函式進

spring beans原始碼解讀--總結篇

spring beans下面有如下原始檔包: org.springframework.beans, 包含了操作java bean的介面和類。org.springframework.beans.annotation, 支援包,提供對java 5註解處理bean樣式的支援。org.springframework

spring beans原始碼解讀--Bean的定義及包裝

  bean的定義,包裝是java bean的基礎。再怎麼強調它的重要性都不為過,因此深入 瞭解這塊的程式碼對以後的程式碼研究可以起到事半功倍的功效。 1. Bean的定義BeanDefinition 1.1 BeanDefinition 作用 一個BeanDefinition描述了一個bean的例項,

Spring原始碼解讀核心容器上節

Spring架構圖 說明 Spring的流行程度就不用我來說了,相信大家如果使用JAVA開發就一定知道它。寫這篇文章的初衷在於:1.瞭解Spring底層實現原理,提升對Spring的認識與理解。2.學習優秀框架程式設計實現,學習優秀的設計模式。3.使用Spring三年多,對於底層細節希望知道更多,便於

Spring原始碼解讀核心容器下節

續 上一篇我們通過ClassPathXmlApplicationContext載入xml檔案,通過BeanFactory獲取例項bean的demo程式碼去解讀了Spring Core Container中的spring-beans,spring-core,spring-context三個元件之間的一些具體類

Spring原始碼解讀Spring MVC HandlerMapping元件(二)

一、HandlerMapping HandlerMapping作用是根據request找到相應的處理器Handler和Interceptors,並將Handler和Interceptors封裝成HandlerExecutionChain 物件返回。Handler

Spring原始碼解讀——bean的生命週期(隨筆)

bean建立---初始化----銷燬的過程 容器管理bean的生命週期; 我們可以自定義初始化和銷燬方法;容器在bean進行到當前生命週期的時候來呼叫我們自定義的初始化和銷燬方法 構造(物件建立)     單例項:在容器啟動的時候建立物件     多例項:在每次獲取的時

Spring原始碼解讀——元件註冊(隨筆)

@ComponentScan  value:指定要掃描的包 excludeFilters = Filter[] :指定掃描的時候按照什麼規則排除那些元件 includeFilters = Filt

spring原始碼解讀 JdbcTemplate原始碼

    在Spring中,JdbcTemplate是經常被使用的類來幫助使用者程式操作資料庫,在JdbcTemplate為使用者程式提供了許多便利的資料庫操作方法,比如查詢,更新等,而且在Spring中,有許多類似 JdbcTemplate的模板,比如HibernateT

Spring原始碼解讀——自動裝配(隨筆)

Spring利用依賴注入(DI),完成對IOC容器中中各個元件的依賴關係賦值; 1、@Autowired:自動注入:     1)、預設優先按照型別去容器中找對應的元件:applicationContext.getBean(BookDao.class);找到就賦值   

Spring原始碼解讀BeanFactoryPostProcessor的處理

前言     前段時間旁聽了某課堂兩節Spring原始碼解析課,剛好最近自己又在重新學習中,便在這裡記錄一下學習所得。我之前寫過一篇博文,是介紹BeanFactoryPostProcessor跟BeanPostProcessor是如何發揮作用的,當時覺得講的還行,但是現在看來,太粗劣了,

Spring Cloud 原始碼解讀 【這也太神奇了,RestTemplate加上一個@LoadBalanced註解就能實現負載均衡!】

前提概要: 前天,有個前端大佬問了我兩個問題:為啥不引入Ribbon依賴就能使用Ribbon?為啥RestTemplate加上@LoadBalanced註解就能負載均衡了?我也表示很疑惑,而我自己其實也真的沒去了解過,所以趁著工作不太忙,趕緊去研究一波。 第一個問題比較簡單,一般都是其他依賴引入了Ribbon

Spring Cloud 原始碼解讀 【如何配置好OpenFeign的各種超時時間!】

關於Feign的超時詳解: 在Spring Cloud微服務架構中,大部分公司都是利用Open Feign進行服務間的呼叫,而比較簡單的業務使用預設配置是不會有多大問題的,但是如果是業務比較複雜,服務要進行比較繁雜的業務計算,那後臺很有可能會出現Read Timeout這個異常。 1、關於hystrix的熔斷

【1】pytorch torchvision原始碼解讀Alexnet

最近開始學習一個新的深度學習框架PyTorch。 框架中有一個非常重要且好用的包:torchvision,顧名思義這個包主要是關於計算機視覺cv的。這個包主要由3個子包組成,分別是:torchvision.datasets、torchvision.models、torchvision.trans

Spring-web原始碼解析Filter-OncePerRequestFilter

轉自:  http://blog.csdn.net/ktlifeng/article/details/50630934 基於4.1.7.RELEASE 我們先看一個filter-mapping的配置 

java原始碼解讀HashMap

1:首先下載openjdk(http://pan.baidu.com/s/1dFMZXg1),把原始碼匯入eclipse,以便看到jdk原始碼            Windows-Prefe

PyTorch原始碼解讀torch.utils.data.DataLoader(轉)

原文連結 https://blog.csdn.net/u014380165/article/details/79058479 寫得特別好!最近正好在學習pytorch,學習一下! PyTorch中資料讀取的一個重要介面是torch.utils.data.DataLoade