Spring技術(shù)內(nèi)幕:深入解析Spring架構(gòu)與設(shè)計(jì)原理_第1頁
Spring技術(shù)內(nèi)幕:深入解析Spring架構(gòu)與設(shè)計(jì)原理_第2頁
Spring技術(shù)內(nèi)幕:深入解析Spring架構(gòu)與設(shè)計(jì)原理_第3頁
Spring技術(shù)內(nèi)幕:深入解析Spring架構(gòu)與設(shè)計(jì)原理_第4頁
Spring技術(shù)內(nèi)幕:深入解析Spring架構(gòu)與設(shè)計(jì)原理_第5頁
已閱讀5頁,還剩40頁未讀, 繼續(xù)免費(fèi)閱讀

下載本文檔

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

文檔簡介

Spring技術(shù)內(nèi)幕深入解析Spring架構(gòu)與設(shè)計(jì)原理(一)引子緣起

已經(jīng)很久沒有寫帖子了,現(xiàn)在總算是有點(diǎn)時(shí)間寫些東西,也算是對(duì)自己的一個(gè)記錄吧。剛剛完成了一個(gè)軟件產(chǎn)品,從概念到運(yùn)營都弄了一下,正在推廣當(dāng)中,雖然還沒有能夠達(dá)到盈虧平衡,但是這個(gè)過程,對(duì)自己也算是一種歷練。先不管結(jié)果如何,好呆走過這么一遭了。

我打算用這個(gè)帖子,把自己在這個(gè)過程中的一些心得,特別是對(duì)Spring新的理解,記錄下來。使用這個(gè)帖子的標(biāo)題,持續(xù)下來。

簡單來說,自己的軟件產(chǎn)品是一個(gè)基于互聯(lián)網(wǎng)的SaaS協(xié)同軟件平臺(tái),操作簡單,支持流程定義,管理和多種客戶端-像短信,MSN,智能手機(jī)什么的(我這里就不多做什么廣告了),也有一個(gè)企業(yè)版的版本,使用的技術(shù)框架是Hibernate+Spring+Wicket,下面是Linux和MySQL,還有云計(jì)算的平臺(tái)的使用,以支持其擴(kuò)展性,雖然現(xiàn)在還沒有可擴(kuò)展性的需求,但似乎不難從SaaS上,就會(huì)想到云計(jì)算,其實(shí),它們真的是天生的一對(duì)!

關(guān)于云計(jì)算,自己對(duì)這個(gè)技術(shù)很感興趣,覺得和開源軟件的結(jié)合,是很有意思的,因?yàn)樗鼈兌加谢诜?wù)的基因,在云計(jì)算平臺(tái)的使用上,也有一些初步的實(shí)踐。云計(jì)算是一個(gè)很有意思的話題,但在這里主要是想談Spring,所以對(duì)云計(jì)算,這里就先不多說了,但非常歡迎有興趣的朋友和一起另外找地方討論!

回到正題,在我自己的產(chǎn)品中,其中除了Wicket和云計(jì)算外,其他都是大家非常熟知的了,像Hibernate,Spring,MySQL什么的。在這個(gè)過程中,發(fā)現(xiàn)自己對(duì)一些技術(shù)點(diǎn)也有了新的認(rèn)識(shí),最有體會(huì)的是Spring。當(dāng)然,在這個(gè)過程中,更大的收獲是對(duì)產(chǎn)品開發(fā)整個(gè)過程的認(rèn)識(shí),在這點(diǎn)上,真是一言難盡........

回到自己還算了解的Spring,這次我使用的是3.0的代碼,所以,有機(jī)會(huì)也把這些代碼讀了幾遍,比原來的理解要加深了許多,也發(fā)現(xiàn)了不少和2.0代碼不同的地方,以及自己一些對(duì)Spring的新的理解,這些,就讓我就用這個(gè)帖子系列,給自己總結(jié)一下,也算是對(duì)自己以前的那個(gè)代碼分析的帖子做一個(gè)新的交代吧。

自己對(duì)Spring一點(diǎn)小小的見解

簡化Java企業(yè)應(yīng)用的開發(fā),是Spring框架的目標(biāo).就是我們熟知的當(dāng)年的那個(gè)interface21,也亦非吳下阿蒙了,由它演進(jìn)出來的Spring,以及由它帶來的嶄新開發(fā)理念,也早已伴隨著這個(gè)開源框架的廣泛應(yīng)用,而飛入尋常百姓家。與此同時(shí),伴隨著Spring的成熟,開源社區(qū)的成長,在Rod.Johnson的領(lǐng)導(dǎo)下,以Spring為核心的一系列開源軟件的產(chǎn)品組合,其脈絡(luò)也逐漸的清晰和豐富起來;現(xiàn)在,已經(jīng)發(fā)展成為一個(gè)包括軟件運(yùn)行,構(gòu)建,部署運(yùn)營,從而涵蓋整個(gè)軟件服務(wù)生命周期的產(chǎn)品族群;同時(shí)也成為,在當(dāng)今主流的軟件業(yè)態(tài)中,一個(gè)不可或缺的重要組成。

在最近完成的VMware公司對(duì)Spring的運(yùn)營者SpringSource公司的收購中,也讓我們又看到了一個(gè),在開源軟件中,蘊(yùn)含著的巨大商業(yè)價(jià)值,以及又一次基于開源模式的商業(yè)成功;也讓我們看到,Spring為自己設(shè)計(jì)的未來定位,它與云計(jì)算的融合趨勢(shì),以及,努力成為在云計(jì)算業(yè)態(tài)中,PaaS(PlatformAsaService)服務(wù)有力競(jìng)爭者的戰(zhàn)略設(shè)想;由此,可以想象,在云計(jì)算這個(gè)全新的計(jì)算時(shí)代中,如何秉承Spring的一貫風(fēng)格,為云計(jì)算應(yīng)用的開發(fā),提供高可靠,高可用,高可擴(kuò)展,高性能的應(yīng)用平臺(tái),對(duì)Spring團(tuán)隊(duì)來說,是一個(gè)面臨的全新挑戰(zhàn);在這個(gè)領(lǐng)域中的雄心和今后的作為,那就讓我們一起拭目以待吧。這里也有點(diǎn)湊巧了,正好Spring和云計(jì)算都是自己喜歡的東西,說不定以后,我還能夠在這兩者的結(jié)合上再寫些東西呢。

作為一個(gè)龐大的體系,Spring在Java企業(yè)應(yīng)用中,和我們熟悉的企業(yè)應(yīng)用服務(wù)器一樣,比如我們熟知的其他產(chǎn)品,像Weblogic,Websphere,JBoss,.NET這些等等,其定位和目的,都在于希望能夠起到一個(gè)企業(yè)應(yīng)用資源的集成管理,以及為應(yīng)用開發(fā)提供平臺(tái)支持的作用,這和我們熟知的,像UNIX和Windows這樣傳統(tǒng)意義上的操作系統(tǒng),在傳統(tǒng)的計(jì)算系統(tǒng)中,起到的作用非常的類似。只不過,按照個(gè)人的理解,它們不同在于,我們熟知的傳統(tǒng)操作系統(tǒng)關(guān)心的是存儲(chǔ),計(jì)算,通信,外圍設(shè)備這些物理資源的管理,并在管理這些資源的基礎(chǔ)上,為應(yīng)用程序提供一個(gè)統(tǒng)一平臺(tái)和服務(wù)接口;而像Spring這樣的應(yīng)用平臺(tái),它們關(guān)心的是在Java企業(yè)應(yīng)用中,對(duì)包括那些像Web應(yīng)用,數(shù)據(jù)持久化,事務(wù)處理,消息中間件,分布式計(jì)算等等這些,為企業(yè)應(yīng)用服務(wù)的抽象資源的統(tǒng)一管理,并在此基礎(chǔ)上,為應(yīng)用提供一個(gè)基于POJO的開發(fā)環(huán)境。盡管各自面向的資源,管理的對(duì)象,支持的應(yīng)用以及使用的場(chǎng)景不同,但這兩者在整個(gè)系統(tǒng)中的定位,卻依然有著可以類比和相互參考的地方,從某種意義上看,它們都起到一個(gè)資源協(xié)調(diào),平臺(tái)支持,以及服務(wù)集成的作用。

所以我覺得可以使用,我們看待傳統(tǒng)操作系統(tǒng)的方法和一些基本觀念,來對(duì)Spring進(jìn)行系統(tǒng)分析,以及對(duì)Spring進(jìn)行層次劃分,這樣可能更加容易理解,同時(shí),所以,個(gè)人感覺,仿照傳統(tǒng)操作系統(tǒng)的眼光,把對(duì)Spring框架的實(shí)現(xiàn),劃分為核心,組件和應(yīng)用這三個(gè)基本的層次,來理解Spring框架是不錯(cuò)的一個(gè)方法,就算是眾所周知的“三段論”的應(yīng)用吧。不知道這種分析方法,是不是太庸俗,但我自己還是覺得挺受用的,呵呵,誰叫我是個(gè)俗人呢!

今天先寫一些,就算是起個(gè)頭吧,明天繼續(xù)!寫寫IOC/AOP的一些具體東西。深入解析Spring架構(gòu)與設(shè)計(jì)原理(一)IOC實(shí)現(xiàn)原理IOC的基礎(chǔ)

下面我們從IOC/AOP開始,它們是Spring平臺(tái)實(shí)現(xiàn)的核心部分;雖然,我們一開始大多只是在這個(gè)層面上,做一些配置和外部特性的使用工作,但對(duì)這兩個(gè)核心模塊工作原理和運(yùn)作機(jī)制的理解,對(duì)深入理解Spring平臺(tái),卻是至關(guān)重要的;因?yàn)椋鼈兺瑫r(shí)也是Spring其他模塊實(shí)現(xiàn)的基礎(chǔ)。從Spring要做到的目標(biāo),也就是從簡化JavaEE開發(fā)的出發(fā)點(diǎn)來看,簡單的來說,它是通過對(duì)POJO開發(fā)的支持,來具體實(shí)現(xiàn)的;具體的說,Spring通過為應(yīng)用開發(fā)提供基于POJO的開發(fā)模式,把應(yīng)用開發(fā)和復(fù)雜的JavaEE服務(wù),實(shí)現(xiàn)解耦,并通過提高單元測(cè)試的覆蓋率,從而有效的提高整個(gè)應(yīng)用的開發(fā)質(zhì)量。這樣一來,實(shí)際上,就需要把為POJO提供支持的,各種JavaEE服務(wù)支持抽象到應(yīng)用平臺(tái)中去,去封裝起來;而這種封裝功能的實(shí)現(xiàn),在Spring中,就是由IOC容器以及AOP來具體提供的,這兩個(gè)模塊,在很大程度上,體現(xiàn)了Spring作為應(yīng)用開發(fā)平臺(tái)的核心價(jià)值。它們的實(shí)現(xiàn),是Rod.Johnson在他的另一本著作《ExpertOne-on-OneJ2EEDevelopmentwithoutEJB》中,所提到WithoutEJB設(shè)計(jì)思想的體現(xiàn);同時(shí)也深刻的體現(xiàn)了Spring背后的設(shè)計(jì)理念。

從更深一點(diǎn)的技術(shù)層面上來看,因?yàn)镾pring是一個(gè)基于Java語言的應(yīng)用平臺(tái),如果我們能夠?qū)ava計(jì)算模型,比如像JVM虛擬機(jī)實(shí)現(xiàn)技術(shù)的基本原理有一些了解,會(huì)讓我們對(duì)Spring實(shí)現(xiàn)的理解,更加的深入,這些JVM虛擬機(jī)的特性使用,包括像反射機(jī)制,代理類,字節(jié)碼技術(shù)等等。它們都是在Spring實(shí)現(xiàn)中,涉及到的一些Java計(jì)算環(huán)境的底層技術(shù);盡管對(duì)應(yīng)用開發(fā)人員來說,可能不會(huì)直接去涉及這些JVM虛擬機(jī)底層實(shí)現(xiàn)的工作,但是了解這些背景知識(shí),或多或少,對(duì)我們了解整個(gè)Spring平臺(tái)的應(yīng)用背景有很大的幫助;打個(gè)比方來說,就像我們?cè)诖髮W(xué)中,學(xué)習(xí)的那些關(guān)于計(jì)算機(jī)組織和系統(tǒng)方面的基本知識(shí),比如像數(shù)字電路,計(jì)算機(jī)組成原理,匯編語言,操作系統(tǒng)等等這些基本課程的學(xué)習(xí)。雖然,坦率的來說,對(duì)我們這些大多數(shù)課程的學(xué)習(xí)者,在以后的工作中,可能并沒有太多的機(jī)會(huì),直接從事這么如此底層的技術(shù)開發(fā)工作;但具備這些知識(shí)背景,為我們深入理解基于這些基礎(chǔ)技術(shù)構(gòu)架起來的應(yīng)用系統(tǒng),毫無疑問,是不可缺少的。隨著JVM虛擬機(jī)技術(shù)的發(fā)展,可以設(shè)想到的是,更多虛擬機(jī)級(jí)別的基本特性,將會(huì)持續(xù)的被應(yīng)用平臺(tái)開發(fā)者所關(guān)注和采用,這也是我們?cè)趯W(xué)習(xí)平臺(tái)實(shí)現(xiàn)的過程中,非常值得注意的一點(diǎn),因?yàn)檫@些底層技術(shù)實(shí)現(xiàn),毫無疑問,會(huì)對(duì)Spring應(yīng)用平臺(tái)的開發(fā)路線,產(chǎn)品策略產(chǎn)生重大的影響。同時(shí),在使用Spring作為應(yīng)用平臺(tái)的時(shí)候,如果需要更深層次的開發(fā)和性能調(diào)優(yōu),這些底層的知識(shí),也是我們知識(shí)庫中不可缺少的部分。有了這些底層知識(shí),理解整個(gè)系統(tǒng),想來就應(yīng)該障礙不大了。

IOC的一點(diǎn)認(rèn)識(shí)

對(duì)SpringIOC的理解離不開對(duì)依賴反轉(zhuǎn)模式的理解,我們知道,關(guān)于如何反轉(zhuǎn)對(duì)依賴的控制,把控制權(quán)從具體業(yè)務(wù)對(duì)象手中轉(zhuǎn)交到平臺(tái)或者框架中,是解決面向?qū)ο笙到y(tǒng)設(shè)計(jì)復(fù)雜性和提高面向?qū)ο笙到y(tǒng)可測(cè)試性的一個(gè)有效的解決方案。這個(gè)問題觸發(fā)了IoC設(shè)計(jì)模式的發(fā)展,是IoC容器要解決的核心問題。同時(shí),也是產(chǎn)品化的IoC容器出現(xiàn)的推動(dòng)力。而我覺得Spring的IoC容器,就是一個(gè)開源的實(shí)現(xiàn)依賴反轉(zhuǎn)模式的產(chǎn)品。

那具體什么是IoC容器呢?它在Spring框架中到底長什么樣?說了這么多,其實(shí)對(duì)IoC容器的使用者來說,我們常常接觸到的BeanFactory和ApplicationContext都可以看成是容器的具體表現(xiàn)形式。這些就是IoC容器,或者說在Spring中提IoC容器,從實(shí)現(xiàn)來說,指的是一個(gè)容器系列。這也就是說,我們通常所說的IoC容器,如果深入到Spring的實(shí)現(xiàn)去看,會(huì)發(fā)現(xiàn)IoC容器實(shí)際上代表著一系列功能各異的容器產(chǎn)品。只是容器的功能有大有小,有各自的特點(diǎn)。打個(gè)比方來說,就像是百貨商店里出售的商品,我們舉水桶為例子,在商店中出售的水桶有大有??;制作材料也各不相同,有金屬的,有塑料的等等,總之是各式各樣,但只要能裝水,具備水桶的基本特性,那就可以作為水桶來出售來讓用戶使用。這在Spring中也是一樣,它有各式各樣的IoC容器的實(shí)現(xiàn)供用戶選擇和使用;使用什么樣的容器完全取決于用戶的需要,但在使用之前如果能夠了解容器的基本情況,那會(huì)對(duì)容器的使用是非常有幫助的;就像我們?cè)谫徺I商品時(shí)進(jìn)行的對(duì)商品的考察和挑選那樣。

我們從最基本的XmlBeanFactory看起,它是容器系列的最底層實(shí)現(xiàn),這個(gè)容器的實(shí)現(xiàn)與我們?cè)赟pring應(yīng)用中用到的那些上下文相比,有一個(gè)非常明顯的特點(diǎn),它只提供了最基本的IoC容器的功能。從它的名字中可以看出,這個(gè)IoC容器可以讀取以XML形式定義的BeanDefinition。理解這一點(diǎn)有助于我們理解ApplicationContext與基本的BeanFactory之間的區(qū)別和聯(lián)系。我們可以認(rèn)為直接的BeanFactory實(shí)現(xiàn)是IoC容器的基本形式,而各種ApplicationContext的實(shí)現(xiàn)是IoC容器的高級(jí)表現(xiàn)形式。

仔細(xì)閱讀XmlBeanFactory的源碼,在一開始的注釋里面已經(jīng)對(duì)XmlBeanFactory的功能做了簡要的說明,從代碼的注釋還可以看到,這是RodJohnson在2001年就寫下的代碼,可見這個(gè)類應(yīng)該是Spring的元老類了。它是繼承DefaultListableBeanFactory這個(gè)類的,這個(gè)DefaultListableBeanFactory就是一個(gè)很值得注意的容器!Java代碼public

class

XmlBeanFactory

extends

DefaultListableBeanFactory

{

private

final

XmlBeanDefinitionReader

reader

=

new

XmlBeanDefinitionReader(this);

public

XmlBeanFactory(Resource

resource)

throws

BeansException

{

this(resource,

null);

}

public

XmlBeanFactory(Resource

resource,

BeanFactory

parentBeanFactory)

throws

BeansException

{

super(parentBeanFactory);

this.reader.loadBeanDefinitions(resource);

}

}

XmlBeanFactory的功能是建立在DefaultListableBeanFactory這個(gè)基本容器的基礎(chǔ)上的,在這個(gè)基本容器的基礎(chǔ)上實(shí)現(xiàn)了其他諸如XML讀取的附加功能。對(duì)于這些功能的實(shí)現(xiàn)原理,看一看XmlBeanFactory的代碼實(shí)現(xiàn)就能很容易地理解。在如下的代碼中可以看到,在XmlBeanFactory構(gòu)造方法中需要得到Resource對(duì)象。對(duì)XmlBeanDefinitionReader對(duì)象的初始化,以及使用這個(gè)這個(gè)對(duì)象來完成loadBeanDefinitions的調(diào)用,就是這個(gè)調(diào)用啟動(dòng)了從Resource中載入BeanDefinitions的過程,這個(gè)loadBeanDefinitions同時(shí)也是IoC容器初始化的重要組成部分。

簡單來說,IoC容器的初始化包括BeanDefinition的Resouce定位、載入和注冊(cè)這三個(gè)基本的過程。我覺得重點(diǎn)是在載入和對(duì)BeanDefinition做解析的這個(gè)過程。可以從DefaultListableBeanFactory來入手看看IoC容器是怎樣完成BeanDefinition載入的。在refresh調(diào)用完成以后,可以看到loadDefinition的調(diào)用:Java代碼public

abstract

class

AbstractXmlApplicationContext

extends

AbstractRefreshableConfigApplicationContext

{

public

AbstractXmlApplicationContext()

{

}

public

AbstractXmlApplicationContext(ApplicationContext

parent)

{

super(parent);

}

//這里是實(shí)現(xiàn)loadBeanDefinitions的地方

protected

void

loadBeanDefinitions(DefaultListableBeanFactory

beanFactory)

throws

IOException

{

//

Create

a

new

XmlBeanDefinitionReader

for

the

given

BeanFactory.

//

創(chuàng)建

XmlBeanDefinitionReader,并通過回調(diào)設(shè)置到

BeanFactory中去,創(chuàng)建BeanFactory的使用的也是

DefaultListableBeanFactory。

XmlBeanDefinitionReader

beanDefinitionReader

=

new

XmlBeanDefinitionReader(beanFactory);

//

Configure

the

bean

definition

reader

with

this

context's

//

resource

loading

environment.

//

這里設(shè)置

XmlBeanDefinitionReader,

為XmlBeanDefinitionReader

配置ResourceLoader,因?yàn)镈efaultResourceLoader是父類,所以this可以直接被使用

beanDefinitionReader.setResourceLoader(this);

beanDefinitionReader.setEntityResolver(new

ResourceEntityResolver(this));

//

Allow

a

subclass

to

provide

custom

initialization

of

the

reader,

//

then

proceed

with

actually

loading

the

bean

definitions.

//

這是啟動(dòng)Bean定義信息載入的過程

initBeanDefinitionReader(beanDefinitionReader);

loadBeanDefinitions(beanDefinitionReader);

}

protected

void

initBeanDefinitionReader(XmlBeanDefinitionReader

beanDefinitionReader)

{

}

這里使用XmlBeanDefinitionReader來載入BeanDefinition到容器中,如以下代碼清單所示:Java代碼

//這里是調(diào)用的入口。

public

int

loadBeanDefinitions(Resource

resource)

throws

BeanDefinitionStoreException

{

return

loadBeanDefinitions(new

EncodedResource(resource));

}

//這里是載入XML形式的BeanDefinition的地方。

public

int

loadBeanDefinitions(EncodedResource

encodedResource)

throws

BeanDefinitionStoreException

{

Assert.notNull(encodedResource,

"EncodedResource

must

not

be

null");

if

(logger.isInfoEnabled())

{

("Loading

XML

bean

definitions

from

"

+

encodedResource.getResource());

}

Set<EncodedResource>

currentResources

=

this.resourcesCurrentlyBeingLoaded.get();

if

(currentResources

==

null)

{

currentResources

=

new

HashSet<EncodedResource>(4);

this.resourcesCurrentlyBeingLoaded.set(currentResources);

}

if

(!currentResources.add(encodedResource))

{

throw

new

BeanDefinitionStoreException(

"Detected

recursive

loading

of

"

+

encodedResource

+

"

-

check

your

import

definitions!");

}

//這里得到XML文件,并得到IO的InputSource準(zhǔn)備進(jìn)行讀取。

try

{

InputStream

inputStream

=

encodedResource.getResource().getInputStream();

try

{

InputSource

inputSource

=

new

InputSource(inputStream);

if

(encodedResource.getEncoding()

!=

null)

{

inputSource.setEncoding(encodedResource.getEncoding());

}

return

doLoadBeanDefinitions(inputSource,

encodedResource.getResource());

}

finally

{

inputStream.close();

}

}

catch

(IOException

ex)

{

throw

new

BeanDefinitionStoreException(

"IOException

parsing

XML

document

from

"

+

encodedResource.getResource(),

ex);

}

finally

{

currentResources.remove(encodedResource);

if

(currentResources.isEmpty())

{

this.resourcesCurrentlyBeingLoaded.set(null);

}

}

}

//具體的讀取過程可以在doLoadBeanDefinitions方法中找到:

//這是從特定的XML文件中實(shí)際載入BeanDefinition的地方

protected

int

doLoadBeanDefinitions(InputSource

inputSource,

Resource

resource)

throws

BeanDefinitionStoreException

{

try

{

int

validationMode

=

getValidationModeForResource(resource);

//這里取得XML文件的Document對(duì)象,這個(gè)解析過程是由

documentLoader完成的,這個(gè)documentLoader是DefaultDocumentLoader,在定義documentLoader的地方創(chuàng)建

Document

doc

=

this.documentLoader.loadDocument(

inputSource,

getEntityResolver(),

this.errorHandler,

validationMode,

isNamespaceAware());

//這里啟動(dòng)的是對(duì)BeanDefinition解析的詳細(xì)過程,這個(gè)解析會(huì)使用到Spring的Bean配置規(guī)則,是我們下面需要詳細(xì)關(guān)注的地方。

return

registerBeanDefinitions(doc,

resource);

}

catch

(BeanDefinitionStoreException

ex)

{

throw

ex;

}

catch

(SAXParseException

ex)

{

throw

new

XmlBeanDefinitionStoreException(resource.getDescription(),

"Line

"

+

ex.getLineNumber()

+

"

in

XML

document

from

"

+

resource

+

"

is

invalid",

ex);

}

catch

(SAXException

ex)

{

throw

new

XmlBeanDefinitionStoreException(resource.getDescription(),

"XML

document

from

"

+

resource

+

"

is

invalid",

ex);

}

catch

(ParserConfigurationException

ex)

{

throw

new

BeanDefinitionStoreException(resource.getDescription(),

"Parser

configuration

exception

parsing

XML

from

"

+

resource,

ex);

}

catch

(IOException

ex)

{

throw

new

BeanDefinitionStoreException(resource.getDescription(),

"IOException

parsing

XML

document

from

"

+

resource,

ex);

}

catch

(Throwable

ex)

{

throw

new

BeanDefinitionStoreException(resource.getDescription(),

"Unexpected

exception

parsing

XML

document

from

"

+

resource,

ex);

}

}

關(guān)于具體的SpringBeanDefinition的解析,是在BeanDefinitionParserDelegate中完成的。這個(gè)類里包含了各種SpringBean定義規(guī)則的處理,感興趣的同學(xué)可以仔細(xì)研究。我們舉一個(gè)例子來分析這個(gè)處理過程,比如我們最熟悉的對(duì)Bean元素的處理是怎樣完成的,也就是我們?cè)赬ML定義文件中出現(xiàn)的<bean></bean>這個(gè)最常見的元素信息是怎樣被處理的。在這里,我們會(huì)看到那些熟悉的BeanDefinition定義的處理,比如id、name、aliase等屬性元素。把這些元素的值從XML文件相應(yīng)的元素的屬性中讀取出來以后,會(huì)被設(shè)置到生成的BeanDefinitionHolder中去。這些屬性的解析還是比較簡單的。對(duì)于其他元素配置的解析,比如各種Bean的屬性配置,通過一個(gè)較為復(fù)雜的解析過程,這個(gè)過程是由parseBeanDefinitionElement來完成的。解析完成以后,會(huì)把解析結(jié)果放到BeanDefinition對(duì)象中并設(shè)置到BeanDefinitionHolder中去,如以下清單所示:Java代碼public

BeanDefinitionHolder

parseBeanDefinitionElement(Element

ele,

BeanDefinition

containingBean)

{

//這里取得在<bean>元素中定義的id、name和aliase屬性的值

String

id

=

ele.getAttribute(ID_ATTRIBUTE);

String

nameAttr

=

ele.getAttribute(NAME_ATTRIBUTE);

List<String>

aliases

=

new

ArrayList<String>();

if

(StringUtils.hasLength(nameAttr))

{

String[]

nameArr

=

StringUtils.tokenizeToStringArray(nameAttr,

BEAN_NAME_DELIMITERS);

aliases.addAll(Arrays.asList(nameArr));

}

String

beanName

=

id;

if

(!StringUtils.hasText(beanName)

&&

!aliases.isEmpty())

{

beanName

=

aliases.remove(0);

if

(logger.isDebugEnabled())

{

logger.debug("No

XML

'id'

specified

-

using

'"

+

beanName

+

"'

as

bean

name

and

"

+

aliases

+

"

as

aliases");

}

}

if

(containingBean

==

null)

{

checkNameUniqueness(beanName,

aliases,

ele);

}

//這個(gè)方法會(huì)引發(fā)對(duì)bean元素的詳細(xì)解析

AbstractBeanDefinition

beanDefinition

=

parseBeanDefinitionElement(ele,

beanName,

containingBean);

if

(beanDefinition

!=

null)

{

if

(!StringUtils.hasText(beanName))

{

try

{

if

(containingBean

!=

null)

{

beanName

=

BeanDefinitionReaderUtils.generateBeanName(

beanDefinition,

this.readerContext.getRegistry(),

true);

}

else

{

beanName

=

this.readerContext.generateBeanName(beanDefinition);

//

Register

an

alias

for

the

plain

bean

class

name,

if

still

possible,

//

if

the

generator

returned

the

class

name

plus

a

suffix.

//

This

is

expected

for

Spring

1.2/2.0

backwards

compatibility.

String

beanClassName

=

beanDefinition.getBeanClassName();

if

(beanClassName

!=

null

&&

beanName.startsWith(beanClassName)

&&

beanName.length()

>

beanClassName.length()

&&

!this.readerContext.getRegistry().isBeanNameInUse(beanClassName))

{

aliases.add(beanClassName);

}

}

if

(logger.isDebugEnabled())

{

logger.debug("Neither

XML

'id'

nor

'name'

specified

-

"

+

"using

generated

bean

name

["

+

beanName

+

"]");

}

}

catch

(Exception

ex)

{

error(ex.getMessage(),

ele);

return

null;

}

}

String[]

aliasesArray

=

StringUtils.toStringArray(aliases);

return

new

BeanDefinitionHolder(beanDefinition,

beanName,

aliasesArray);

}

return

null;

}

在具體生成BeanDefinition以后。我們舉一個(gè)對(duì)property進(jìn)行解析的例子來完成對(duì)整個(gè)BeanDefinition載入過程的分析,還是在類BeanDefinitionParserDelegate的代碼中,它對(duì)BeanDefinition中的定義一層一層地進(jìn)行解析,比如從屬性元素集合到具體的每一個(gè)屬性元素,然后才是對(duì)具體的屬性值的處理。根據(jù)解析結(jié)果,對(duì)這些屬性值的處理會(huì)封裝成PropertyValue對(duì)象并設(shè)置到BeanDefinition對(duì)象中去,如以下代碼清單所示。Java代碼/**

*

這里對(duì)指定bean元素的property子元素集合進(jìn)行解析。

*/

public

void

parsePropertyElements(Element

beanEle,

BeanDefinition

bd)

{

//遍歷所有bean元素下定義的property元素

NodeList

nl

=

beanEle.getChildNodes();

for

(int

i

=

0;

i

<

nl.getLength();

i++)

{

Node

node

=

nl.item(i);

if

(node

instanceof

Element

&&

DomUtils.nodeNameEquals(node,

PROPERTY_ELEMENT))

{

//在判斷是property元素后對(duì)該property元素進(jìn)行解析的過程

parsePropertyElement((Element)

node,

bd);

}

}

}

public

void

parsePropertyElement(Element

ele,

BeanDefinition

bd)

{

//這里取得property的名字

String

propertyName

=

ele.getAttribute(NAME_ATTRIBUTE);

if

(!StringUtils.hasLength(propertyName))

{

error("Tag

'property'

must

have

a

'name'

attribute",

ele);

return;

}

this.parseState.push(new

PropertyEntry(propertyName));

try

{

//如果同一個(gè)bean中已經(jīng)有同名的存在,則不進(jìn)行解析,直接返回。也就是說,如果在同一個(gè)bean中有同名的property設(shè)置,那么起作用的只是第一個(gè)。

if

(bd.getPropertyValues().contains(propertyName))

{

error("Multiple

'property'

definitions

for

property

'"

+

propertyName

+

"'",

ele);

return;

}

//這里是解析property值的地方,返回的對(duì)象對(duì)應(yīng)對(duì)Bean定義的property屬性設(shè)置的解析結(jié)果,這個(gè)解析結(jié)果會(huì)封裝到PropertyValue對(duì)象中,然后設(shè)置到BeanDefinitionHolder中去。

Object

val

=

parsePropertyValue(ele,

bd,

propertyName);

PropertyValue

pv

=

new

PropertyValue(propertyName,

val);

parseMetaElements(ele,

pv);

pv.setSource(extractSource(ele));

bd.getPropertyValues().addPropertyValue(pv);

}

finally

{

this.parseState.pop();

}

}

/**

*

這里取得property元素的值,也許是一個(gè)list或其他。

*/

public

Object

parsePropertyValue(Element

ele,

BeanDefinition

bd,

String

propertyName)

{

String

elementName

=

(propertyName

!=

null)

?

"<property>

element

for

property

'"

+

propertyName

+

"'"

:

"<constructor-arg>

element";

//

Should

only

have

one

child

element:

ref,

value,

list,

etc.

NodeList

nl

=

ele.getChildNodes();

Element

subElement

=

null;

for

(int

i

=

0;

i

<

nl.getLength();

i++)

{

Node

node

=

nl.item(i);

if

(node

instanceof

Element

&&

!DomUtils.nodeNameEquals(node,

DESCRIPTION_ELEMENT)

&&

!DomUtils.nodeNameEquals(node,

META_ELEMENT))

{

//

Child

element

is

what

we're

looking

for.

if

(subElement

!=

null)

{

error(elementName

+

"

must

not

contain

more

than

one

sub-element",

ele);

}

else

{

subElement

=

(Element)

node;

}

}

}

//這里判斷property的屬性,是ref還是value,不允許同時(shí)是ref和value。

boolean

hasRefAttribute

=

ele.hasAttribute(REF_ATTRIBUTE);

boolean

hasValueAttribute

=

ele.hasAttribute(VALUE_ATTRIBUTE);

if

((hasRefAttribute

&&

hasValueAttribute)

||

((hasRefAttribute

||

hasValueAttribute)

&&

subElement

!=

null))

{

error(elementName

+

"

is

only

allowed

to

contain

either

'ref'

attribute

OR

'value'

attribute

OR

sub-element",

ele);

}

//如果是ref,創(chuàng)建一個(gè)ref的數(shù)據(jù)對(duì)象RuntimeBeanReference,這個(gè)對(duì)象封裝了ref的信息。

if

(hasRefAttribute)

{

String

refName

=

ele.getAttribute(REF_ATTRIBUTE);

if

(!StringUtils.hasText(refName))

{

error(elementName

+

"

contains

empty

'ref'

attribute",

ele);

}

RuntimeBeanReference

ref

=

new

RuntimeBeanReference(refName);

ref.setSource(extractSource(ele));

return

ref;

}

//如果是value,創(chuàng)建一個(gè)value的數(shù)據(jù)對(duì)象TypedStringValue

,這個(gè)對(duì)象封裝了value的信息。

else

if

(hasValueAttribute)

{

TypedStringValue

valueHolder

=

new

TypedStringValue(ele.getAttribute(VALUE_ATTRIBUTE));

valueHolder.setSource(extractSource(ele));

return

valueHolder;

}

//如果還有子元素,觸發(fā)對(duì)子元素的解析

else

if

(subElement

!=

null)

{

return

parsePropertySubElement(subElement,

bd);

}

else

{

//

Neither

child

element

nor

"ref"

or

"value"

attribute

found.

error(elementName

+

"

must

specify

a

ref

or

value",

ele);

return

null;

}

}

比如,再往下看,我們看到像List這樣的屬性配置是怎樣被解析的,依然在BeanDefinitionParserDelegate中:返回的是一個(gè)List對(duì)象,這個(gè)List是Spring定義的ManagedList,作為封裝List這類配置定義的數(shù)據(jù)封裝,如以下代碼清單所示。Java代碼public

List

parseListElement(Element

collectionEle,

BeanDefinition

bd)

{

String

defaultElementType

=

collectionEle.getAttribute(VALUE_TYPE_ATTRIBUTE);

NodeList

nl

=

collectionEle.getChildNodes();

ManagedList<Object>

target

=

new

ManagedList<Object>(nl.getLength());

target.setSource(extractSource(collectionEle));

target.setElementTypeName(defaultElementType);

target.setMergeEnabled(parseMergeAttribute(collectionEle));

//具體的List元素的解析過程。

parseCollectionElements(nl,

target,

bd,

defaultElementType);

return

target;

}

protected

void

parseCollectionElements(

NodeList

elementNodes,

Collection<Object>

target,

BeanDefinition

bd,

String

defaultElementType)

{

//遍歷所有的元素節(jié)點(diǎn),并判斷其類型是否為Element。

for

(int

i

=

0;

i

<

elementNodes.getLength();

i++)

{

Node

node

=

elementNodes.item(i);

if

(node

instanceof

Element

&&

!DomUtils.nodeNameEquals(node,

DESCRIPTION_ELEMENT))

{

//加入到target中去,target是一個(gè)ManagedList,同時(shí)觸發(fā)對(duì)下一層子元素的解析過程,這是一個(gè)遞歸的調(diào)用。

target.add(parsePropertySubElement((Element)

node,

bd,

defaultElementType));

}

}

}

經(jīng)過這樣一層一層的解析,我們?cè)赬ML文件中定義的BeanDefinition就被整個(gè)給載入到了IoC容器中,并在容器中建立了數(shù)據(jù)映射。在IoC容器中建立了對(duì)應(yīng)的數(shù)據(jù)結(jié)構(gòu),或者說可以看成是POJO對(duì)象在IoC容器中的映像,這些數(shù)據(jù)結(jié)構(gòu)可以以AbstractBeanDefinition為入口,讓IoC容器執(zhí)行索引、查詢和操作。

在我的感覺中,對(duì)核心數(shù)據(jù)結(jié)構(gòu)的定義和處理應(yīng)該可以看成是一個(gè)軟件的核心部分了。所以,這里的BeanDefinition的載入可以說是IoC容器的核心,如果說IoC容器是Spring的核心,那么這些BeanDefinition就是Spring的核心的核心了!

呵呵,這部分代碼數(shù)量不小,但如果掌握這條主線,其他都可以舉一反三吧,就像我們掌握了操作系統(tǒng)啟動(dòng)的過程,以及在操作系統(tǒng)設(shè)計(jì)中的核心數(shù)據(jù)結(jié)構(gòu)像進(jìn)程數(shù)據(jù)結(jié)構(gòu),文件系統(tǒng)數(shù)據(jù)結(jié)構(gòu),網(wǎng)絡(luò)協(xié)議數(shù)據(jù)結(jié)構(gòu)的設(shè)計(jì)和處理一樣,對(duì)整個(gè)系統(tǒng)的設(shè)計(jì)原理,包括移植,驅(qū)動(dòng)開發(fā)和應(yīng)用開發(fā),是非常有幫助的!深入解析Spring架構(gòu)與設(shè)計(jì)原理(二)AOP關(guān)于AOP的個(gè)人理解

AOP聯(lián)盟定義的AOP體系結(jié)構(gòu)把與AOP相關(guān)的概念大致分為了由高到低、從使用到實(shí)現(xiàn)的三個(gè)層次。關(guān)于這個(gè)體系結(jié)構(gòu),個(gè)人的理解是這樣的,從上往下,最高層是語言和開發(fā)環(huán)境,在這個(gè)環(huán)境中可以看到幾個(gè)重要的概念:base可以視為待增強(qiáng)對(duì)象,或者說目標(biāo)對(duì)象;aspect指切面,通常包含對(duì)于base的增強(qiáng)應(yīng)用;configuration可以看成是一種編織或者說配置,通過在AOP體系中提供這個(gè)configuration配置環(huán)境,可以把base和aspect結(jié)合起來,從而完成切面對(duì)目標(biāo)對(duì)象的編織實(shí)現(xiàn)。

對(duì)Spring平臺(tái)或者說生態(tài)系統(tǒng)來說,AOP是Spring框架的核心功能模塊之一。AOP與IOC容器的結(jié)合使用,為應(yīng)用開發(fā)或者Spring自身功能的擴(kuò)展都提供了許多便利。SpringAOP的實(shí)現(xiàn)和其他特性的實(shí)現(xiàn)一樣,非常豐富,除了可以使用Spring本身提供的AOP實(shí)現(xiàn)之外,還封裝了業(yè)界優(yōu)秀的AOP解決方案AspectJ來讓應(yīng)用使用。在這里,主要對(duì)Spring自身的AOP實(shí)現(xiàn)原理做一些解析;在這個(gè)AOP實(shí)現(xiàn)中,Spring充分利用了IOC容器Proxy代理對(duì)象以及AOP攔截器的功能特性,通過這些對(duì)AOP基本功能的封裝機(jī)制,為用戶提供了AOP的實(shí)現(xiàn)框架。所以,要了解這些AOP的基本實(shí)現(xiàn),需要我們對(duì)Java的Proxy機(jī)制有一些基本了解。

AOP實(shí)現(xiàn)的基本線索

AOP實(shí)現(xiàn)中,可以看到三個(gè)主要的步驟,一個(gè)是代理對(duì)象的生成,然后是攔截器的作用,然后是Aspect編織的實(shí)現(xiàn)。AOP框架的豐富,很大程度體現(xiàn)在這三個(gè)具體實(shí)現(xiàn)中,所具有的豐富的技術(shù)選擇,以及如何實(shí)現(xiàn)與IOC容器的無縫結(jié)合。畢竟這也是一個(gè)非常核心的模塊,需要滿足不同的應(yīng)用需求帶來的解決方案需求。

在SpringAOP的實(shí)現(xiàn)原理中,我們主要舉ProxyFactoryBean的實(shí)現(xiàn)作為例子和實(shí)現(xiàn)的基本線索進(jìn)行分析;很大一個(gè)原因,是因?yàn)镻roxyFactoryBean是在SpringIoC環(huán)境中,創(chuàng)建AOP應(yīng)用的最底層方法,從中,可以看到一條實(shí)現(xiàn)AOP的基本線索。在ProxyFactoryBean中,它的AOP實(shí)現(xiàn)需要依賴JDK或者CGLIB提供的Proxy特性。從FactoryBean中獲取對(duì)象,是從getObject()方法作為入口完成的。然后為proxy代理對(duì)象配置advisor鏈,這個(gè)配置是在initializeAdvisorChain方法中完成的;然后就為生成AOP代理對(duì)象做好了準(zhǔn)備,生成代理對(duì)象如下所示:Java代碼private

synchronized

Object

getSingletonInstance()

{

if

(this.singletonInstance

==

null)

{

this.targetSource

=

freshTargetSource();

if

(this.autodetectInterfaces

&&

getProxiedInterfaces().length

==

0

&&

!isProxyTargetClass())

{

//

Rely

on

AOP

infrastructure

to

tell

us

what

interfaces

to

proxy.

Class

targetClass

=

getTargetClass();

if

(targetClass

==

null)

{

throw

new

FactoryBeanNotInitializedException("Cannot

determine

target

class

for

proxy");

}

//

這里設(shè)置代理對(duì)象的接口

setInterfaces(ClassUtils.getAllInterfacesForClass(targetClass,

xyClassLoader));

}

//

Initialize

the

shared

singleton

instance.

super.setFrozen(this.freezeProxy);

//

注意這里的方法會(huì)使用ProxyFactory來生成我們需要的Proxy

this.singletonInstance

=

getProxy(createAopProxy());

}

return

this.singletonInstance;

}

//使用createAopProxy返回的AopProxy來得到代理對(duì)象

protected

Object

getProxy(AopProxy

aopProxy)

{

return

aopProxy.getProxy(xyClassLoader);

}

上面我們看到了在Spring中通過ProxyFactoryBean實(shí)現(xiàn)AOP功能的第一步,得到AopProxy代理對(duì)象的基本過程,下面我們看看AopProxy代理對(duì)象的攔截機(jī)制是怎樣發(fā)揮作用,是怎樣實(shí)現(xiàn)AOP功能的。我們知道,對(duì)代理對(duì)象的生成,有CGLIB和JDK兩種生成方式,在CGLIB中,對(duì)攔截器設(shè)計(jì)是通過在Cglib2AopProxy的AopProxy代理對(duì)象生成的時(shí)候,在回調(diào)DynamicAdvisedInterceptor對(duì)象中實(shí)現(xiàn)的,這個(gè)回調(diào)的實(shí)現(xiàn)在intercept方法中完成。對(duì)于AOP是怎樣完成對(duì)目標(biāo)對(duì)象的增強(qiáng)的,這些實(shí)現(xiàn)是封裝在AOP攔截器鏈中,由一個(gè)個(gè)具體的攔截器來完成的。具體攔截器的運(yùn)行是在以下的代碼實(shí)現(xiàn)中完成的,這些調(diào)用在ReflectiveMethodInvocation中。Java代碼public

Object

proceed()

throws

Throwable

{

//

We

start

with

an

index

of

-1

and

increment

early.

//如果攔截器鏈中的攔截器迭代調(diào)用完畢,這里開始調(diào)用target的函數(shù),這個(gè)函數(shù)是通過反射機(jī)制完成的,具體實(shí)現(xiàn)在:AopUtils.invokeJoinpointUsingReflection方法里面。

if

(this.currentInterceptorIndex

==

erceptorsAndDynamicMethodMatchers.size()

-

1)

{

return

invokeJoinpoint();

}

//這里沿著定義好的

interceptorOrInterceptionAdvice鏈進(jìn)行處理。

Object

interceptorOrInterceptionAdvice

=

erceptorsAndDynamicMethodMatchers.get(++this.currentInterceptorIndex);

if

(interceptorOrInterceptionAdvice

instanceof

InterceptorAndDynamicMethodMatcher)

{

//

Evaluate

dynamic

method

matcher

here:

static

part

will

already

have

//

been

evaluated

and

found

to

match.

//這里對(duì)攔截器進(jìn)行動(dòng)態(tài)匹配的的判斷,還記得我們前面分析的pointcut嗎?這里是觸發(fā)進(jìn)行匹配的地方,如果和定義的pointcut匹配,那么這個(gè)advice將會(huì)得到執(zhí)行。

InterceptorAndDynamicMethodMatcher

dm

=

(InterceptorAndDynamicMethodMatcher)

interceptorOrInterceptionAdvice;

if

(dm.methodMatcher.matches(this.method,

this.targetClass,

this.arguments))

{

return

erceptor.invoke(this);

}

else

{

//

Dynamic

matching

failed.

//

Skip

this

interceptor

and

invoke

the

next

in

the

chain.

//

//如果不匹配,那么這個(gè)proceed會(huì)被遞歸調(diào)用,直到所有的攔截器都被運(yùn)行過為止。

return

proceed();

}

}

else

{

//

It's

an

interceptor,

so

we

just

invoke

it:

The

pointcut

will

have

//

been

evaluated

statically

before

this

object

was

constructed.

//如果是一個(gè)interceptor,直接調(diào)用這個(gè)interceptor對(duì)應(yīng)的方法

return((MethodInterceptor)

interceptorOrInterceptionAdvice).invoke(this);

}

}

在調(diào)用攔截器的時(shí)候,我們接下去就可以看到對(duì)advice的通知的調(diào)用。而經(jīng)過一系列的注冊(cè),適配的過程以后,攔截器在攔截的時(shí)候,會(huì)調(diào)用到預(yù)置好的一個(gè)通知適配器,設(shè)置通知攔截器,這是一系列Spring設(shè)計(jì)好為通知服務(wù)的類的一個(gè),是最終完成通知攔截和實(shí)現(xiàn)的地方,非常的關(guān)鍵。比如,對(duì)MethodBeforeAdviceInterceptor的實(shí)現(xiàn)是這樣的:Java代碼public

class

MethodBeforeAdviceInterceptor

implements

MethodInterceptor,

Serializable

{

private

MethodBeforeAdvice

advice;

/**

*

Create

a

new

MethodBeforeAdviceInterceptor

for

the

given

advice.

*

@param

advice

the

MethodBeforeAdvice

to

wrap

*/

public

MethodBeforeAdviceInterceptor(MethodBeforeAdvice

advice)

{

Assert.notNull(advice,

"Advice

must

not

be

null");

this.advice

=

advice;

}

//這個(gè)invoke方法是攔截器的回調(diào)方法,會(huì)在代理對(duì)象的方法被調(diào)用的時(shí)候觸發(fā)回調(diào)。

public

Object

invoke(MethodInvocation

mi)

throws

Throwable

{

this.advice.before(mi.getMethod(),

mi.getArguments(),

mi.getThis()

);

return

ceed();

}

}

在代碼中,可以看到,就是這里,會(huì)調(diào)用advice的before方法!這樣就成功的完成了before通知的編織!

因?yàn)镾pringAOP本身并不打算成為一個(gè)一統(tǒng)天下的AOP框架,秉持Spring的一貫設(shè)計(jì)理念,設(shè)想中的Spring設(shè)計(jì)目標(biāo)應(yīng)該是,致力于AOP框架與IOC容器的緊密集成,通過集成AOP技術(shù)為JavaEE應(yīng)用開發(fā)中遇到的普遍問題提供解決方案,從而為AOP用戶使用AOP技術(shù)提供最大的便利,從這個(gè)角度上為JavaEE的應(yīng)用開發(fā)人員服務(wù)。在沒有使用第三方AOP解決方案的時(shí)候,Spring通過虛擬機(jī)的Proxy特性和CGLIB實(shí)現(xiàn)了AOP的基本功能,我想,如果有了SpringAOP實(shí)現(xiàn)原理的知識(shí)背景,再加上我們對(duì)源代碼實(shí)現(xiàn)的認(rèn)真解讀,可以為我們了解其他AOP框架與IOC容器的集成原理,也打下了很好的基礎(chǔ),并真正了解一個(gè)AOP框架是在怎樣實(shí)現(xiàn)的。

這還真是就是我們喜歡開源軟件一個(gè)原因,有了源代碼,軟件就沒有什么神秘的面紗了!本立而道生,多讀源代碼吧,或者找一本從源代碼出發(fā)講解軟件實(shí)現(xiàn)的書來看看,就像以前我們學(xué)習(xí)操作系統(tǒng),學(xué)習(xí)TCP/IP那樣!一定會(huì)有長進(jìn)的。深入解析Spring架構(gòu)與設(shè)計(jì)原理(三)數(shù)據(jù)庫的操作實(shí)現(xiàn)最近事情實(shí)在是比較多,沒有及時(shí)更新帖子,還望大家見諒啊。今天,一起討論討論SpringJDBC的實(shí)現(xiàn)吧。

關(guān)于SpringJDBC

還是從SpringJDBC說起吧,雖然現(xiàn)在應(yīng)用很多都是直接使用Hibernate或者其他的ORM工具。但JDBC畢竟還是很基本的,其中的JdbcTemplate就是我們經(jīng)常使用的,比如JDBCTemplate的execute方法,就是一個(gè)基本的方法,在這個(gè)方法的實(shí)現(xiàn)中,可以看到對(duì)數(shù)據(jù)庫操作的基本過程。Java代碼//execute方法執(zhí)行的是輸入的sql語句

public

void

execute(final

String

sql)

throws

DataAccessException

{

if

(logger.isDebugEnabled())

{

logger.debug("Executing

SQL

statement

["

+

sql

+

"]");

}

class

ExecuteStatementCallback

implements

StatementCallback<Object>,

SqlProvider

{

public

Object

doInStatement(Statement

stmt)

throws

SQLException

{

stmt.execute(sql);

return

null;

}

public

String

getSql()

{

return

sql;

}

}

execut

溫馨提示

  • 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. 人人文庫網(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)論