Spring源代碼解析-入門_第1頁
Spring源代碼解析-入門_第2頁
Spring源代碼解析-入門_第3頁
Spring源代碼解析-入門_第4頁
Spring源代碼解析-入門_第5頁
已閱讀5頁,還剩55頁未讀, 繼續(xù)免費(fèi)閱讀

下載本文檔

版權(quán)說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請(qǐng)進(jìn)行舉報(bào)或認(rèn)領(lǐng)

文檔簡(jiǎn)介

在認(rèn)真學(xué)習(xí)Rod.Johnson的三部曲之一:?ProfessionalJavaDevelopmentwiththespringframework?,

順便也看了看源代碼想知道個(gè)究竟,拋磚引玉,有興趣的同志?起討論研究吧!

在Spring中,IOC容器的重要地位我們就不多說了,對(duì)于Spring的使用者而言,10c容器實(shí)際上是什么

呢?我們可以說BeanFactory就是我們看到的loC容器,當(dāng)然了Spring為我們準(zhǔn)備了許多種loC容器來

使用,這樣可以方便我們從不同的層面,不同的資源位置,不同的形式的定義信息來建立我們需要的loC

容器。

在Spring中,最基本的I0C容器接口是BeanFactory-這個(gè)接口為具體的I0C容器的實(shí)現(xiàn)作了最基本的

功能規(guī)定-不管怎么著,作為I0C容器,這些接口你必須要滿足應(yīng)用程序的最基本要求:

Java代碼

1.publicinterfaceBeanFactory{

2.

3.〃這里是對(duì)FactoryBean的轉(zhuǎn)義定義,因?yàn)槿绻褂胋ean的名字檢索FactoryBean得到的對(duì)

象是工廠生成的對(duì)象,

4.〃如果需要得到工廠本身,需要轉(zhuǎn)義

5.StringFACTORY_BEAN_PREFIX=

6.

7.

8.〃這里根據(jù)bean的名字,在I0C容器中得到bean實(shí)例,這個(gè)IOC容器就是一個(gè)大的抽象I.

廠。

9.ObjectgetBean(Stringname)throwsBeansException;

10.

11.〃這里根據(jù)bean的名字和Class類型來得到bean實(shí)例,和上面的方法不同在于它會(huì)拋出異常:

如果根據(jù)名字取得的bean實(shí)例的Class類型和需要的不同的話。

12.ObjectgetBean(Stringname,ClassrequiredType)throwsBeansException;

13.

14.〃這里提供對(duì)bean的檢索,看看是否在IOC容器有這個(gè)名字的bean

15.booleancontainsBean(Stringname);

16.

17.〃這里根據(jù)bean名字得到bean實(shí)例,并同時(shí)判斷這個(gè)bean是不是單件

18.booleanisSingleton(Stringname)throwsNoSuchBeanDefinitionException;

19.

20.〃這里對(duì)得到bean實(shí)例的Class類型

21.ClassgetType(Stringname)throwsNoSuchBeanDefinitionException;

22.

23.〃這里得到bean的別名,如果根據(jù)別名檢索,那么其原名也會(huì)被檢索出來

24.String[]getAliases(Stringname);

25.

26.}

在BeanFactory里只對(duì)IOC容器的基本行為作了定義,根本不關(guān)心你的bean是怎樣定義怎樣加載的-就

像我們只關(guān)心從這個(gè)工廠里我們得到到什么產(chǎn)品對(duì)象,至于工廠是怎么生產(chǎn)這些對(duì)象的,這個(gè)基本的接口

不關(guān)心這些。如果要關(guān)心工廠是怎樣產(chǎn)生對(duì)象的,應(yīng)用程序需要使用具體的10c容器實(shí)現(xiàn)-當(dāng)然你可以自

己根據(jù)這個(gè)BeanFactory來實(shí)現(xiàn)自己的I0C容器,但這個(gè)沒有必耍,因?yàn)镾pring已經(jīng)為我們準(zhǔn)備好了一

系列工廠來讓我們使用。比如XmlBeanFactory就是針對(duì)最基礎(chǔ)的BeanFactory的I0C容器的實(shí)現(xiàn)-這

個(gè)實(shí)現(xiàn)使用xml來定義IOC容器中的bean。

Spring提供了?個(gè)BeanFactory的基本實(shí)現(xiàn),XmlBeanFactory同樣的通過使用模板模式來得到對(duì)IOC容

器的抽象-AbstractBeanFactory,DefaultListableBeanFactory這些抽象類為其提供模板服務(wù)。其中通過

resource接口來抽象bean定義數(shù)據(jù),對(duì)Xml定義文件的解析通過委托給XmlBeanDefinitionReader來完

成。下面我們根據(jù)書上的例子,簡(jiǎn)單的演示IOC容器的創(chuàng)建過程:

Java代碼

1.ClassPathResourceres=newClassPathResource("beans.xml");

2.DefaultListableBeanFactoryfactory=newDefaultListableBeanFactory();

3.XmlBeanDefinitionReaderreader=newXmlBeanDefinitionReader(factory);

4.reader.loadBeanDefinitions(res);

這些代碼演示了以下幾個(gè)步驟:

1.創(chuàng)建10c配置文件的抽象資源

2.創(chuàng)建一個(gè)BeanFactory

3.把讀取配置信息的BeanDefinitionReader,這里是XmlBeanDefinitionReader配置給BeanFactory

4.從定義好的資源位置讀入配置信息,具體的解析過程由Xm舊eanDefinitionReader來完成,這樣完成

整個(gè)載入bean定義的過程。我們的loC容器就建立起來了。在BeanFactory的源代碼中我們可以看到:

Java代碼

1.publicclassXmlBeanFactoryextendsDefaultListableBeanFactory{

2.〃這里為容器定義了一個(gè)默認(rèn)使用的bean定義讀取器

3.privatefinalXmlBeanDefinitionReaderreader=newXmlBeanDefinitionReader(this);

4.publicXmlBeanFactory(Resourceresource)throwsBeansException{

5.this(resource,null);

6.}

7.〃在初始化函數(shù)中使用讀取器來對(duì)資源進(jìn)行讀取,得到bean定義信息。

8.publicXmlBeanFactory(Resourceresource,BeanFactoryparentBeanFactory)throwsBean

sException{

9.super(parentBeanFactory);

10.this.reader.loadBeanDefinitions(resource);

11.)

我們?cè)诤竺鏁?huì)看到讀取器讀取資源和注冊(cè)bean定義信息的整個(gè)過程,基本上是和上下文的處理是一樣的,

從這里我們可以看到上下文和XmlBeanFactoryIOC容器的區(qū)別,BeanFactory往往不具備對(duì)資源

定義的能力,而上下文可以自己完成資源定義,從這個(gè)角度上看上下文更好用一些。

仔細(xì)分析SpringBeanFactory的結(jié)構(gòu),我們來看看在BeanFactory基礎(chǔ)上擴(kuò)展出的Applicationcontext-

我們最常使用的上下文。除了具備BeanFactory的全部能力,上下文為應(yīng)用程序又增添了許多便利:

*可以支持不同的信息源,我們看至UApplicationcontext擴(kuò)展了MessageSource

*訪問資源,體現(xiàn)在對(duì)ResourceLoader?和Resource的支持上面,這樣我們可以從不同地方得到bean

定義資源

*支持應(yīng)用事件,繼承了接口ApplicationEventPublisher,這樣在上下文中引入了事件機(jī)制而

BeanFactory是沒有的。

Applicationcontext允許上下文嵌套-通過保持父上下文可以維持一個(gè)上下文體系-這個(gè)體系我們?cè)谝?/p>

后對(duì)Web容器中的上下文環(huán)境的分析中可以清楚地看到。對(duì)丁-bean的查找可以在這個(gè)上下文體系中發(fā)生,

首先檢查當(dāng)前上下文,其次是父上下文,逐級(jí)向上,這樣為不同的Spring應(yīng)用提供了一個(gè)共享的bean定

義環(huán)境。這個(gè)我們?cè)诜治鯳eb容器中的上下文環(huán)境時(shí)也能看到。

Applicationcontext提供loC容器的生要接口,在其體系中有許多抽象子類比如AbstractApplicationContext

為具體的BeanFactory的實(shí)現(xiàn),比如FileSystemXmlApplicationContext和

ClassPathXmlApplicationContext提供上下文的模板,使得他們只需要關(guān)心具體的資源定位問題。當(dāng)應(yīng)用

程序代碼實(shí)例化FileSystemXmlApplicationContext的時(shí)候,得到loC容器的一種具體表現(xiàn)-

Applicationcontext,從而應(yīng)用程序通過Applicationcontext來管理對(duì)bean的操作。

BeanFactory是一個(gè)接口,在實(shí)際應(yīng)用中我們一般使用ApplicationContext來使用IOC容器,它們也是IOC

容器展現(xiàn)給應(yīng)用開發(fā)者的使用接口。對(duì)應(yīng)用程序開發(fā)者來說,可以認(rèn)為BeanFactoryApplicationFactory

在不同的使用層面上代表了SPRING提供的IOC容器服務(wù)。

卜面我們具體看看通過FileSystemXmlApplicationContext是怎樣建立起IOC容器的,顯而易見我們可以通

過new來得到loC容器:

Java代碼

1.Applicationcontext=newFileSystemXmlApplicationContext(xmlPath);

調(diào)用的是它初始化代碼:

Java代碼

1.publicFileSystemXmlApplicationContext(String[]configLocations,booleanrefresh,Applicatio

nContextparent)

2.throwsBeansException{

3.super(parent);

4.this.configLocations=configLocations;

5.if(refresh){

6.〃這里.是loC容器的初始化過程,兀初始化過程的大致步驟由AbstractApplicationContext

來定義

7.refresh();

8.}

9.)

refresh的模板在AbstractApplicationContext:

Java代碼

1.publicvoidrefresh()throwsBeansException,HlegalStateException{

2.synchronized(this.startupShutdownMonitor){

3.synchronized(this.activeMonitor){

4.this.active=true;

5.)

6.

7.〃這里需要子類來協(xié)助完成資源位置定義,bean載入和向IOC容器注冊(cè)的過程

8.refreshBeanFactory();

9......................

10.}

這個(gè)方法包含了整個(gè)BeanFactory初始化的過程,對(duì)于特定的FileSystemXm舊eanFactory,我們看到定位

資源位置由refreshBeanFactory()來實(shí)現(xiàn):

在AbstractXmlApplicationContext中定義了對(duì)資源的讀取過程,默認(rèn)由Xm舊eanDefi川tionReader來讀?。?/p>

Java代碼

1.protectedvoidloadBeanDefinitions(DefaultListableBeanFactorybeanFactory)throwslOExce

ption{

2.//這里使用XMLBeanDefinitionReader來投入bean定義信息的XML文件

3.XmlBeanDefinitionReaderbeanDefinitionReader=newXmlBeanDefinitionReader(beanFact

ory);

4.

5.〃這里配置reader的環(huán)境,其中ResourceLoader是我們用來定位bean定義信息資源位置.的

6.〃/因?yàn)樯舷挛谋旧韺?shí)現(xiàn)了ResourceLoader接口,所以?U以內(nèi)接把上卜.文作為ResourceLoader

傳遞給XmlBeanDefinitionReader

7.beanDefinitionReader.setResourceLoader(this);

8.beanDefinitionReader.setEntityResolver(newResourceEntityResolver(this));

9.

10.initBeanDefinitionReader(beanDefinitionReader);

11.〃這里轉(zhuǎn)到定義好的Xm舊eanDefi川tionReader「口對(duì)載入bean信息進(jìn)行處理

12.loadBeanDefinitions(beanDefinitionReader);

13.}

轉(zhuǎn)至UbeanDefinitionReader中進(jìn)行處理:

Java代碼

1.protectedvoidloadBeanDefinitions(XmlBeanDefinitionReaderreader)throwsBeansExceptio

n,lOException{

2.Resource[]configResources=getConfigResources();

3.if(configResources!=null){

4.〃調(diào)用XmlBeanDefinitionReader來載入bean定義信息。

5.reader.loadBeanDefinitions(configResources);

6.}

7.String[]configLocations=getConfigLocations();

8.if(configLocations!=null){

9.reader.loadBeanDefinitions(configLocations);

10.}

11.)

而在作為其抽象父類的AbstractBeanDefirdtionReader「t1來定義載入過程:

Java代碼

1.publicintloadBeanDefinitions(Stringlocation)throwsBeanDefinitionStoreException{

2.〃這里得到當(dāng)前定義的ResourceLoader,默認(rèn)的我們使用DefaultResourceLoader

3.ResourceLoaderresourceLoader=getResourceLoader();

4〃如果沒有找到我們需要的ResourceLoader,宜接拋出異常

5.if(resourceLoaderinstanceofResourcePatternResolver){

6.//這里處理我們?cè)诙x位置時(shí)使用的各種pattern,需要ResourcePatternResolver來完成

7.try{

8.Resource[]resources=((ResourcePatternResolver)resourceLoader).getResources(loc

ation);

9.intloadCount=loadBeanDefinitions(resources);

10.returnloadCount;

11.}

12.

13.}

14.else{

15.//這里通過ResourceLoader來完成位置定位

16.Resourceresource=resourceLoader.getResource(location);

17.//這里已經(jīng)把一個(gè)位置定義轉(zhuǎn)化為Resource接口,可以供XmlBeanDefinitionReader來使

用了

18.intloadCount=loadBeanDefinitions(resource);

19.returnloadCount;

20.}

21.)

當(dāng)我們通過ResourceLoader載入資源,別忘了了我們的GenericApplicationContext也實(shí)現(xiàn)了

ResourceLoader接口:

Java代碼

1.publicclassGenericApplicationContextextendsAbstractApplicationContextimplementsBea

nDefinitionRegistry{

2.publicResourcegetResource(Stringlocation){

3.〃這里調(diào)用當(dāng)前的loader也就是DefaultResourceLoader來完成載入

4.if(this.resourceLoader!=null){

5.returnthis.resourceLoader.getResource(location);

6.}

7.returnsuper.getResource(location);

8.)

9

10.}

而我們的FileSystemXmlApplicationContext就是一個(gè)DefaultResourceLoader-

GenericApplicationContext。通過DefaultResourceLoader:

Java代碼

1.publicResourcegetResource(Stringlocation){

2.〃如果是類路徑的方式,那需要使用ClassPathResource來得到bean文件的資源對(duì)象

3.if(location.startsWith(CLASSPATH_URL_PREFIX)){

4.returnnewClassPathResource(location.substnng(CLASSPATH_URL_PREFIX.Iength()),

getClassLoader());

5.}

6.else{

7.try{

8.//如果是URL方式,使用UrIResource作:為bean文件的資源對(duì)象

9.URLurl=newURL(location);

10.returnnewUrlResource(url);

11.}

12.catch(MalformedURLExceptionex){

13.〃如果都不是,那我們只能委托給了類由廣類來決定使用什么樣的資源對(duì)象了

14.returngetResourceByPath(location);

15.}

16.}

17.)

我們的FileSystemXmlApplicationContext本身就是是DefaultResourceLoader的實(shí)現(xiàn)類,他實(shí)現(xiàn)了以卜的

接口:

Java代碼

1.protectedResourcegetResourceByPath(Stringpath){

2.if(path!=null&&path.startsWith(7")){

3.path=path.substring(l);

4.)

5.〃這里使用文件系統(tǒng)資源對(duì)象來定義bean文件

6.returnnewFileSystemResource(path);

7.}

這樣代碼就回到了FileSystemXmlApplicationContext中來,他提供了FileSystemResource來完成從文件

系統(tǒng)得到配置文件的資源定義。這樣,就可以從文件系統(tǒng)路徑上對(duì)IOC配置文件進(jìn)行加載-當(dāng)然我們可

以按照這個(gè)邏輯從任何地方加載,在Spring中我們看到它提供的各種資源抽象,比如ClassPathResource,

URLResource,FileSystemResource等來供我們使用。上面我們看到的是定位Resource的一個(gè)過程,而

這只是加載過程的一部分一我們回至UAbstractBeanDefirdtionReaderz中的loadDefinitions(resource)來看

看得到代表bean文件的資源定義以后的載入過程,默認(rèn)的我們使用XmlBeanDefinitionReader:

Java代碼

1.publicintloadBeanDefinitions(EncodedResourceencodedResource)throwsBeanDefinitionS

toreException{

2.............

3.try{

4.〃這里通過Resource得至UInputstream的IO流

5.Inputstreaminputstream=encodedResource.getResource().getlnputStream();

6.try(

7.〃從Inputstream中得到XML的解析源

8.InputSourceinputSource=newInputSource(inputStream);

9.if(encodedResource.getEncoding()!=null){

10.inputSource.setEncoding(encodedResource.getEncoding());

11.)

12.〃這里是具體的解析和注冊(cè)過程

13.returndoLoadBeanDefinitions(inputSource,encodedResource.getResource());

14.}

15.finally{

16.〃關(guān)閉從Resource中得到的IO流

17.inputStream.close();

18.}

19.}

20.

21.)

22.

23.protectedintdoLoadBeanDefinitions(lnputSourceinputsource,Resourceresource)

24.throwsBeanDefinitionStoreException{

25.try{

26.intvalidationMode=getValidationModeForResource(resource);

27.〃通過解析得到DOM,然后完成bean在IOC容器中的注冊(cè)

28.Documentdoc=this.documentLoader.loadDocument(

29.inputsource,this.entityResolver,this.errorHandler,validationMode,space

Aware);

30.returnregisterBeanDefinitions(doc,resource);

31.}

32.............

33.}

我們看到先把定義文件解析為DOM對(duì)象,然后進(jìn)行具體的注冊(cè)過程:

Java代碼

1.publicintregisterBeanDefinitions(Documentdoc,Resourceresource)throwsBeanDefinition

StoreException{

2.//這里定義解析器,使用XmlBeanDefinitionParser來解析xml方式的bean定義文件-現(xiàn)在的

版本不用這個(gè)解析器了,使用的是XmlBeanDefinitionReader

3.if(this.parserClass!=null){

4.XmlBeanDefinitionParserparser=

5.(XmlBeanDefinitionParser)BeanUtils.instantiateClass(this.parserClass);

6.returnparser.registerBeanDefinitions(this,doc,resource);

7.)

8.//具體的注冊(cè)過程,首先得到XmlBeanDefinitionReader,來處理xml的bean定義文件

9.BeanDefinitionDocumentReaderdocumentReader=createBeanDefinitionDocumentReader(

);

10.intcountBefore=getBeanFactory().getBeanDefinitionCount();

11.documentReader.registerBeanDefinitions(doc,createReaderContext(resource));

12.returngetBeanFactory().getBeanDefinitionCount()-countBefore;

13.)

具體的在BeanDefinitionDocumentReader中完成對(duì),下面是一個(gè)簡(jiǎn)要的注冊(cè)過程來完成bean定義文件的

解析和IOC容器中bean的初始化

Java代碼

1.publicvoidregisterBeanDefinitions(Documentdoc,XmlReaderContextreaderContext){

2.this.readerContext=readerContext;

3.

4.logger.debug("Loadingbeandefinitions*');

5.Elementroot=doc.getDocumentElement();

6.

7.BeanDefinitionParserDelegatedelegate=createHelper(readerContext,root);

8.

9.preProcessXmI(root);

10.parseBeanDefinitions(root,delegate);

11.postProcessXml(root);

12.)

13.

14.protectedvoidparseBeanDefinitions(Elementroot,BeanDefinitionParserDelegatedelegate){

15.if(delegate.isDefaultNamespace(root.getNamespaceURI())){

16.〃這里得到xml文件的子節(jié)點(diǎn),比如各個(gè)bean節(jié)點(diǎn)

17.NodeListnl=root.getChildNodes();

18.

19.〃這里對(duì)每個(gè)節(jié)點(diǎn)進(jìn)行分析處理

20.for(inti=0;i<nl.getLength();i++){

21.Nodenode=nl.item(i);

22.if(nodeinstanceofElement){

23.Elementele=(Element)node;

24.Stringnamespacellri=ele.getNamespacellRI();

25.if(delegate.isDefaultNamespace(namespacellri)){

26.〃這此是解析過程的調(diào)用,對(duì)缺省的元素進(jìn)行分析比如bean元素

27.parseDefaultElement(ele,delegate);

28.}

29.else{

30.delegate.parseCustomElement(ele);

31.)

32.}

33.}

34.}else{

35.delegate.parseCustomElement(root);

36.}

37.)

38.

39.privatevoidparseDefaultElement(Elementele,BeanDefinitionParserDelegatedelegate){

40.〃這里對(duì)元素Import進(jìn)行處理

41.if(DomUtils.nodeNameEquals(eletIMPORT_ELEMENT)){

42.importBeanDefinitionResource(ele);

43.}

44.elseif(DomUtils.nodeNameEquals(ele,ALIAS_ELEMENT)){

45.Stringname=ele.getAttribute(NAME_ATTRIBUTE);

46.Stringalias=ele.getAttribute(ALIAS_ATTRIBUTE);

47.getReaderContext().getReader().getBeanFactory().registerAlias(name,alias);

48.getReaderContext().fireAliasRegistered(name1alias,extractSource(ele));

49.}

50.〃這里對(duì)我們最熟悉的bean元素進(jìn)行處理

51.elseif(DomUtils.nodeNameEquals(ele,BEAN_ELEMENT)){

52.〃委托給BeanDefinitionParserDelegate來完成對(duì)bean元素的處理,這個(gè)類包含了具體的

bean解析的過程。

53.//把解析bean文件得到的信息放到BeanDefinition里,他是bean信息的主要載體,也是IOC

容器的管理對(duì)象。

54.BeanDefinitionHolderbdHolder=delegate.parseBeanDefinitionElement(ele);

55.if(bdHolder!=null){

56.bdHolder=delegate.decorateBeanDefinitionlfRequired(ele,bdHolder);

57.//這里是向10c容器注冊(cè),實(shí)際上是放到IOC容器的一個(gè)map里

58.BeanDefinitionReaderlltils.registerBeanDefinition(bdHolder,getReaderContext().getRe

gistryO);

59.

60.//這里向IOC容器發(fā)送事件,表示解析和注冊(cè)完成。

61.getReaderContext().fireComponentRegistered(newBeanComponentDefinition(bdHolde

0);

62.}

63.}

64.}

我們看到在parseBeanDefinition中對(duì)具體bean元素的解析式交給BeanDefinitionParserDelegate來完成

的,下面我們看看解析完的bean是怎樣在IOC容器中注冊(cè)的:

在BeanDefinitionReaderUtils調(diào)用的是:

Java代碼

1.publicstaticvoidregisterBeanDefinition(

2.BeanDefinitionHolderbdHolder,BeanDefinitionRegistrybeanFactory)throwsBeansExce

ption{

3.

4.//這里得到需要注冊(cè)bean的名字:

5.StringbeanName=bdHolder.getBeanName();

6.〃這是調(diào)用IOC來注冊(cè)的bean的過程,需要得到BeanDefinition

7.beanFactory.registerBeanDefinition(beanName,bdHolder.getBeanDefinition());

8.

9.//別名也是可以通過IOC容器和bean聯(lián)系起來的進(jìn)行注冊(cè)

10.String[]aliases=bdHolder.getAliases();

11.if(aliases!=null){

12.for(inti=0;i<aliases.length;i++){

13.beanFactory.registerAlias(beanName,aliases[i]);

14.}

15.}

16.}

我們看看XmlBeanFactory中的注冊(cè)實(shí)現(xiàn):

Java代碼

1.//----------------------------------------------------------------

2.//這里是IOC容器對(duì)BeanDefiMionRegistry接口的實(shí)現(xiàn)

3.//----------------------------------------------------------------

4.

5.publicvoidregisterBeanDefinition(StringbeanName,BeanDefinitionbeanDefinition)

6.throwsBeanDefinitionStoreException{

7.

8..….〃這里省略了對(duì)BeanDefinition的驗(yàn)證過程

9.〃先看看在容得丫是不是已經(jīng)有了同名的bean,如果仃拋出異常。

10.ObjectoldBeanDefinition=this.beanDefinitionMap.get(beanName);

11.if(oldBeanDefinition!=null){

12.if(ithis.allowBeanDefinitionOverriding){

13....................

14.)

15.else{

16.〃把bean的名字加到IOC容器中去

17.this.beanDefinitionNames.add(beanName);

18.}

19.〃這口!把bean的名字和Bean定義聯(lián)系起來放到一個(gè)HashMap中去JOC容器通過這個(gè)Map

來維護(hù)容器里的Bean定義信息。

20.this.beanDefinitionMap.put(beanName,beanDefinition);

21.removeSingleton(beanName);

22.}

這樣就完成了Bean定義在IOC容器中的注冊(cè),就可被IOC容器進(jìn)行管理和使用了。

從上面的代碼來看,我們總結(jié)一下IOC容器初始化的基本步驟:

*初始化的入口在容器實(shí)現(xiàn)中的refresh。調(diào)用來完成

*對(duì)bean定義載入IOC容器使用的方法是loadBeanDefiEtion,其中的大致過程如下:通過

ResourceLoader來完成資源文件位.置的定位,DefaultResourceLoader是默認(rèn)的實(shí)現(xiàn),同時(shí)上下文本身就

給出了ResourceLoader的實(shí)現(xiàn),可以從類路徑,文件系統(tǒng),URL等方式來定為資源位置。如果是

XmlBeanFactory作為IOC容器,那么需要為它指定bean定義的資源,也就是說bean定義文件時(shí)通過抽

象成Resource來被IOC容器處理的,容器通過BeanDefinitionReader來完成定義信息的解析和Bean信

息的注冊(cè),往往使用的是XmlBeanDefinitionReader來解析bean的xml定義文件-實(shí)際的處理過程是委托

給BeanDefinitionParserDelegate來完成的,從而得到bean的定義信息,這些信息在Spring中使用

BeanDefinition對(duì)象來表示一這個(gè)名字可以讓我們想到loadBeanDefinition,RegisterBeanDefirdtion這些

相關(guān)的方法一他們都是為處理BeanDefinitin服務(wù)的,loC容器解析得到BeanDefinition以后,需要把它

在IOC容器中注冊(cè),這由10c實(shí)現(xiàn)BeanDbiEtionRegistry接口來實(shí)現(xiàn)。注冊(cè)過程就是在IOC容器內(nèi)部

維護(hù)的一個(gè)HashMap來保存得到的BeanDefinition的過程。這個(gè)HashMap是loC容器持有bean信息的

場(chǎng)所,以后對(duì)bean的操作都是圍繞這個(gè)HashMap來實(shí)現(xiàn)的0

*然后我們就可以通過BeanFactory和Applicationcontext來享受到SpringIOC的服務(wù)了.

在使用IOC容器的時(shí)候,我們注意到除了少量粘合代碼,絕大多數(shù)以正確loC風(fēng)格編寫的應(yīng)用程序代碼完

全不用關(guān)心如何到達(dá)工廠,因?yàn)槿萜鲗堰@些對(duì)象與容器管理的其他對(duì)象鉤在一起?;镜牟呗允前压S

放到已知的地方,最好是放在對(duì)預(yù)期使用的上下文有意義的地方,以及代碼將實(shí)際需要訪問工廠的地方。

Spring本身提供了對(duì)聲明式載入web應(yīng)用程序用法的應(yīng)用程序上下文,并將其存儲(chǔ)在ServletContext中的

框架實(shí)現(xiàn)。具體可以參見以后的文章。

在使用SpringIOC容器的時(shí)候我們還需要區(qū)別兩個(gè)概念:

BeanfactoryFactorybean,其中BeanFactory指的是IOC容器的編程抽象,ttillApplicationcontext,

XmlBeanFactory等,這些都是IOC容器的具體表現(xiàn),需要使用什么樣的容器由客戶決定但Spring為我們

提供了豐富的選擇。而FactoryBean只是?個(gè)可以在10c容器中被管理的個(gè)bean,是對(duì)各種處理過程和

資源使用的抽象,Factorybean在需要時(shí)產(chǎn)生另一個(gè)對(duì)象,而不返回FactoryBean本省,我們可以把它看成

是一個(gè)抽象工廠,對(duì)它的調(diào)用返回的是工廠生產(chǎn)的產(chǎn)品。所有的Factorybean都實(shí)現(xiàn)特殊的

org.springframework.beans.factory.FactoryBeanft0,當(dāng)使用容器中factorybean的時(shí)候,該容器不會(huì)返

叵Ifactorybean本身,而是返回其生成的對(duì)象。Spring包括了大部分的通用資源和服務(wù)訪問抽象的Factory

bean的實(shí)現(xiàn),其中包括:

對(duì)JNDI查詢的處理,對(duì)代理對(duì)象的處理,對(duì)事務(wù)性代理的處理,對(duì)RMI代理的處理等,這些我們都可以

看成是具體的工廠,看成是SPRING為我們建立好的工廠。也就是說Spring通過使用抽象工廠模式為我

們準(zhǔn)備了一系列工廠來生產(chǎn)一些特定的對(duì)象,免除我們手工重復(fù)的工作,我們要使用時(shí)只需要在I0C容器

里配置好就能很方便的使用了。

現(xiàn)在我們來看看在Spring的事件機(jī)制,Spring中有3個(gè)標(biāo)準(zhǔn)事件,ContextRefreshEvent,

ContextCloseEvent,RequestHandledEvent他們通過ApplicationEvent,同樣的如果需要自定義時(shí)間

也只需要實(shí)現(xiàn)ApplicationEvent接口,參照ContextCloseEvent的實(shí)現(xiàn)可以定制自己的事件實(shí)現(xiàn):

Java代碼,J

1.publicclassContextClosedEventextendsApplicationEvent{

2.

3.publicContextClosedEvent(ApplicationContextsource){

4.super(source);

5.}

6.

7.publicApplicationcontextgetApplicationContext(){

8.return(Applicationcontext)getSource();

9.)

10.}

可以通過顯現(xiàn)ApplicationEventPublishAware接口,將事件發(fā)布器耦合到Applicationcontext這樣可以使

用Applicationcontext框架來傳遞和消費(fèi)消息,然后在Applicationcontext中配置好bean就可以了,在消

費(fèi)消息的過程中,接受者通過實(shí)現(xiàn)ApplicationListener接收消息。

比如可以直接使用Spring的ScheduleTimerTask和TimerFactoryBean作為定時(shí)器定時(shí)產(chǎn)生消息,具體可

以參見《Spring框架高級(jí)編程》。

TimerFactoryBean是一個(gè)工廠bean,對(duì)其中的ScheduleTimerTask進(jìn)行處理后輸出,參考

ScheduleTimerTask的實(shí)現(xiàn)發(fā)現(xiàn)它最后調(diào)用的是jre的TimerTask:

Java代碼

1.publicvoidsetRunnable(RunnabletimerTask){

2.this.timerTask=newDelegatingTimerTask(timerTask);

3.)

在書中給出了一個(gè)定時(shí)發(fā)送消息的例子,當(dāng)然可以可以通過定時(shí)器作其他的動(dòng)作,有兩種方法:

1.定義MethodlnvokingTimerTaskFactoryBean定義要執(zhí)行的特定bean的特定方法,對(duì)需要做什么進(jìn)行封

裝定義;

2.定義TimerTask類,通過extendsTimerTask來得到,同時(shí)對(duì)需要做什么進(jìn)行自定義

然后需要定義具體的定時(shí)器參數(shù),通過配置ScheduledTimerTask中的參數(shù)和timerTask來完成,以下是

它需要定義的具體屬性,timerTask是在前面已經(jīng)定義好的bean

Java代碼

1.privateTimerTasktimerTask;

2.

3.privatelongdelay=0;

4.

5.privatelongperiod=0;

6.

7.privatebooleanfixedRate=false;

最后,需要在Applicationcontext中注冊(cè),需要把ScheduledTimerTask配置到FactoryBean-

TimerFactoryBean,這樣就由IOC容器來管理定時(shí)器了。參照

TimerFactoryBean的屬性,可以定制一組定時(shí)器。

Java代碼

1.publicclassTimerFactoryBeanimplementsFactoryBean,InitializingBean,DisposableBean{

2.

3.protectedfinalLoglogger=LogFactory.getLog(getClass());

4.

5.privateScheduledTimerTask[]scheduledTimerTasks;

6.

7.privatebooleandaemon=false;

8.

9.privateTimertimer;

10.

11

12.)

如果要發(fā)送時(shí)間我們只需要在定義好的ScheduledTimerTasks中publish定義好的事件就可以了。具體可

以參考書中例子的實(shí)現(xiàn),這里只是結(jié)合FactoryBean的原理做一些解釋。如果結(jié)合事件和定時(shí)器機(jī)制,我

們可以很方便的實(shí)現(xiàn)heartbeat(看門狗),書中給出了這個(gè)例子,這個(gè)例子實(shí)際上結(jié)合了Spring事件和定

時(shí)機(jī)制的使用兩個(gè)方面的知識(shí)-當(dāng)然了還有IOC容器的知識(shí)(任何Spring應(yīng)用我想都逃不掉10c的魔

爪:)

上面我們分析了I0C容器本身的實(shí)現(xiàn),下面我們看看在典型的web環(huán)境中,SpringIOC容器是怎樣被載

入和起作用的。

簡(jiǎn)單的說,在web容器中,通過ServletContext為Spring的IOC容器提供宿主環(huán)境,對(duì)應(yīng)的建立起一個(gè)

IOC容器的體系。其中,首先需要建立的是根上下文,這個(gè)上下文持有的對(duì)象可以有業(yè)務(wù)對(duì)象,數(shù)據(jù)存取

對(duì)象,資源,事物管理器等各種中間層對(duì)象。在這個(gè)上下文的基礎(chǔ)匕和webMVC相關(guān)還會(huì)有一個(gè)上下

文來保存控制器之類的MVC對(duì)象,這樣就構(gòu)成了一個(gè)層次化的上下文結(jié)構(gòu)。在web容器中啟動(dòng)Spring應(yīng)

用程序就是一個(gè)建立這個(gè)上下文體系的過程。Spring為web應(yīng)用提供了上下文的擴(kuò)展接口

WebApplicationContext:

Java代碼

1.publicinterfaceWebApplicationContextextendsApplicationcontext{

2.〃這里定義的常量用于在ServletContext中存取根上下文

3.StringROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE=WebApplicationContext.clas

s.getName()+".ROOT";

4.……

5.〃對(duì)WebApplicationContext來說,需要得到Web容器的ServletContext

6.ServletContextgetServletContext();

7.)

而一般的啟動(dòng)過程,Spring會(huì)使用一個(gè)默認(rèn)的實(shí)現(xiàn),XmlWebApplicationContext-這個(gè)上下文實(shí)現(xiàn)作為在

web容器中的根上下文容器被建立起來,具體的建立過程在下面我們會(huì)詳細(xì)分析。

Java代碼

1.publicclassXmlWebApplicationContextextendsAbstractRefreshableWebApplicationContext

{

2.

3.r這是和web部署相關(guān)的位置信息,用來作為默認(rèn)的根上下文bean定義信息的存放位置*/

4.publicstaticfinalStringDEFAULT_CONFIG_LOCATION="/WEB-INF/applicationContext.

xml”;

5.publicstaticfinalStringDEFAULT_CONFIG_LOCATION_PREFIX="/WEB-INF/'1;

6.publicstaticfinalStringDEFAULT_CONFIG_LOCATION_SUFFIX=".xml1';

7.

8.〃我們又看到了熟悉的loadBeanDefi川tion,就像我們前面對(duì)IOC容器的分析中一樣,這個(gè)加載

工程在容器的refresh。的時(shí)候啟動(dòng)。

9.protectedvoidloadBeanDefinitions(DefaultListableBeanFactorybeanFactory)throwslOEx

ception{

10.〃對(duì)于XmlWebApplicationContext,當(dāng)然使用的是XmlBeanDefinitionReader來對(duì)bean定義

信息來進(jìn)行解析

11.XmlBeanDefinitionReaderbeanDefinitionReader=newXmlBeanDefinitionReader(beanF

actory);

12.

13.beanDefinitionReader.setResourceLoader(this);

14.beanDefinitionReader.setEntityResolver(newResourceEntityResolver(this));

15.

16.initBeanDefinitionReader(beanDefinitionReader);

17.loadBeanDefinitions(beanDefinitionReader);

18.}

19.

20.protectedvoidinitBeanDefinitionReader(XmlBeanDefinitionReaderbeanDefinitionReader){

21.}

22.〃使用Xm舊eanDefinitionReader來讀入bean定義信息

23.protectedvoidloadBeanDefinitions(XmlBeanDefinitionReaderreader)throwsBeansExcep

tion,lOException{

24.String[]configLocations=getConfigLocations();

25.if(configLocations!=null){

26.for(inti=0;i<configLocations.length;i++){

27.readeHoadBeanDefi川tions(configLocations[i]);

28.}

29.}

30.}

31.〃這里取得bean定義信息位置,默認(rèn)的地方是/WEB-INF/applicaUonContext.xml

32.protectedStringOgetDefaultConfigLocations(){

33.if(getNamespace()!=null){

34.returnnewString[]{DEFAULT_CONFIG_LOCATION_PREFIX+getNamespace()+D

EFAULT_CONFIG_LOCATION_SUFFIX};

35.}

36.else{

37.returnnewStringf]{DEFAULT_CONFIG_LOCATION};

38.}

39.}

40.}

對(duì)于一個(gè)Spring激活的web應(yīng)用程序,可以通過使用Spring代碼聲明式的指定在web應(yīng)用程序啟動(dòng)時(shí)我

入應(yīng)用程序上下文(WebApplicaHonContext),Spring的ContextLoader是提供這樣性能的類,我們可以使

用ContextLoaderServlet或者ContextLoaderListener的啟動(dòng)時(shí)投入的Servlet來實(shí)例化SpringIOC容器

-為什么會(huì)有兩個(gè)不同的類來裝載它呢,這是因?yàn)樗鼈兊氖褂眯杷^(qū)別不同的Servlet容器支持的Serlvet

版本。但不管是ContextLoaderSevletiEMContextLoaderListener都使用ContextLoader來完成實(shí)際的

WebApplicationContext的初始化工作。這個(gè)ContextLoder就像是SpringWeb應(yīng)用程序在Web容器中的

加載器bootero當(dāng)然這些Servlet的具體使用我們都要借助web容器中的部署描述符來進(jìn)行相關(guān)的定義。

下面我們使用ContextLoaderListener作為載入器作一個(gè)詳細(xì)的分析,這個(gè)Servlet的監(jiān)聽器是根上下文被

投入的地方,也是整個(gè)Springweb應(yīng)用加載上卜.文的第一個(gè)地方;從加載過程我們可以看到,首先從

Servlet事件中得到ServletContext,然后可以讀到配置好的在web.xml的中的各個(gè)屬性值,然后

ContextLoder實(shí)例化WebApplicationContext并完成其載入和初始化作為根上下文。當(dāng)這個(gè)根上下文被裁

入后,它被綁定到web應(yīng)用程序的ServletContext上。任何需要訪問該Applicationcontext的應(yīng)用程序代

碼都可以從WebApplicationContextUtils類的靜態(tài)方法來得到:

Java代碼

1.WebApplicationContextgetWebApplicationContext(ServletContextsc)

以Tomcat作為Servlet容器為例,卜.面是具體的步驟:

1.Tomcat啟動(dòng)時(shí)需耍從web.xml中讀取啟動(dòng)參數(shù),在web.xml中我們需耍對(duì)ContextLoaderListener進(jìn)行

配置,對(duì)于在web應(yīng)用啟動(dòng)入口是在ContextLoaderListener中的初始化部分;從SpringMVC上看,實(shí)

際上在web容器中維護(hù)了一系列的IOC容器,其中在ContextLoader中載入的IOC容器作為根上下文而

存在于ServletContext中。

Java代碼

1.〃這里對(duì)根上下文進(jìn)行初始化。

2.publicvoidcontextlnitialized(ServletContextEventevent){

3.〃這里創(chuàng)建需要的ContextLoader

4.this.contextLoader=createContextLoader();

5.〃這里使用ContextLoader對(duì)根上下文進(jìn)行載入和初始化

6.this.contextLoader.initWebApplicationContext(event.getServletContext());

7.}

通過ContextLoader建立起根上下文的過程,我們可以在ContextLoader中看到:

Java代碼

1.publicWebApplicationContextinitWebApplicationContext(ServletContextservletContext)

2.throwsHlegalStateException,BeansException{

3.〃這里先看看是不是已經(jīng)在ServletContext中存在I??卜.文,如果仃說明前面已經(jīng)被載入過,或者

是配置文件有錯(cuò)誤。

4.if(servletContext.getAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTE

XT_ATTRIBUTE)!=null){

5.〃直接拋出異常

6..............

7.)

8.

9......................

10.try{

11.//這里載入根上下文的父上下文

12.Applicationcontextparent=loadParentContext(servletContext);

13.

14.〃這里創(chuàng)建根上下文作為整個(gè)應(yīng)用的上下文同時(shí)把它存到ServletContext中去,注意這里使用

的ServletContext的屬性值是

15.//ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE,以后的應(yīng)用都是根據(jù)這個(gè)屬性值

來取得根上下文的?往往作為自己上下文的父上下文

1

溫馨提示

  • 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請(qǐng)下載最新的WinRAR軟件解壓。
  • 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請(qǐng)聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
  • 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁內(nèi)容里面會(huì)有圖紙預(yù)覽,若沒有圖紙預(yù)覽就沒有圖紙。
  • 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
  • 5. 人人文庫(kù)網(wǎng)僅提供信息存儲(chǔ)空間,僅對(duì)用戶上傳內(nèi)容的表現(xiàn)方式做保護(hù)處理,對(duì)用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對(duì)任何下載內(nèi)容負(fù)責(zé)。
  • 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請(qǐng)與我們聯(lián)系,我們立即糾正。
  • 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時(shí)也不承擔(dān)用戶因使用這些下載資源對(duì)自己和他人造成任何形式的傷害或損失。

評(píng)論

0/150

提交評(píng)論