(轉(zhuǎn)發(fā))9種設計模式在Spring中的運用,一定要非常熟練噩凹!

bean容器的啟動階段:

讀取bean的xml配置文件,將bean元素分別轉(zhuǎn)換成一個BeanDefinition對象巴元。

然后通過BeanDefinitionRegistry將這些bean注冊到beanFactory中,保存在它的一個ConcurrentHashMap中驮宴。

將BeanDefinition注冊到了beanFactory之后逮刨,在這里Spring為我們提供了一個擴展的切口,允許我們通過實現(xiàn)接口BeanFactoryPostProcessor 在此處來插入我們定義的代碼堵泽。

典型的例子就是:PropertyPlaceholderConfigurer修己,我們一般在配置數(shù)據(jù)庫的dataSource時使用到的占位符的值,就是它注入進去的迎罗。

容器中bean的實例化階段:

實例化階段主要是通過反射或者CGLIB對bean進行實例化睬愤,在這個階段Spring又給我們暴露了很多的擴展點:

各種的Aware接口,比如 BeanFactoryAware纹安,對于實現(xiàn)了這些Aware接口的bean尤辱,在實例化bean時Spring會幫我們注入對應的BeanFactory的實例。

BeanPostProcessor接口钻蔑,實現(xiàn)了BeanPostProcessor接口的bean啥刻,在實例化bean時Spring會幫我們調(diào)用接口中的方法。

InitializingBean接口咪笑,實現(xiàn)了InitializingBean接口的bean可帽,在實例化bean時Spring會幫我們調(diào)用接口中的方法。

DisposableBean接口窗怒,實現(xiàn)了BeanPostProcessor接口的bean映跟,在該bean死亡時Spring會幫我們調(diào)用接口中的方法蓄拣。

設計意義:

松耦合。可以將原來硬編碼的依賴努隙,通過Spring這個beanFactory這個工廠來注入依賴球恤,也就是說原來只有依賴方和被依賴方,現(xiàn)在我們引入了第三方——spring這個beanFactory荸镊,由它來解決bean之間的依賴問題咽斧,達到了松耦合的效果.

bean的額外處理。通過Spring接口的暴露躬存,在實例化bean的階段我們可以進行一些額外的處理张惹,這些額外的處理只需要讓bean實現(xiàn)對應的接口即可,那么spring就會在bean的生命周期調(diào)用我們實現(xiàn)的接口來處理該bean岭洲。[非常重要]

2.工廠方法

實現(xiàn)方式:

FactoryBean接口宛逗。

實現(xiàn)原理:

實現(xiàn)了FactoryBean接口的bean是一類叫做factory的bean。其特點是盾剩,spring會在使用getBean()調(diào)用獲得該bean時雷激,會自動調(diào)用該bean的getObject()方法,所以返回的不是factory這個bean告私,而是這個bean.getOjbect()方法的返回值屎暇。

例子:

典型的例子有spring與mybatis的結(jié)合。

代碼示例:

說明:

我們看上面該bean德挣,因為實現(xiàn)了FactoryBean接口恭垦,所以返回的不是 SqlSessionFactoryBean 的實例,而是它的 SqlSessionFactoryBean.getObject() 的返回值格嗅。

擴展:設計模式是什么鬼(工廠方法)

3.單例模式

Spring依賴注入Bean實例默認是單例的番挺。

Spring的依賴注入(包括lazy-init方式)都是發(fā)生在AbstractBeanFactory的getBean里。getBean的doGetBean方法調(diào)用getSingleton進行bean的創(chuàng)建屯掖。

分析getSingleton()方法

publicObject?getSingleton(String?beanName){

//參數(shù)true設置標識允許早期依賴

returngetSingleton(beanName,true);

}

protectedObject?getSingleton(String?beanName,?boolean?allowEarlyReference)?{

//檢查緩存中是否存在實例

Object?singletonObject?=this.singletonObjects.get(beanName);

if(singletonObject?==null&&?isSingletonCurrentlyInCreation(beanName))?{

//如果為空玄柏,則鎖定全局變量并進行處理。

synchronized?(this.singletonObjects)?{

//如果此bean正在加載贴铜,則不處理

singletonObject?=this.earlySingletonObjects.get(beanName);

if(singletonObject?==null&&?allowEarlyReference)?{

//當某些方法需要提前初始化的時候則會調(diào)用addSingleFactory?方法將對應的ObjectFactory初始化策略存儲在singletonFactories

ObjectFactory?singletonFactory?=this.singletonFactories.get(beanName);

if(singletonFactory?!=null)?{

//調(diào)用預先設定的getObject方法

singletonObject?=?singletonFactory.getObject();

//記錄在緩存中粪摘,earlysingletonObjects和singletonFactories互斥

this.earlySingletonObjects.put(beanName,?singletonObject);

this.singletonFactories.remove(beanName);

}

}

}

}

return(singletonObject?!=?NULL_OBJECT???singletonObject?:null);

}

getSingleton()過程圖

ps:spring依賴注入時,使用了 雙重判斷加鎖 的單例模式

總結(jié)

單例模式定義:保證一個類僅有一個實例绍坝,并提供一個訪問它的全局訪問點徘意。

spring對單例的實現(xiàn):spring中的單例模式完成了后半句話,即提供了全局的訪問點BeanFactory轩褐。但沒有從構(gòu)造器級別去控制單例椎咧,這是因為spring管理的是任意的java對象。

擴展:設計模式是什么鬼(單例)

4.適配器模式

實現(xiàn)方式:

SpringMVC中的適配器HandlerAdatper。

實現(xiàn)原理:

HandlerAdatper根據(jù)Handler規(guī)則執(zhí)行不同的Handler勤讽。

實現(xiàn)過程:

DispatcherServlet根據(jù)HandlerMapping返回的handler蟋座,向HandlerAdatper發(fā)起請求,處理Handler脚牍。

HandlerAdapter根據(jù)規(guī)則找到對應的Handler并讓其執(zhí)行向臀,執(zhí)行完畢后Handler會向HandlerAdapter返回一個ModelAndView,最后由HandlerAdapter向DispatchServelet返回一個ModelAndView诸狭。

實現(xiàn)意義:

HandlerAdatper使得Handler的擴展變得容易券膀,只需要增加一個新的Handler和一個對應的HandlerAdapter即可。

因此Spring定義了一個適配接口驯遇,使得每一種Controller有一種對應的適配器實現(xiàn)類三娩,讓適配器代替controller執(zhí)行相應的方法。這樣在擴展Controller時妹懒,只需要增加一個適配器類就完成了SpringMVC的擴展了。

擴展:設計模式是什么鬼(適配器)

5.裝飾器模式

實現(xiàn)方式:

Spring中用到的包裝器模式在類名上有兩種表現(xiàn):一種是類名中含有Wrapper双吆,另一種是類名中含有Decorator眨唬。

實質(zhì):

動態(tài)地給一個對象添加一些額外的職責。

就增加功能來說好乐,Decorator模式相比生成子類更為靈活匾竿。

擴展:設計模式是什么鬼(裝飾)

6.代理模式

實現(xiàn)方式:

AOP底層,就是動態(tài)代理模式的實現(xiàn)蔚万。

動態(tài)代理:

在內(nèi)存中構(gòu)建的岭妖,不需要手動編寫代理類

靜態(tài)代理:

需要手工編寫代理類,代理類引用被代理對象反璃。

實現(xiàn)原理:

切面在應用運行的時刻被織入昵慌。一般情況下,在織入切面時淮蜈,AOP容器會為目標對象創(chuàng)建動態(tài)的創(chuàng)建一個代理對象斋攀。SpringAOP就是以這種方式織入切面的。

織入:把切面應用到目標對象并創(chuàng)建新的代理對象的過程梧田。

擴展:設計模式是什么鬼(代理)

7.觀察者模式

實現(xiàn)方式:

spring的事件驅(qū)動模型使用的是 觀察者模式 淳蔼,Spring中Observer模式常用的地方是listener的實現(xiàn)。

具體實現(xiàn):

事件機制的實現(xiàn)需要三個部分,事件源,事件,事件監(jiān)聽器

ApplicationEvent抽象類[事件]

繼承自jdk的EventObject,所有的事件都需要繼承ApplicationEvent,并且通過構(gòu)造器參數(shù)source得到事件源.

該類的實現(xiàn)類ApplicationContextEvent表示ApplicaitonContext的容器事件.

代碼:

publicabstractclassApplicationEventextendsEventObject{

privatestaticfinallongserialVersionUID?=7099057708183571937L;

privatefinallongtimestamp;

publicApplicationEvent(Object?source){

super(source);

this.timestamp?=?System.currentTimeMillis();

}

publicfinallonggetTimestamp(){

returnthis.timestamp;

}

}

ApplicationListener接口[事件監(jiān)聽器]

繼承自jdk的EventListener,所有的監(jiān)聽器都要實現(xiàn)這個接口裁眯。

這個接口只有一個onApplicationEvent()方法,該方法接受一個ApplicationEvent或其子類對象作為參數(shù),在方法體中,可以通過不同對Event類的判斷來進行相應的處理鹉梨。

當事件觸發(fā)時所有的監(jiān)聽器都會收到消息。

代碼:

publicinterfaceApplicationListenerextendsEventListener{

voidonApplicationEvent(E?event);

}

ApplicationContext接口[事件源]

ApplicationContext是spring中的全局容器穿稳,翻譯過來是”應用上下文”存皂。

實現(xiàn)了ApplicationEventPublisher接口。

職責:

負責讀取bean的配置文檔,管理bean的加載,維護bean之間的依賴關(guān)系,可以說是負責bean的整個生命周期,再通俗一點就是我們平時所說的IOC容器司草。

代碼:

publicinterfaceApplicationEventPublisher{

voidpublishEvent(ApplicationEventevent);

}

publicvoidpublishEvent(ApplicationEventevent){

Assert.notNull(event,"Event?must?not?be?null");

if(logger.isTraceEnabled())?{

logger.trace("Publishing?event?in?"+?getDisplayName()?+":?"+event);

}

getApplicationEventMulticaster().multicastEvent(event);

if(this.parent?!=null)?{

this.parent.publishEvent(event);

}

}

ApplicationEventMulticaster抽象類[事件源中publishEvent方法需要調(diào)用其方法getApplicationEventMulticaster]

屬于事件廣播器,它的作用是把Applicationcontext發(fā)布的Event廣播給所有的監(jiān)聽器.

代碼:

publicabstractclassAbstractApplicationContextextendsDefaultResourceLoader

implementsConfigurableApplicationContext,DisposableBean

{

privateApplicationEventMulticaster?applicationEventMulticaster;

protectedvoidregisterListeners(){

//?Register?statically?specified?listeners?first.??

for(ApplicationListener?listener?:?getApplicationListeners())?{

getApplicationEventMulticaster().addApplicationListener(listener);

}

//?Do?not?initialize?FactoryBeans?here:?We?need?to?leave?all?regular?beans??

//?uninitialized?to?let?post-processors?apply?to?them!??

String[]?listenerBeanNames?=?getBeanNamesForType(ApplicationListener.class,true,false);

for(String?lisName?:?listenerBeanNames)?{

getApplicationEventMulticaster().addApplicationListenerBean(lisName);

}

}

}

擴展:設計模式是什么鬼(觀察者)

8.策略模式

實現(xiàn)方式:

Spring框架的資源訪問Resource接口艰垂。該接口提供了更強的資源訪問能力泡仗,Spring 框架本身大量使用了 Resource 接口來訪問底層資源。

Resource 接口介紹

source 接口是具體資源訪問策略的抽象猜憎,也是所有資源訪問類所實現(xiàn)的接口娩怎。

Resource 接口主要提供了如下幾個方法:

getInputStream():定位并打開資源,返回資源對應的輸入流胰柑。每次調(diào)用都返回新的輸入流截亦。調(diào)用者必須負責關(guān)閉輸入流。

exists():返回 Resource 所指向的資源是否存在柬讨。

isOpen():返回資源文件是否打開崩瓤,如果資源文件不能多次讀取,每次讀取結(jié)束應該顯式關(guān)閉踩官,以防止資源泄漏却桶。

getDescription():返回資源的描述信息,通常用于資源處理出錯時輸出該信息蔗牡,通常是全限定文件名或?qū)嶋H URL颖系。

getFile:返回資源對應的 File 對象。

getURL:返回資源對應的 URL 對象辩越。

最后兩個方法通常無須使用嘁扼,僅在通過簡單方式訪問無法實現(xiàn)時,Resource 提供傳統(tǒng)的資源訪問的功能黔攒。

Resource 接口本身沒有提供訪問任何底層資源的實現(xiàn)邏輯趁啸,針對不同的底層資源,Spring 將會提供不同的 Resource 實現(xiàn)類督惰,不同的實現(xiàn)類負責不同的資源訪問邏輯不傅。

Spring 為 Resource 接口提供了如下實現(xiàn)類:

UrlResource:訪問網(wǎng)絡資源的實現(xiàn)類。

ClassPathResource:訪問類加載路徑里資源的實現(xiàn)類姑丑。

FileSystemResource:訪問文件系統(tǒng)里資源的實現(xiàn)類蛤签。

ServletContextResource:訪問相對于 ServletContext 路徑里的資源的實現(xiàn)類.

InputStreamResource:訪問輸入流資源的實現(xiàn)類。

ByteArrayResource:訪問字節(jié)數(shù)組資源的實現(xiàn)類栅哀。

這些 Resource 實現(xiàn)類震肮,針對不同的的底層資源,提供了相應的資源訪問邏輯留拾,并提供便捷的包裝戳晌,以利于客戶端程序的資源訪問。

擴展:設計模式是什么鬼(策略)

9.模版方法模式

經(jīng)典模板方法定義:

父類定義了骨架(調(diào)用哪些方法及順序)痴柔,某些特定方法由子類實現(xiàn)沦偎。

最大的好處:代碼復用,減少重復代碼。除了子類要實現(xiàn)的特定方法豪嚎,其他方法及方法調(diào)用順序都在父類中預先寫好了搔驼。

所以父類模板方法中有兩類方法:

共同的方法:所有子類都會用到的代碼

不同的方法:子類要覆蓋的方法,分為兩種:

抽象方法:父類中的是抽象方法侈询,子類必須覆蓋

鉤子方法:父類中是一個空方法舌涨,子類繼承了默認也是空的

注:為什么叫鉤子,子類可以通過這個鉤子(方法)扔字,控制父類囊嘉,因為這個鉤子實際是父類的方法(空方法)!

Spring模板方法模式實質(zhì):

是模板方法模式和回調(diào)模式的結(jié)合革为,是Template Method不需要繼承的另一種實現(xiàn)方式扭粱。Spring幾乎所有的外接擴展都采用這種模式。

推薦:設計模式是什么鬼(模板方法)

具體實現(xiàn):

JDBC的抽象和對Hibernate的集成震檩,都采用了一種理念或者處理方式琢蛤,那就是模板方法模式與相應的Callback接口相結(jié)合。

采用模板方法模式是為了以一種統(tǒng)一而集中的方式來處理資源的獲取和釋放抛虏,以JdbcTempalte為例:

publicabstractclassJdbcTemplate{

publicfinalObjectexecute(Stringsql){

Connection?con=null;

Statement?stmt=null;

try{

con=getConnection();

stmt=con.createStatement();

ObjectretValue=executeWithStatement(stmt,sql);

returnretValue;

}catch(SQLException?e){

...

}finally{

closeStatement(stmt);

releaseConnection(con);

}

}

protectedabstractObjectexecuteWithStatement(Statement???stmt,Stringsql);

}

引入回調(diào)原因:

JdbcTemplate是抽象類虐块,不能夠獨立使用,我們每次進行數(shù)據(jù)訪問的時候都要給出一個相應的子類實現(xiàn),這樣肯定不方便嘉蕾,所以就引入了回調(diào)。

回調(diào)代碼

publicinterfaceStatementCallback{

Object?doWithStatement(Statement?stmt);

}

利用回調(diào)方法重寫JdbcTemplate方法

publicclassJdbcTemplate{

publicfinalObject?execute(StatementCallback?callback){

Connection?con=null;

Statement?stmt=null;

try{

con=getConnection();

stmt=con.createStatement();

Object?retValue=callback.doWithStatement(stmt);

returnretValue;

}catch(SQLException?e){

...

}finally{

closeStatement(stmt);

releaseConnection(con);

}

}

...//其它方法定義??

}

Jdbc使用方法如下:

JdbcTemplate?jdbcTemplate=...;

finalStringsql=...;

StatementCallback?callback=newStatementCallback(){

publicObject=doWithStatement(Statement?stmt){

return...;

}

}

jdbcTemplate.execute(callback);

為什么JdbcTemplate沒有使用繼承霜旧?

因為這個類的方法太多错忱,但是我們還是想用到JdbcTemplate已有的穩(wěn)定的、公用的數(shù)據(jù)庫連接挂据,那么我們怎么辦呢以清?

我們可以把變化的東西抽出來作為一個參數(shù)傳入JdbcTemplate的方法中。但是變化的東西是一段代碼崎逃,而且這段代碼會用到JdbcTemplate中的變量掷倔。怎么辦?

那我們就用回調(diào)對象吧个绍。在這個回調(diào)對象中定義一個操縱JdbcTemplate中變量的方法勒葱,我們?nèi)崿F(xiàn)這個方法,就把變化的東西集中到這里了巴柿。然后我們再傳入這個回調(diào)對象到JdbcTemplate凛虽,從而完成了調(diào)用。

參考

https://www.cnblogs.com/digdeep/p/4518571.html

https://www.cnblogs.com/tongkey/p/7919401.html

https://www.cnblogs.com/fingerboy/p/6393644.html

https://blog.csdn.net/ovoo_8/article/details/51189401

https://blog.csdn.net/z69183787/article/details/65628166

來源:https://mp.weixin.qq.com/s/ThK3QTGxIQla6AjKosZNVw

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末广恢,一起剝皮案震驚了整個濱河市凯旋,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖至非,帶你破解...
    沈念sama閱讀 206,311評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件钠署,死亡現(xiàn)場離奇詭異,居然都是意外死亡荒椭,警方通過查閱死者的電腦和手機谐鼎,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,339評論 2 382
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來戳杀,“玉大人该面,你說我怎么就攤上這事⌒趴ǎ” “怎么了隔缀?”我有些...
    開封第一講書人閱讀 152,671評論 0 342
  • 文/不壞的土叔 我叫張陵,是天一觀的道長傍菇。 經(jīng)常有香客問我猾瘸,道長,這世上最難降的妖魔是什么丢习? 我笑而不...
    開封第一講書人閱讀 55,252評論 1 279
  • 正文 為了忘掉前任牵触,我火速辦了婚禮,結(jié)果婚禮上咐低,老公的妹妹穿的比我還像新娘揽思。我一直安慰自己,他們只是感情好见擦,可當我...
    茶點故事閱讀 64,253評論 5 371
  • 文/花漫 我一把揭開白布钉汗。 她就那樣靜靜地躺著,像睡著了一般鲤屡。 火紅的嫁衣襯著肌膚如雪损痰。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,031評論 1 285
  • 那天酒来,我揣著相機與錄音卢未,去河邊找鬼。 笑死堰汉,一個胖子當著我的面吹牛辽社,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播翘鸭,決...
    沈念sama閱讀 38,340評論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼爹袁,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了矮固?” 一聲冷哼從身側(cè)響起失息,我...
    開封第一講書人閱讀 36,973評論 0 259
  • 序言:老撾萬榮一對情侶失蹤譬淳,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后盹兢,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體邻梆,經(jīng)...
    沈念sama閱讀 43,466評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 35,937評論 2 323
  • 正文 我和宋清朗相戀三年绎秒,在試婚紗的時候發(fā)現(xiàn)自己被綠了浦妄。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 38,039評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡见芹,死狀恐怖剂娄,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情玄呛,我是刑警寧澤阅懦,帶...
    沈念sama閱讀 33,701評論 4 323
  • 正文 年R本政府宣布,位于F島的核電站徘铝,受9級特大地震影響耳胎,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜惕它,卻給世界環(huán)境...
    茶點故事閱讀 39,254評論 3 307
  • 文/蒙蒙 一怕午、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧淹魄,春花似錦郁惜、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,259評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至搔体,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間半醉,已是汗流浹背疚俱。 一陣腳步聲響...
    開封第一講書人閱讀 31,485評論 1 262
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留缩多,地道東北人呆奕。 一個月前我還...
    沈念sama閱讀 45,497評論 2 354
  • 正文 我出身青樓,卻偏偏與公主長得像衬吆,于是被迫代替她去往敵國和親梁钾。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 42,786評論 2 345

推薦閱讀更多精彩內(nèi)容