版權(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ì)自己和他人造成任何形式的傷害或損失。
最新文檔
- 恒流恒壓電源課程設(shè)計(jì)
- 體育行業(yè)的會(huì)計(jì)工作總結(jié)
- 線性代數(shù)總結(jié)課程設(shè)計(jì)
- 自動(dòng)散熱器課程設(shè)計(jì)
- 電子信息行業(yè)電話客服工作總結(jié)
- 文化行業(yè)采購(gòu)合作案例分析
- 教育行業(yè)美工工作心得交流
- 2023-2024學(xué)年上海師大附中閔行分校高一(下)期中語文試卷
- 醫(yī)療機(jī)構(gòu)保安工作內(nèi)容詳解
- IT科技行業(yè)中信息技術(shù)顧問的工作總結(jié)
- 廣東省公務(wù)員考試筆試真題及答案
- 道路減速帶減速模型分析
- 35kv及以下架空線路施工及驗(yàn)收規(guī)范
- 身體健康狀況自測(cè)表
- PID控制原理與調(diào)整方法
- 山東昌樂二中“271高效課堂”解讀
- 配電工程竣工資料
- 花鍵強(qiáng)度校核程序
- 毗尼日用切要20140619最終版
- 出庫(kù)單樣本12623
- 塔吊附墻加節(jié)頂升安全技術(shù)交底
評(píng)論
0/150
提交評(píng)論