前段時(shí)間,一位讀者面阿里被問(wèn)到了一個(gè)經(jīng)典問(wèn)題:說(shuō)一說(shuō) Spring 框架中用到了哪些設(shè)計(jì)模式?答的不是很好。不得不說(shuō),這個(gè)面試題出現(xiàn)的頻次還真不低,不少同學(xué)都遇到過(guò)。 所以今天這篇文章我們就來(lái)好好梳理一下這個(gè)知識(shí)點(diǎn),希望對(duì)大家有所幫助。
代理模式
所謂代理,是指它與被代理對(duì)象實(shí)現(xiàn)了相同的接口,客戶端必須通過(guò)代理才能與被代理的目標(biāo)類(lèi)進(jìn)行交互,而代理一般在交互的過(guò)程中(交互前后),進(jìn)行某些特定的處理,比如在調(diào)用這個(gè)方法前做前置處理,調(diào)用這個(gè)方法后做后置處理。
代理又分為靜態(tài)代理和動(dòng)態(tài)代理兩種方式,Spring 的 AOP 采用的是動(dòng)態(tài)代理的方式
Spring 通過(guò)動(dòng)態(tài)代理對(duì)類(lèi)進(jìn)行方法級(jí)別的切面增強(qiáng),動(dòng)態(tài)生成目標(biāo)對(duì)象的代理類(lèi),并在代理類(lèi)的方法中設(shè)置攔截器,通過(guò)執(zhí)行攔截器中的邏輯增強(qiáng)了代理方法的功能,從而實(shí)現(xiàn) AOP。
關(guān)于動(dòng)態(tài)代理可以看這篇文章,寫(xiě)的很詳細(xì):動(dòng)態(tài)代理總結(jié),你要知道的都在這里,無(wú)廢話!
策略模式
我們前面講到,Spring AOP 是通過(guò)動(dòng)態(tài)代理來(lái)實(shí)現(xiàn)的。
具體到代碼實(shí)現(xiàn),Spring 支持兩種動(dòng)態(tài)代理實(shí)現(xiàn)方式,一種是 JDK 提供的動(dòng)態(tài)代理實(shí)現(xiàn)方式,另一種是 Cglib 提供的動(dòng)態(tài)代理實(shí)現(xiàn)方式。
Spring 會(huì)在運(yùn)行時(shí)動(dòng)態(tài)地選擇不同的動(dòng)態(tài)代理實(shí)現(xiàn)方式。這個(gè)應(yīng)用場(chǎng)景實(shí)際上就是策略模式的典型應(yīng)用場(chǎng)景。
我們只需要定義一個(gè)策略接口,讓不同的策略類(lèi)都實(shí)現(xiàn)這一個(gè)策略接口。對(duì)應(yīng)到 Spring 源碼,AopProxy 是策略接口,JdkDynamicAopProxy、CglibAopProxy 是兩個(gè)實(shí)現(xiàn)了 AopProxy 接口的策略類(lèi)。
其中,AopProxy接口的定義如下所示:
在策略模式中,策略的創(chuàng)建一般通過(guò)工廠方法來(lái)實(shí)現(xiàn)。對(duì)應(yīng)到Spring源碼,AopProxyFactory 是一個(gè)工廠類(lèi)接口,DefaultAopProxyFactory 是一個(gè)默認(rèn)的工廠類(lèi),用來(lái)創(chuàng)建 AopProxy 對(duì)象。
源碼如下所示:
策略模式的典型應(yīng)用場(chǎng)景,一般是通過(guò)環(huán)境變量、狀態(tài)值、計(jì)算結(jié)果等動(dòng)態(tài)地決定使用哪個(gè)策略。
對(duì)應(yīng)到 Spring 源碼中,我們可以參看剛剛給出的 DefaultAopProxyFactory 類(lèi)中的 createAopProxy() 函數(shù)的代碼實(shí)現(xiàn)。
其中,第10行代碼是動(dòng)態(tài)選擇哪種策略的判斷條件。
裝飾器模式
我們知道,緩存一般都是配合數(shù)據(jù)庫(kù)來(lái)使用的。如果寫(xiě)緩存成功,但數(shù)據(jù)庫(kù)事務(wù)回滾了,那緩存中就會(huì)有臟數(shù)據(jù)。
為了解決這個(gè)問(wèn)題,我們需要將緩存的寫(xiě)操作和數(shù)據(jù)庫(kù)的寫(xiě)操作,放到同一個(gè)事務(wù)中,要么都成功,要么都失敗。
實(shí)現(xiàn)這樣一個(gè)功能,Spring 使用到了裝飾器模式。
TransactionAwareCacheDecorator 增加了對(duì)事務(wù)的支持,在事務(wù)提交、回滾的時(shí)候分別對(duì)Cache的數(shù)據(jù)進(jìn)行處理。
TransactionAwareCacheDecorator 實(shí)現(xiàn) Cache 接口,并且將所有的操作都委托給 targetCache 來(lái)實(shí)現(xiàn),對(duì)其中的寫(xiě)操作添加了事務(wù)功能。這是典型的裝飾器模式的應(yīng)用場(chǎng)景和代碼實(shí)現(xiàn)。
單例模式
單例模式是指一個(gè)類(lèi)在整個(gè)系統(tǒng)運(yùn)行過(guò)程中,只允許產(chǎn)生一個(gè)實(shí)例
在Spring中,Bean 可以被定義為兩種模式:Prototype(多例)和Singleton(單例),Spring Bean默認(rèn)是單例模式。
那Spring是如何實(shí)現(xiàn)單例模式的呢?
答案是通過(guò)單例注冊(cè)表的方式,具體來(lái)說(shuō)就是使用了HashMap。簡(jiǎn)化代碼如下:
public?class?DefaultSingletonBeanRegistry?{ ???? ????//使用了線程安全容器ConcurrentHashMap,保存各種單實(shí)例對(duì)象 ????private?final?Map?singletonObjects?=?new?ConcurrentHashMap; ????protected?Object?getSingleton(String?beanName)?{ ????//先到HashMap中拿Object ????Object?singletonObject?=?singletonObjects.get(beanName); ???? ????//如果沒(méi)拿到通過(guò)反射創(chuàng)建一個(gè)對(duì)象實(shí)例,并添加到HashMap中 ????if?(singletonObject?==?null)?{ ??????singletonObjects.put(beanName, ???????????????????????????Class.forName(beanName).newInstance()); ???} ??? ???//返回對(duì)象實(shí)例 ???return?singletonObjects.get(beanName); ??} }
上面的代碼邏輯比較清晰,先到? HashMap去拿單實(shí)例對(duì)象,沒(méi)拿到就創(chuàng)建一個(gè)添加到 HashMap。
簡(jiǎn)單工廠模式
有這樣一個(gè)場(chǎng)景:
當(dāng) A 對(duì)象需要調(diào)用 B 對(duì)象的方法時(shí),我們需要在 A 中 new 一個(gè) B 的實(shí)例,它的缺點(diǎn)是一旦需求發(fā)生變化,比如需要使用C類(lèi)來(lái)代替B時(shí),就要改寫(xiě)A類(lèi)的方法。
假如應(yīng)用中有 100 個(gè)類(lèi)以的方式耦合了 B,那改起來(lái)就費(fèi)勁了。
使用簡(jiǎn)單工廠模式:
簡(jiǎn)單工廠模式又叫靜態(tài)工廠方法,其實(shí)質(zhì)是由一個(gè)工廠類(lèi)根據(jù)傳入的參數(shù),動(dòng)態(tài)決定應(yīng)該創(chuàng)建哪一個(gè)產(chǎn)品類(lèi)。
其中 Spring 中的 BeanFactory 就是簡(jiǎn)單工廠模式的體現(xiàn),BeanFactory 是 Spring IOC 容器中的一個(gè)核心接口,它的定義如下:
我們可以通過(guò)它的具體實(shí)現(xiàn)類(lèi)(比如ClassPathXmlApplicationContext)來(lái)獲取Bean:
BeanFactory?bf?=?new?ClassPathXmlApplicationContext("spring.xml"); FlyFish?flyFishBean?=?(FlyFish)?bf.getBean("flyfishBean");
從上面代碼可以看到,使用者不需要自己來(lái)new對(duì)象,而是通過(guò)工廠類(lèi)的方法getBean來(lái)獲取對(duì)象實(shí)例,這是典型的簡(jiǎn)單工廠模式,只不過(guò)Spring是用反射機(jī)制來(lái)創(chuàng)建Bean的。
工廠方法模式
在簡(jiǎn)單工廠中,由工廠類(lèi)進(jìn)行所有的邏輯判斷、實(shí)例創(chuàng)建;如果不想在工廠類(lèi)中進(jìn)行判斷,可以為不同的產(chǎn)品提供不同的工廠,不同的工廠生產(chǎn)不同的產(chǎn)品,每一個(gè)工廠都只對(duì)應(yīng)一個(gè)相應(yīng)的對(duì)象,這就是工廠方法模式。
Spring中的FactoryBean就是這種思想的體現(xiàn),F(xiàn)actoryBean可以理解為工廠Bean,先來(lái)看看它的定義:
我們定義一個(gè)類(lèi)FlyFishFactoryBean來(lái)實(shí)現(xiàn)FactoryBean接口,主要是在getObject方法里new一個(gè)FlyFish對(duì)象。這樣我們通過(guò)getBean(id) 獲得的是該工廠所產(chǎn)生的FlyFish的實(shí)例,而不是FlyFishFactoryBean本身的實(shí)例,像下面這樣:
BeanFactory?bf?=?new?ClassPathXmlApplicationContext("spring.xml"); FlyFish?flyFishBean?=?(FlyFish)?bf.getBean("flyfishBean");
觀察者模式
Spring中實(shí)現(xiàn)的觀察者模式包含三部分:Event事件(相當(dāng)于消息)、Listener監(jiān)聽(tīng)者(相當(dāng)于觀察者)、Publisher發(fā)送者(相當(dāng)于被觀察者)
我們通過(guò)一個(gè)例子來(lái)看下Spring提供的觀察者模式是怎么使用的
// Event事件 public?class?DemoEvent?extends?ApplicationEvent?{ ??private?String?message; ??public?DemoEvent(Object?source,?String?message)?{ ????super(source); ??} ??public?String?getMessage()?{ ????return?this.message; ??} } //?Listener監(jiān)聽(tīng)者 @Component public?class?DemoListener?implements?ApplicationListener?{ ??@Override ??public?void?onApplicationEvent(DemoEvent?demoEvent)?{ ????String?message?=?demoEvent.getMessage(); ????System.out.println(message); ??} } //?Publisher發(fā)送者 @Component public?class?DemoPublisher?{ ??@Autowired ??private?ApplicationContext?applicationContext; ??public?void?publishEvent(DemoEvent?demoEvent)?{ ????this.applicationContext.publishEvent(demoEvent); ??} }
從代碼中,我們可以看出,主要包含三部分工作:
定義一個(gè)繼承ApplicationEvent的事件(DemoEvent);
定義一個(gè)實(shí)現(xiàn)了ApplicationListener的監(jiān)聽(tīng)器(DemoListener);
定義一個(gè)發(fā)送者(DemoPublisher),發(fā)送者調(diào)用ApplicationContext來(lái)發(fā)送事件消息。
在Spring的實(shí)現(xiàn)中,觀察者注冊(cè)到了哪里呢?又是如何注冊(cè)的呢?
Spring把觀察者注冊(cè)到了ApplicationContext對(duì)象中。
實(shí)際上,具體到源碼來(lái)說(shuō),ApplicationContext只是一個(gè)接口,具體的代碼實(shí)現(xiàn)包含在它的實(shí)現(xiàn)類(lèi)AbstractApplicationContext中。我把跟觀察者模式相關(guān)的代碼,如下。你只需要關(guān)注它是如何發(fā)送事件和注冊(cè)監(jiān)聽(tīng)者就好。
從上面的代碼中,我們發(fā)現(xiàn),真正的消息發(fā)送,實(shí)際上是通過(guò)ApplicationEventMulticaster這個(gè)類(lèi)來(lái)完成的。
下面這個(gè)類(lèi)的源碼我只摘抄了最關(guān)鍵的一部分,也就是multicastEvent()這個(gè)消息發(fā)送函數(shù),它通過(guò)線程池,支持異步非阻塞、同步阻塞這兩種類(lèi)型的觀察者模式。
借助Spring提供的觀察者模式的骨架代碼,如果我們要在Spring下實(shí)現(xiàn)某個(gè)事件的發(fā)送和監(jiān)聽(tīng),只需要做很少的工作,定義事件、定義監(jiān)聽(tīng)器、往ApplicationContext中發(fā)送事件就可以了,剩下的工作都由Spring框架來(lái)完成。
實(shí)際上,這也體現(xiàn)了Spring框架的擴(kuò)展性,也就是在不需要修改任何代碼的情況下,擴(kuò)展新的事件和監(jiān)聽(tīng)。
模板模式
我們經(jīng)常在面試中被問(wèn)到的一個(gè)問(wèn)題:
請(qǐng)你說(shuō)下Spring Bean的創(chuàng)建過(guò)程包含哪些主要的步驟。
這其中就涉及模板模式。它也體現(xiàn)了Spring的擴(kuò)展性。利用模板模式,Spring能讓用戶定制Bean的創(chuàng)建過(guò)程。
下面是Spring Bean的整個(gè)生命周期,一張圖,清晰明了:
如果你仔細(xì)看過(guò)源碼會(huì)發(fā)現(xiàn),實(shí)際上,這里的模板模式的實(shí)現(xiàn),并不是標(biāo)準(zhǔn)的抽象類(lèi)的實(shí)現(xiàn)方式,而是有點(diǎn)類(lèi)似? Callback回調(diào)的實(shí)現(xiàn)方式,也就是將要執(zhí)行的函數(shù)封裝成對(duì)象(比如,初始化方法封裝成 InitializingBean 對(duì)象),傳遞給模板(BeanFactory)來(lái)執(zhí)行。
觀察者模式和模板模式,這兩種模式能夠幫助我們創(chuàng)建擴(kuò)展點(diǎn),讓框架的使用者在不修改源碼的情況下,基于擴(kuò)展點(diǎn)定制化框架功能。
適配器模式
在Spring MVC中,定義一個(gè)Controller最常用的方式是,通過(guò)@Controller注解來(lái)標(biāo)記某個(gè)類(lèi)是Controller類(lèi),通過(guò)@RequesMapping注解來(lái)標(biāo)記函數(shù)對(duì)應(yīng)的URL
不過(guò),我們還可以通過(guò)讓類(lèi)實(shí)現(xiàn)Controller接口或者Servlet接口,來(lái)定義一個(gè)Controller。
針對(duì)這三種定義方式,我寫(xiě)了三段示例代碼,如下所示:
//?方法一:通過(guò)@Controller、@RequestMapping來(lái)定義 @Controller public?class?DemoController?{ ????@RequestMapping("/FlyFish") ????public?ModelAndView?getEmployeeName()?{ ????????ModelAndView?model?=?new?ModelAndView("FlyFish");???????? ????????model.addObject("message",?"FlyFish");??????? ????????return?model;? ????}?? } //?方法二:實(shí)現(xiàn)Controller接口?+ xml配置文件:配置DemoController與URL的對(duì)應(yīng)關(guān)系 public?class?DemoController?implements?Controller?{ ????@Override ????public?ModelAndView?handleRequest(HttpServletRequest?req,?HttpServletResponse?resp)?throws?Exception?{ ????????ModelAndView?model?=?new?ModelAndView("FlyFish"); ????????model.addObject("message",?"FlyFish"); ????????return?model; ????} } //?方法三:實(shí)現(xiàn)Servlet接口?+ xml配置文件:配置DemoController類(lèi)與URL的對(duì)應(yīng)關(guān)系 public?class?DemoServlet?extends?HttpServlet?{ ??@Override ??protected?void?doGet(HttpServletRequest?req,?HttpServletResponse?resp)?throws?ServletException,?IOException?{ ????this.doPost(req,?resp); ??} ?? ??@Override ??protected?void?doPost(HttpServletRequest?req,?HttpServletResponse?resp)?throws?ServletException,?IOException?{ ????resp.getWriter().write("Hello?World."); ??} }
在應(yīng)用啟動(dòng)的時(shí)候,Spring容器會(huì)加載這些Controller類(lèi),并且解析出URL對(duì)應(yīng)的處理函數(shù),封裝成Handler對(duì)象,存儲(chǔ)到HandlerMapping對(duì)象中。當(dāng)有請(qǐng)求到來(lái)的時(shí)候,DispatcherServlet從HanderMapping中,查找請(qǐng)求URL對(duì)應(yīng)的Handler,然后調(diào)用執(zhí)行Handler對(duì)應(yīng)的函數(shù)代碼,最后將執(zhí)行結(jié)果返回給客戶端。
但是,不同方式定義的Controller,其函數(shù)的定義(函數(shù)名、入?yún)ⅰ⒎祷刂档龋┦遣唤y(tǒng)一的。
DispatcherServlet調(diào)用的是service()方法,DispatcherServlet需要根據(jù)不同類(lèi)型的Controller,調(diào)用不同的函數(shù)。
Spring利用適配器模式,我們將不同方式定義的Controller類(lèi)中的函數(shù),適配為統(tǒng)一的函數(shù)定義。
我們?cè)倬唧w看下Spring的代碼實(shí)現(xiàn)。
Spring定義了統(tǒng)一的接口HandlerAdapter,并且對(duì)每種Controller定義了對(duì)應(yīng)的適配器類(lèi)。
這些適配器類(lèi)包括:AnnotationMethodHandlerAdapter、SimpleControllerHandlerAdapter、SimpleServletHandlerAdapter等。
在DispatcherServlet類(lèi)中,我們就不需要區(qū)分對(duì)待不同的Controller對(duì)象了,統(tǒng)一調(diào)用HandlerAdapter的handle()函數(shù)就可以了。
編輯:黃飛
評(píng)論