<rt id="bn8ez"></rt>
<label id="bn8ez"></label>

  • <span id="bn8ez"></span>

    <label id="bn8ez"><meter id="bn8ez"></meter></label>

    美麗涵涵童裝店--說我博客名字,給你們打折!
    隨筆 - 82  文章 - 266  trackbacks - 0
    <2016年6月>
    2930311234
    567891011
    12131415161718
    19202122232425
    262728293012
    3456789


    點擊這里給楊愛友發消息
    美麗涵涵童裝店
    說我博客名字,給你們打折!

    常用鏈接

    留言簿(6)

    隨筆分類

    隨筆檔案

    文章檔案

    好友的BLOG

    搜索

    •  

    最新評論

    閱讀排行榜

    評論排行榜

    1. 場景

    一個applicationContext.xml配置文件,這個不可少
    一個bean,這里我沒用接口,直接用一個普通的類做為Spring的bean
    一個Junit測試類

    applicationContext.xml
    <?xml version="1.0" encoding="UTF-8"?>
    <!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN 2.0//EN" "http://www.springframework.org/dtd/spring-beans-2.0.dtd">
    <beans>
        
    <bean id="studentBean" class="my.StudentBean"></bean>
    </beans>

    StudentBean
    public class StudentBean{
        
    public void getName(String name) {
            System.out.println(
    "你的名字是:" + name);
        }
    }

    單元測試類
     1 public class MyTest {
     2 
     3     public static void main(String[] args) {
     4         ClassPathResource res = new ClassPathResource("my/applicationContext.xml");
     5         
     6         XmlBeanFactory bf = new XmlBeanFactory(res);
     7         
     8         StudentBean bean = (StudentBean)bf.getBean("studentBean");
     9         
    10         bean.getName("yangay");
    11     }
    12 }
    運行單元測試,打印出“你的名字是:yangay”,測試類只有四行代碼,但Spring到底為我們做了些什么?下面我們就基于這樣的場景去分析bean的加載過程。

    2. 初步分析

    (1) 獲取配置文件
    ClassPathResource res = new ClassPathResource("my/applicationContext.xml");
        這一句只是讀入配置文件,并封裝成Spring提供的Resource對象,供后面邏輯使用。
        在Spring內部,有超過十個以Resource結尾的類或文件,他們處理不同類型的資源文件,如FileSystemResource、ClassPathResource、UrlResource等,處理過程大同小異,內部細節可以不必關心,跟其他組件邏輯幾乎沒關系。
    (2) 解析配置文件并注冊bean
     XmlBeanFactory bf = new XmlBeanFactory(res);
        這里面的邏輯相當復雜,涉及到眾多Factory、Reader、Loader、BeanDefinition、Perser、Registry系列接口和類,但他們做的基本事情就是將applicationContext.xml配置的Bean信息構成BeanDefinition對象,然后放到Factory的map中(這一步就是所謂的注冊),這樣以后程序就可以直接從Factory中拿Bean信息了。
        要跟蹤這個處理過程,大致流程如下:
        a. 構造XmlBeanFactory時,會調用Reader對象的loadBeanDefinitions方法去加載bean定義信息
        b. 在Reader對象的doLoadBeanDefinitions驗證文檔(配置文件)模式,然后通過documentLoader對象處理資源對象,生成我們Document對象;
        c. 調用BeanDefinitionDocumentReader對象的doRegisterBeanDefinitions去注冊bean定義信息;
        d. parseBeanDefinitions從xml文檔根節點遞歸循環處理各個節點,對bean節點真正的處理工作委托給了BeanDefinitionParserDelegate,方法parseBeanDefinitionElement將一個bean節點轉換成一個BeanDefinitionHolder對象,這才是最終的解析過程;
        e. DefaultListableBeanFactory.registerBeanDefinition利用解析好的beanDefinition對象完成最終的注冊,其實就是把beanName和beanDefinition作為鍵值對放到beanFactory對象的map;
    (3) 實例化Bean
    StudentBean bean = (StudentBean)bf.getBean("studentBean");
        這一步Spring同樣做了復雜的處理,但基本原理就是利用反射機制,通過bean的class屬性創建一個bean的實例,例子中是創建了一個StudentBean對象。
    (4) 調用對象的方法,沒什么好說的。當然如果方法上做了事務、AOP之類的聲明,這一步的處理就不會那么簡單了。

    3. 解析配置文件并注冊bin對象

    我們分析bean的注冊過程,就是下面這行代碼,他完成了配置文件的解析和bin的注冊功能,我們看看Spring到底為我們做了多少事情。
    XmlBeanFactory bf = new XmlBeanFactory(res);

    public class XmlBeanFactory extends DefaultListableBeanFactory {
        
    //這里為容器定義了一個默認使用的bean定義讀取器
        private final XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(this);
        
    public XmlBeanFactory(Resource resource) throws BeansException {
            
    this(resource, null);
        }
        
    //在初始化函數中使用讀取器來對資源進行讀取,得到bean定義信息。
        public XmlBeanFactory(Resource resource, BeanFactory parentBeanFactory) throws BeansException {
            
    super(parentBeanFactory);
            
    this.reader.loadBeanDefinitions(resource);
        }
    我們跟進去,發現他實際調用了XmlBeanDefinitionReader對象的loadBeanDefinitions方法。
    3.1 XmlBeanDefinitionReader.loadBeanDefinitions
    public int loadBeanDefinitions(Resource resource) throws BeanDefinitionStoreException {
            
    //封裝資源文件
            return loadBeanDefinitions(new EncodedResource(resource));
        }
    InputStream inputStream 
    = encodedResource.getResource().getInputStream();
                
    try {
                    InputSource inputSource 
    = new InputSource(inputStream);
                    
    if (encodedResource.getEncoding() != null) {
                        inputSource.setEncoding(encodedResource.getEncoding());
                    }
                    
    return doLoadBeanDefinitions(inputSource, encodedResource.getResource());
                }
    這個方法是整個資源加載的切入點,我們先大致看看這個方法的處理流程:
    a. 封裝資源文件
        new EncodedResource(resource)
    b. 獲取輸入流
        從EncodedResource對象中獲取InputStream并構造InputSource對象
    c. 然后調用doLoadBeanDefinitions方法完成具體的加載過程
    3.2 doLoadBeanDefinitions方法
    int validationMode = getValidationModeForResource(resource);
    Document doc 
    = this.documentLoader.loadDocument(inputSource, getEntityResolver(), this.errorHandler, validationMode, isNamespaceAware());
    return registerBeanDefinitions(doc, resource);
    這個方法的代碼很長,如果不考慮異常處理,其實只做了三件事情:
    a. 獲取對XML文件的驗證模式
    b. 加載XML文件,并得到對應的Document對象
    c. 根據返回的Document對象注冊bean信息

    這里對驗證模式不進行討論;
    這里不對Document對象的加載過程進行討論;
    這里直接進入bean的注冊方法registerBeanDefinitions
    3.3 registerBeanDefinitions方法
        public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException {
            
    // 這里定義解析器,使用XmlBeanDefinitionParser來解析xml方式的bean定義文件 - 現在的版本不用這個解析器了,使用的是XmlBeanDefinitionReader
            if (this.parserClass != null) {
                XmlBeanDefinitionParser parser 
    =
                        (XmlBeanDefinitionParser) BeanUtils.instantiateClass(
    this.parserClass);
                
    return parser.registerBeanDefinitions(this, doc, resource);
            }
            
    // 具體的注冊過程,首先得到XmlBeanDefinitionReader,來處理xml的bean定義文件
            BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader();
            
    int countBefore = getBeanFactory().getBeanDefinitionCount();
            documentReader.registerBeanDefinitions(doc, createReaderContext(resource));
            
    return getBeanFactory().getBeanDefinitionCount() - countBefore;
        }
    當把文檔轉換為Document對象后,提取及注冊bean就是我們的重頭戲了。
    這里并沒有看到我們想要的代碼,而是把工作委托給了BeanDefinitionDocumentReader對象去處理
    3.4 BeanDefinitionDocumentReader.doRegisterBeanDefinitions方法
        public void registerBeanDefinitions(Document doc, XmlReaderContext readerContext) {
            
    this.readerContext = readerContext;
            Element root 
    = doc.getDocumentElement();

            BeanDefinitionParserDelegate delegate 
    = createHelper(readerContext, root);
            preProcessXml(root);
            parseBeanDefinitions(root, delegate);
            postProcessXml(root);
        }
        
    protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) {
            
    if (delegate.isDefaultNamespace(root.getNamespaceURI())) {
                
    //這里得到xml文件的子節點,比如各個bean節點         
                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;
                        String namespaceUri 
    = ele.getNamespaceURI();
                        
    if (delegate.isDefaultNamespace(namespaceUri)) {
                            
    //這里是解析過程的調用,對缺省的元素進行分析比如bean元素
                            parseDefaultElement(ele, delegate);
                        }
    else {
                            delegate.parseCustomElement(ele);
                        }
                    }
                }
            } 
    else {
                delegate.parseCustomElement(root);
            }
        }
    經過艱難險阻,山路十八彎,我們終于走到了核心邏輯的底部doRegisterBeanDefinitions,如果說以前一直是XML加載解析的準備階段,
    那么這個方法算是真正地開始進行解析了,我們期待的核心部分真正開始了。
    這個方法的代碼我們比較熟悉,讀取Document對象,循環每一個bean節點,然后進行處理。
    Spring有兩類Bean,一個是默認的,一個是自定義的bean,這個方法對他們分別調用了不同方法進行處理。
    3.5 對默認標簽的處理 processBeanDefinition方法
        protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) {
            BeanDefinitionHolder bdHolder 
    = delegate.parseBeanDefinitionElement(ele);
            
    if (bdHolder != null) {
                bdHolder 
    = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder);
                BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry());
                getReaderContext().fireComponentRegistered(
    new BeanComponentDefinition(bdHolder));
            }
        }
    a. 首先利用委托類的parseBeanDefinitionElement方法進行元素解析,返回BeanDefinitionHolder對象bdHolder,經過這個方法,bdHolder實例已經包含我們配置文件中對bean的所有配置信息了,如name、class等。
    b. 對bdHolder進行裝飾
    c. 解析完成后,要對bdHolder進行注冊,同樣,注冊過程委托給了BeanDefinitionReaderUtils去處理
    3.6 delegate.parseBeanDefinitionElement(ele)
    這個方法便是對默認標簽解析的全過程了,他將一個element節點轉換成BeanDefinitionsHolder對象,其中ele和bdHolder中的屬性是對應的。不論是常用的還是不常用的我們都看到了,盡管有些復雜屬性還需要進一步解析,但絲毫不會影響我們興奮的心情。
    a. 提取元素的id和name屬性
    b. 進一步解析其他所有屬性并統一封裝到BeanDefinition類型的實例
    c. 將獲取到的信息封裝到BeanDefinitionHolder實例中待續。。。
    String id = ele.getAttribute(ID_ATTRIBUTE);
    String nameAttr 
    = ele.getAttribute(NAME_ATTRIBUTE);

    AbstractBeanDefinition bd 
    = createBeanDefinition(className, parent);

    parseBeanDefinitionAttributes(ele, beanName, containingBean, bd);
    bd.setDescription(DomUtils.getChildElementValueByTagName(ele, DESCRIPTION_ELEMENT));
                
    parseMetaElements(ele, bd);
    parseLookupOverrideSubElements(ele, bd.getMethodOverrides());
    parseReplacedMethodSubElements(ele, bd.getMethodOverrides());
    parseConstructorArgElements(ele, bd);
    parsePropertyElements(ele, bd);
    parseQualifierElements(ele, bd);

    return new BeanDefinitionHolder(bd, beanName, aliasesArray);
    跟進去,我們就看到解析的最底層了,如parseMetaElements,這里不再進行往下分析。

    對于配置文件,解析也解析完了,裝飾也裝飾完了,已經把xml中bean元素的各屬性封裝到了BeanDefinition對象,已經可以滿足后續的使用要求了,剩下的工作便是注冊解析的BeanDefinition。
    3.7 BeanDefinitionReaderUtils.registerBeanDefinition
    這個方法并沒有做太多事情,而是直接調用了BeanDefinitionRegistry的注冊方法。BeanDefinitionRegistry是一個接口,有多個實現類,這里我們使用了默認的實現DefaultListableBeanFactory。
    3.8 DefaultListableBeanFactory.registerBeanDefinition
    代碼啰嗦了一大堆,實際上所謂的注冊,就是把beanName和beanDefinition對象作為鍵值對放到BeanFactory對象的beanDefinitionMap。
    但Spring經常把簡單的邏輯寫的非常“啰嗦”,仔細分析代碼,發現他完成了幾個事情:
    a. 對bean對象的校驗
    b. 檢查beanFactory中是否已經有同名的bean,如果有,進行相應處理
    c. 把bean對象放到beanDefinitionMap中(這就是最終所謂的注冊)
    d. 清除整個過程緩存的對象數據

    以上便是Spring對bean解析注冊的全過程,總結一下大致步驟:
    1. 加載XML文件,封裝成Resource對象
    2. 調用Reader對象方法讀取XML文件內容,并將相關屬性放到BeanDefinition實例
    3. 將BeanDefinition對象放到BeanFactory對象

    4. 實例化bean

     

    詳細過程,預留位置

     

    posted on 2014-08-29 10:47 楊愛友 閱讀(10861) 評論(3)  編輯  收藏

    FeedBack:
    # re: Spring源碼學習-bean加載 2014-09-25 11:36 李金龍
    這個不錯  回復  更多評論
      
    # re: Spring源碼學習-bean加載 2014-11-14 11:17 duicky
    不錯,調試看下,基本按上述流程  回復  更多評論
      
    # re: Spring源碼學習-bean加載 2016-06-29 11:09 飛天奔月
    詳細過程,預留位置?

    2年了 哦   回復  更多評論
      

    只有注冊用戶登錄后才能發表評論。


    網站導航:
     
    美麗涵涵童裝店
    親,說我博客名字,給你們打折!
    主站蜘蛛池模板: 亚洲国产av玩弄放荡人妇 | 精品国产日韩亚洲一区91| 67pao强力打造高清免费| 水蜜桃亚洲一二三四在线| 免费国产99久久久香蕉| 亚洲久悠悠色悠在线播放| AA免费观看的1000部电影| 2020国产精品亚洲综合网| 一个人晚上在线观看的免费视频 | 亚洲黄色在线观看视频| 2020因为爱你带字幕免费观看全集| 精品亚洲aⅴ在线观看| 美女免费精品高清毛片在线视| 免费人成在线观看网站品爱网日本| 日韩在线视频线视频免费网站| 亚洲真人日本在线| 色偷偷亚洲第一综合网| 亚洲另类少妇17p| 日本免费中文视频| 亚洲三级视频在线观看| 免费v片在线观看品善网| h片在线观看免费| 亚洲成人福利在线| 日韩免费视频一区| 成人性生交大片免费看中文| 亚洲综合激情另类小说区| 成全视频在线观看免费高清动漫视频下载| 自拍偷自拍亚洲精品偷一| 久久久久亚洲AV成人网人人软件 | 亚洲GV天堂GV无码男同| 国产亚洲精品久久久久秋霞 | a级毛片免费网站| 亚洲欧洲精品久久| 亚洲AV无码成人精品区大在线| 永久免费av无码网站yy| 最新国产精品亚洲| 国产亚洲av片在线观看播放| 两性刺激生活片免费视频| 亚洲一区二区三区91| 亚洲人AV永久一区二区三区久久| 1000部啪啪未满十八勿入免费|