国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁 > 學(xué)院 > 開發(fā)設(shè)計 > 正文

Spring源碼學(xué)習(xí)-bean加載

2019-11-14 21:08:19
字體:
供稿:網(wǎng)友
SPRing源碼學(xué)習(xí)-bean加載1. 場景

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

applicationContext.xml<?xmlversion="1.0"encoding="UTF-8"?><!DOCTYPEbeansPUBLIC"-//SPRING//DTDBEAN2.0//EN""http://www.springframework.org/dtd/spring-beans-2.0.dtd"><beans><beanid="studentBean"class="my.StudentBean"></bean></beans>StudentBeanpublicclassStudentBean{publicvoidgetName(Stringname){System.out.println("你的名字是:"+name);}}

單元測試類

1publicclassMyTest{23publicstaticvoidmain(String[]args){4ClassPathResourceres=newClassPathResource("my/applicationContext.xml");56XmlBeanFactorybf=newXmlBeanFactory(res);78StudentBeanbean=(StudentBean)bf.getBean("studentBean");910bean.getName("yangay");11}12}

運行單元測試,打印出“你的名字是:yangay”,測試類只有四行代碼,但Spring到底為我們做了些什么?下面我們就基于這樣的場景去分析bean的加載過程。

2. 初步分析

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

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

我們分析bean的注冊過程,就是下面這行代碼,他完成了配置文件的解析和bin的注冊功能,我們看看Spring到底為我們做了多少事情。XmlBeanFactorybf=newXmlBeanFactory(res);publicclassXmlBeanFactoryextendsDefaultListableBeanFactory{//這里為容器定義了一個默認使用的bean定義讀取器privatefinalXmlBeanDefinitionReaderreader=newXmlBeanDefinitionReader(this);publicXmlBeanFactory(Resourceresource)throwsBeansException{this(resource,null);}//在初始化函數(shù)中使用讀取器來對資源進行讀取,得到bean定義信息。publicXmlBeanFactory(Resourceresource,BeanFactoryparentBeanFactory)throwsBeansException{super(parentBeanFactory);this.reader.loadBeanDefinitions(resource);}我們跟進去,發(fā)現(xiàn)他實際調(diào)用了XmlBeanDefinitionReader對象的loadBeanDefinitions方法。3.1 XmlBeanDefinitionReader.loadBeanDefinitionspublicintloadBeanDefinitions(Resourceresource)throwsBeanDefinitionStoreException{//封裝資源文件returnloadBeanDefinitions(newEncodedResource(resource));}InputStreaminputStream=encodedResource.getResource().getInputStream();try{InputSourceinputSource=newInputSource(inputStream);if(encodedResource.getEncoding()!=null){inputSource.setEncoding(encodedResource.getEncoding());}returndoLoadBeanDefinitions(inputSource,encodedResource.getResource());}這個方法是整個資源加載的切入點,我們先大致看看這個方法的處理流程:a. 封裝資源文件 new EncodedResource(resource)b. 獲取輸入流 從EncodedResource對象中獲取InputStream并構(gòu)造InputSource對象c. 然后調(diào)用doLoadBeanDefinitions方法完成具體的加載過程3.2 doLoadBeanDefinitions方法intvalidationMode=getValidationModeForResource(resource);Documentdoc=this.documentLoader.loadDocument(inputSource,getEntityResolver(),this.errorHandler,validationMode,isNamespaceAware());returnregisterBeanDefinitions(doc,resource);這個方法的代碼很長,如果不考慮異常處理,其實只做了三件事情:a. 獲取對XML文件的驗證模式b. 加載XML文件,并得到對應(yīng)的Document對象c. 根據(jù)返回的Document對象注冊bean信息這里對驗證模式不進行討論;這里不對Document對象的加載過程進行討論;這里直接進入bean的注冊方法registerBeanDefinitions3.3 registerBeanDefinitions方法publicintregisterBeanDefinitions(Documentdoc,Resourceresource)throwsBeanDefinitionStoreException{//這里定義解析器,使用XmlBeanDefinitionParser來解析xml方式的bean定義文件-現(xiàn)在的版本不用這個解析器了,使用的是XmlBeanDefinitionReaderif(this.parserClass!=null){XmlBeanDefinitionParserparser=(XmlBeanDefinitionParser)BeanUtils.instantiateClass(this.parserClass);returnparser.registerBeanDefinitions(this,doc,resource);}//具體的注冊過程,首先得到XmlBeanDefinitionReader,來處理xml的bean定義文件BeanDefinitionDocumentReaderdocumentReader=createBeanDefinitionDocumentReader();intcountBefore=getBeanFactory().getBeanDefinitionCount();documentReader.registerBeanDefinitions(doc,createReaderContext(resource));returngetBeanFactory().getBeanDefinitionCount()-countBefore;}當把文檔轉(zhuǎn)換為Document對象后,提取及注冊bean就是我們的重頭戲了。這里并沒有看到我們想要的代碼,而是把工作委托給了BeanDefinitionDocumentReader對象去處理3.4 BeanDefinitionDocumentReader.doRegisterBeanDefinitions方法publicvoidregisterBeanDefinitions(Documentdoc,XmlReaderContextreaderContext){this.readerContext=readerContext;Elementroot=doc.getDocumentElement();BeanDefinitionParserDelegatedelegate=createHelper(readerContext,root);preProcessXml(root);parseBeanDefinitions(root,delegate);postProcessXml(root);}protectedvoidparseBeanDefinitions(Elementroot,BeanDefinitionParserDelegatedelegate){if(delegate.isDefaultNamespace(root.getNamespaceURI())){//這里得到xml文件的子節(jié)點,比如各個bean節(jié)點NodeListnl=root.getChildNodes();//這里對每個節(jié)點進行分析處理for(inti=0;i<nl.getLength();i++){Nodenode=nl.item(i);if(nodeinstanceofElement){Elementele=(Element)node;StringnamespaceUri=ele.getNamespaceURI();if(delegate.isDefaultNamespace(namespaceUri)){//這里是解析過程的調(diào)用,對缺省的元素進行分析比如bean元素parseDefaultElement(ele,delegate);}else{delegate.parseCustomElement(ele);}}}}else{delegate.parseCustomElement(root);}}經(jīng)過艱難險阻,山路十八彎,我們終于走到了核心邏輯的底部doRegisterBeanDefinitions,如果說以前一直是XML加載解析的準備階段,那么這個方法算是真正地開始進行解析了,我們期待的核心部分真正開始了。這個方法的代碼我們比較熟悉,讀取Document對象,循環(huán)每一個bean節(jié)點,然后進行處理。Spring有兩類Bean,一個是默認的,一個是自定義的bean,這個方法對他們分別調(diào)用了不同方法進行處理。3.5 對默認標簽的處理 processBeanDefinition方法protectedvoidprocessBeanDefinition(Elementele,BeanDefinitionParserDelegatedelegate){BeanDefinitionHolderbdHolder=delegate.parseBeanDefinitionElement(ele);if(bdHolder!=null){bdHolder=delegate.decorateBeanDefinitionIfRequired(ele,bdHolder); BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder,getReaderContext().getRegistry());getReaderContext().fireComponentRegistered(newBeanComponentDefinition(bdHolder));}}a. 首先利用委托類的parseBeanDefinitionElement方法進行元素解析,返回BeanDefinitionHolder對象bdHolder,經(jīng)過這個方法,bdHolder實例已經(jīng)包含我們配置文件中對bean的所有配置信息了,如name、class等。b. 對bdHolder進行裝飾c. 解析完成后,要對bdHolder進行注冊,同樣,注冊過程委托給了BeanDefinitionReaderUtils去處理3.6 delegate.parseBeanDefinitionElement(ele)這個方法便是對默認標簽解析的全過程了,他將一個element節(jié)點轉(zhuǎn)換成BeanDefinitionsHolder對象,其中ele和bdHolder中的屬性是對應(yīng)的。不論是常用的還是不常用的我們都看到了,盡管有些復(fù)雜屬性還需要進一步解析,但絲毫不會影響我們興奮的心情。a. 提取元素的id和name屬性b. 進一步解析其他所有屬性并統(tǒng)一封裝到BeanDefinition類型的實例c. 將獲取到的信息封裝到BeanDefinitionHolder實例中待續(xù)。。。Stringid=ele.getAttribute(ID_ATTRIBUTE);StringnameAttr=ele.getAttribute(NAME_ATTRIBUTE);AbstractBeanDefinitionbd=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);returnnewBeanDefinitionHolder(bd,beanName,aliasesArray);跟進去,我們就看到解析的最底層了,如parseMetaElements,這里不再進行往下分析。對于配置文件,解析也解析完了,裝飾也裝飾完了,已經(jīng)把xml中bean元素的各屬性封裝到了BeanDefinition對象,已經(jīng)可以滿足后續(xù)的使用要求了,剩下的工作便是注冊解析的BeanDefinition。3.7 BeanDefinitionReaderUtils.registerBeanDefinition這個方法并沒有做太多事情,而是直接調(diào)用了BeanDefinitionRegistry的注冊方法。BeanDefinitionRegistry是一個接口,有多個實現(xiàn)類,這里我們使用了默認的實現(xiàn)DefaultListableBeanFactory。3.8 DefaultListableBeanFactory.registerBeanDefinition代碼啰嗦了一大堆,實際上所謂的注冊,就是把beanName和beanDefinition對象作為鍵值對放到BeanFactory對象的beanDefinitionMap。但Spring經(jīng)常把簡單的邏輯寫的非常“啰嗦”,仔細分析代碼,發(fā)現(xiàn)他完成了幾個事情:a. 對bean對象的校驗b. 檢查beanFactory中是否已經(jīng)有同名的bean,如果有,進行相應(yīng)處理c. 把bean對象放到beanDefinitionMap中(這就是最終所謂的注冊)d. 清除整個過程緩存的對象數(shù)據(jù)以上便是Spring對bean解析注冊的全過程,總結(jié)一下大致步驟:1. 加載XML文件,封裝成Resource對象2. 調(diào)用Reader對象方法讀取XML文件內(nèi)容,并將相關(guān)屬性放到BeanDefinition實例3. 將BeanDefinition對象放到BeanFactory對象

4. 實例化bean

詳細過程,預(yù)留位置
發(fā)表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發(fā)表
主站蜘蛛池模板: 古丈县| 梅河口市| 宁城县| 庐江县| 上犹县| 三门峡市| 江永县| 邹城市| 潼关县| 仙居县| 香港 | 南皮县| 开鲁县| 门头沟区| 永济市| 香河县| 房山区| 安丘市| 天柱县| 平潭县| 翼城县| 腾冲县| 海林市| 长寿区| 内江市| 沙田区| 梅州市| 彭阳县| 堆龙德庆县| 红原县| 嫩江县| 天峨县| 福鼎市| 阳谷县| 瑞丽市| 濮阳县| 山东省| 夹江县| 安福县| 巧家县| 黔西县|