如果僅僅是想學會使用,則可以不學習設(shè)計模式,如果想深入層次的學習Spring底層源碼,則設(shè)計模式是必不可少的!
下面從幾個方面進行介紹
(1)Spring源碼中使用了哪些設(shè)計模式?
(2)怎樣學習設(shè)計模式?
(3)推薦設(shè)計模式的書籍
Spring源碼中使用了哪些設(shè)計模式?
spring中使用了多種設(shè)計模式,下面簡單介紹一下主要的設(shè)計模式
(1)單例模式
保證獨一無二,為了提高資源重復利用,通過技術(shù)手段保證在整個系統(tǒng)運行階段,只有一個實例
場景:配置文件、監(jiān)控程序、IOC容器、日歷
實現(xiàn)手段:懶漢式、餓漢式、注冊登記式、反序列處理
Spring最常用的,注冊登記式,效率性能最高的
Spring中bean的默認作用域就是singleton(單例)。
實現(xiàn)方式:
- xml:<beanid="userService"class="top.snailclimb.UserService"scope="singleton"/>
- 注解:@Scope(value="singleton")
Spring通過ConcurrentHashMap實現(xiàn)單例注冊表的特殊方式實現(xiàn)單例模式。Spring實現(xiàn)單例的核心代碼如下
(2)代理設(shè)計模式
代理模式在AOP中的應(yīng)用
AOP(Aspect-OrientedProgramming:面向切面編程)能夠?qū)⒛切┡c業(yè)務(wù)無關(guān),卻為業(yè)務(wù)模塊所共同調(diào)用的邏輯或責任(例如事務(wù)處理、日志管理、權(quán)限控制等)封裝起來,便于減少系統(tǒng)的重復代碼,降低模塊間的耦合度,并有利于未來的可拓展性和可維護性。
SpringAOP就是基于動態(tài)代理的,如果要代理的對象,實現(xiàn)了某個接口,那么SpringAOP會使用JDKProxy,去創(chuàng)建代理對象,而對于沒有實現(xiàn)接口的對象,就無法使用JDKProxy去進行代理了,這時候SpringAOP會使用Cglib,這時候SpringAOP會使用Cglib生成一個被代理對象的子類來作為代理,如下圖所示:
當然你也可以使用AspectJ,SpringAOP已經(jīng)集成了AspectJ,AspectJ應(yīng)該算的上是Java生態(tài)系統(tǒng)中最完整的AOP框架了。
使用AOP之后我們可以把一些通用功能抽象出來,在需要用到的地方直接使用即可,這樣大大簡化了代碼量。我們需要增加新功能時也方便,這樣也提高了系統(tǒng)擴展性。日志功能、事務(wù)管理等等場景都用到了AOP。
SpringAOP和AspectJAOP有什么區(qū)別?
SpringAOP屬于運行時增強,而AspectJ是編譯時增強。SpringAOP基于代理(Proxying),而AspectJ基于字節(jié)碼操作(BytecodeManipulation)。
SpringAOP已經(jīng)集成了AspectJ,AspectJ應(yīng)該算的上是Java生態(tài)系統(tǒng)中最完整的AOP框架了。AspectJ相比于SpringAOP功能更加強大,但是SpringAOP相對來說更簡單,
如果我們的切面比較少,那么兩者性能差異不大。但是,當切面太多的話,最好選擇AspectJ,它比SpringAOP快很多。
(3)模板方法
應(yīng)用場景:JdbcTemplate工作流
代碼場景:模擬SpringJdbcTemplate的簡單實現(xiàn)
Spring中jdbcTemplate、hibernateTemplate等于Template結(jié)尾的對數(shù)據(jù)庫操作的類,它們就使用到了模板模式。一般情況下,我們都是使用繼承的方式來實現(xiàn)模板模式,但是Spring并沒有使用這種方式,而是使用Callback模式與模板方法模式配合,既達到了代碼復用的效果,同時增加了靈活性。
(4)觀察者模式
觀察者模式是一種對象行為型模式。它表示的是一種對象與對象之間具有依賴關(guān)系,當一個對象發(fā)生改變的時候,這個對象所依賴的對象也會做出反應(yīng)。Spring事件驅(qū)動模型就是觀察者模式很經(jīng)典的一個應(yīng)用。Spring事件驅(qū)動模型非常有用,在很多場景都可以解耦我們的代碼。比如我們每次添加商品的時候都需要重新更新商品索引,這個時候就可以利用觀察者模式來解決這個問題。
Spring事件驅(qū)動模型中的三種角色
事件角色
ApplicationEvent(org.springframework.context包下)充當事件的角色,這是一個抽象類,它繼承了java.util.EventObject并實現(xiàn)了java.io.Serializable接口。
Spring中默認存在以下事件,他們都是對ApplicationContextEvent的實現(xiàn)(繼承自ApplicationContextEvent):
- ContextStartedEvent:ApplicationContext啟動后觸發(fā)的事件;
- ContextStoppedEvent:ApplicationContext停止后觸發(fā)的事件;
- ContextRefreshedEvent:ApplicationContext初始化或刷新完成后觸發(fā)的事件;
- ContextClosedEvent:ApplicationContext關(guān)閉后觸發(fā)的事件。
事件監(jiān)聽者角色
ApplicationListener充當了事件監(jiān)聽者角色,它是一個接口,里面只定義了一個onApplicationEvent()方法來處理ApplicationEvent。ApplicationListener接口類源碼如下,可以看出接口定義看出接口中的事件只要實現(xiàn)了ApplicationEvent就可以了。所以,在Spring中我們只要實現(xiàn)ApplicationListener接口實現(xiàn)onApplicationEvent()方法即可完成監(jiān)聽事件
事件發(fā)布者角色
ApplicationEventPublisher接口的publishEvent()這個方法在AbstractApplicationContext類中被實現(xiàn),閱讀這個方法的實現(xiàn),你會發(fā)現(xiàn)實際上事件真正是通過ApplicationEventMulticaster來廣播出去的。具體內(nèi)容過多,就不在這里分析了,后面可能會單獨寫一篇文章提到。
Spring的事件流程總結(jié)
定義一個事件:實現(xiàn)一個繼承自ApplicationEvent,并且寫相應(yīng)的構(gòu)造函數(shù);
定義一個事件監(jiān)聽者:實現(xiàn)ApplicationListener接口,重寫onApplicationEvent()方法;
使用事件發(fā)布者發(fā)布消息:可以通過ApplicationEventPublisher的publishEvent()方法發(fā)布消息。
Example:
(5)適配器模式
適配器模式(AdapterPattern)將一個接口轉(zhuǎn)換成客戶希望的另一個接口,適配器模式使接口不兼容的那些類可以一起工作,其別名為包裝器(Wrapper)。
springAOP中的適配器模式
我們知道SpringAOP的實現(xiàn)是基于代理模式,但是SpringAOP的增強或通知(Advice)使用到了適配器模式,與之相關(guān)的接口是AdvisorAdapter。Advice常用的類型有:BeforeAdvice(目標方法調(diào)用前,前置通知)、AfterAdvice(目標方法調(diào)用后,后置通知)、AfterReturningAdvice(目標方法執(zhí)行結(jié)束后,return之前)等等。每個類型Advice(通知)都有對應(yīng)的攔截器:MethodBeforeAdviceInterceptor、AfterReturningAdviceAdapter、AfterReturningAdviceInterceptor。Spring預定義的通知要通過對應(yīng)的適配器,適配成MethodInterceptor接口(方法攔截器)類型的對象(如:MethodBeforeAdviceInterceptor負責適配MethodBeforeAdvice)。
代碼場景:登錄,為了兼容舊系統(tǒng)的登錄功能,在老系統(tǒng)的基礎(chǔ)之上進行兼容編程,SpringAdapter結(jié)尾的
(6)裝飾器模式
裝飾者模式可以動態(tài)地給對象添加一些額外的屬性或行為。相比于使用繼承,裝飾者模式更加靈活。簡單點兒說就是當我們需要修改原有的功能,但我們又不愿直接去修改原有的代碼時,設(shè)計一個Decorator套在原有代碼外面。其實在JDK中就有很多地方用到了裝飾者模式,比如InputStream家族,InputStream類下有FileInputStream(讀取文件)、BufferedInputStream(增加緩存,使讀取文件速度大大提升)等子類都在不修改InputStream代碼的情況下擴展了它的功能
應(yīng)用場景:IO流、數(shù)據(jù)源、也用代碼改造一個舊系統(tǒng),在Spring中Decorator結(jié)尾的,Wrapper結(jié)尾的都是。
(7)委派模式
代理模式的特殊情況,全權(quán)代理
應(yīng)用場景:項目經(jīng)理、Dispatcher
代碼場景:Spring中ServletDispatcher/Delegate命名結(jié)尾的都是委派模式
怎樣學習設(shè)計模式
(1)調(diào)整好心態(tài),不要指望一蹴而就,不可浮躁。
學習和掌握設(shè)計模式需要一個過程,不同的階段看這些設(shè)計模式有不同的領(lǐng)悟和感受。不要指望真正的設(shè)計模式的書籍既簡單又有趣,一看就懂的。
(2)學習設(shè)計模式的第一步:準確理解每個設(shè)計模式的功能、基本結(jié)構(gòu)、標準實現(xiàn),了解適合使用它的場景以及使用的效果。
(3)學習設(shè)計模式的第二步:實際的開發(fā)中,嘗試著使用這些設(shè)計模式,并反復思考和總結(jié)是否使用得當,是否需要做一些變化。
(4)學習設(shè)計模式的第三步:再回頭去看設(shè)計模式的理論,有了實際的模式應(yīng)用經(jīng)驗再看設(shè)計模式,會有不同的感悟,一邊看一邊結(jié)合著應(yīng)用經(jīng)驗來思考。比如設(shè)計模式的本質(zhì)功能是什么?它是如何實現(xiàn)的?z這種實現(xiàn)方式還可以在什么地方應(yīng)用?如何才能把這個設(shè)計模式和具體的應(yīng)用結(jié)合起來?這個設(shè)計模式的出發(fā)點是什么?等等。可以有很多考慮的點,從不同的角度對設(shè)計模式進行思考。
(5)第四步:多次反復學習設(shè)計模式的第二步和第三步。也就是在實際開發(fā)中使用,然后結(jié)合理論思考,然后再應(yīng)用,再思考...如此循環(huán),反復多次,直到達到對設(shè)計模式基本掌握的水平。
簡而言之,要注意使用設(shè)計模式的理論和實踐相結(jié)合,理論指導實踐,實踐反過來加深對理論的理解,如此反復循環(huán),成螺旋式上升!
設(shè)計模式書籍的推薦
這本書非常經(jīng)典,大家可以反復閱讀,由于網(wǎng)上已沒有紙質(zhì)書,只有電子版,
有需要電子版的,可以私信。