日韩无码专区无码一级三级片|91人人爱网站中日韩无码电影|厨房大战丰满熟妇|AV高清无码在线免费观看|另类AV日韩少妇熟女|中文日本大黄一级黄色片|色情在线视频免费|亚洲成人特黄a片|黄片wwwav色图欧美|欧亚乱色一区二区三区

RELATEED CONSULTING
相關(guān)咨詢
選擇下列產(chǎn)品馬上在線溝通
服務(wù)時(shí)間:8:30-17:00
你可能遇到了下面的問題
關(guān)閉右側(cè)工具欄

新聞中心

這里有您想知道的互聯(lián)網(wǎng)營(yíng)銷解決方案
來自讀者的面試題:談?wù)凷pring用到了哪些設(shè)計(jì)模式?

本文轉(zhuǎn)載自微信公眾號(hào)「月伴飛魚」,作者日常加油站。轉(zhuǎn)載本文請(qǐng)聯(lián)系月伴飛魚公眾號(hào)。

創(chuàng)新互聯(lián)主要從事成都網(wǎng)站建設(shè)、做網(wǎng)站、網(wǎng)頁(yè)設(shè)計(jì)、企業(yè)做網(wǎng)站、公司建網(wǎng)站等業(yè)務(wù)。立足成都服務(wù)亞東,10年網(wǎng)站建設(shè)經(jīng)驗(yàn),價(jià)格優(yōu)惠、服務(wù)專業(yè),歡迎來電咨詢建站服務(wù):18980820575

代理模式

所謂代理,是指它與被代理對(duì)象實(shí)現(xiàn)了相同的接口,客戶端必須通過代理才能與被代理的目標(biāo)類進(jìn)行交互,而代理一般在交互的過程中(交互前后),進(jìn)行某些特定的處理,比如在調(diào)用這個(gè)方法前做前置處理,調(diào)用這個(gè)方法后做后置處理。

代理又分為靜態(tài)代理和動(dòng)態(tài)代理兩種方式,Spring的AOP采用的是動(dòng)態(tài)代理的方式

Spring通過動(dòng)態(tài)代理對(duì)類進(jìn)行方法級(jí)別的切面增強(qiáng),動(dòng)態(tài)生成目標(biāo)對(duì)象的代理類,并在代理類的方法中設(shè)置攔截器,通過執(zhí)行攔截器中的邏輯增強(qiáng)了代理方法的功能,從而實(shí)現(xiàn)AOP。

策略模式

我們前面講到,Spring AOP是通過動(dòng)態(tà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è)策略接口,讓不同的策略類都實(shí)現(xiàn)這一個(gè)策略接口。對(duì)應(yīng)到Spring源碼,AopProxy是策略接口,JdkDynamicAopProxy、CglibAopProxy是兩個(gè)實(shí)現(xiàn)了AopProxy接口的策略類。

其中,AopProxy接口的定義如下所示:

在策略模式中,策略的創(chuàng)建一般通過工廠方法來實(shí)現(xiàn)。對(duì)應(yīng)到Spring源碼,AopProxyFactory是一個(gè)工廠類接口,DefaultAopProxyFactory是一個(gè)默認(rèn)的工廠類,用來創(chuàng)建AopProxy對(duì)象。

源碼如下所示:

策略模式的典型應(yīng)用場(chǎng)景,一般是通過環(huán)境變量、狀態(tài)值、計(jì)算結(jié)果等動(dòng)態(tài)地決定使用哪個(gè)策略。

對(duì)應(yīng)到Spring源碼中,我們可以參看剛剛給出的DefaultAopProxyFactory類中的createAopProxy()函數(shù)的代碼實(shí)現(xiàn)。

其中,第10行代碼是動(dòng)態(tài)選擇哪種策略的判斷條件。

裝飾器模式

我們知道,緩存一般都是配合數(shù)據(jù)庫(kù)來使用的。如果寫緩存成功,但數(shù)據(jù)庫(kù)事務(wù)回滾了,那緩存中就會(huì)有臟數(shù)據(jù)。

為了解決這個(gè)問題,我們需要將緩存的寫操作和數(shù)據(jù)庫(kù)的寫操作,放到同一個(gè)事務(wù)中,要么都成功,要么都失敗。

實(shí)現(xiàn)這樣一個(gè)功能,Spring使用到了裝飾器模式。

TransactionAwareCacheDecorator增加了對(duì)事務(wù)的支持,在事務(wù)提交、回滾的時(shí)候分別對(duì)Cache的數(shù)據(jù)進(jìn)行處理。

TransactionAwareCacheDecorator實(shí)現(xiàn)Cache接口,并且將所有的操作都委托給targetCache來實(shí)現(xiàn),對(duì)其中的寫操作添加了事務(wù)功能。這是典型的裝飾器模式的應(yīng)用場(chǎng)景和代碼實(shí)現(xiàn)。

單例模式

單例模式是指一個(gè)類在整個(gè)系統(tǒng)運(yùn)行過程中,只允許產(chǎn)生一個(gè)實(shí)例

在Spring中,Bean可以被定義為兩種模式:Prototype(多例)和Singleton(單例),Spring Bean默認(rèn)是單例模式。

那Spring是如何實(shí)現(xiàn)單例模式的呢?

答案是通過單例注冊(cè)表的方式,具體來說就是使用了HashMap。簡(jiǎn)化代碼如下:

 
 
 
 
  1. public class DefaultSingletonBeanRegistry { 
  2.      
  3.     //使用了線程安全容器ConcurrentHashMap,保存各種單實(shí)例對(duì)象 
  4.     private final Map singletonObjects = new ConcurrentHashMap; 
  5.  
  6.     protected Object getSingleton(String beanName) { 
  7.     //先到HashMap中拿Object 
  8.     Object singletonObject = singletonObjects.get(beanName); 
  9.      
  10.     //如果沒拿到通過反射創(chuàng)建一個(gè)對(duì)象實(shí)例,并添加到HashMap中 
  11.     if (singletonObject == null) { 
  12.       singletonObjects.put(beanName, 
  13.                            Class.forName(beanName).newInstance()); 
  14.    } 
  15.     
  16.    //返回對(duì)象實(shí)例 
  17.    return singletonObjects.get(beanName); 
  18.   } 

上面的代碼邏輯比較清晰,先到HashMap去拿單實(shí)例對(duì)象,沒拿到就創(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類來代替B時(shí),就要改寫A類的方法。

假如應(yīng)用中有100個(gè)類以的方式耦合了B,那改起來就費(fèi)勁了。

使用簡(jiǎn)單工廠模式:

簡(jiǎn)單工廠模式又叫靜態(tài)工廠方法,其實(shí)質(zhì)是由一個(gè)工廠類根據(jù)傳入的參數(shù),動(dòng)態(tài)決定應(yīng)該創(chuàng)建哪一個(gè)產(chǎn)品類。

其中Spring中的BeanFactory就是簡(jiǎn)單工廠模式的體現(xiàn),BeanFactory是Spring IOC容器中的一個(gè)核心接口,它的定義如下:

我們可以通過它的具體實(shí)現(xiàn)類(比如ClassPathXmlApplicationContext)來獲取Bean:

 
 
 
 
  1. BeanFactory bf = new ClassPathXmlApplicationContext("spring.xml"); 
  2. FlyFish flyFishBean = (FlyFish) bf.getBean("flyfishBean"); 

從上面代碼可以看到,使用者不需要自己來new對(duì)象,而是通過工廠類的方法getBean來獲取對(duì)象實(shí)例,這是典型的簡(jiǎn)單工廠模式,只不過Spring是用反射機(jī)制來創(chuàng)建Bean的。

工廠方法模式

在簡(jiǎn)單工廠中,由工廠類進(jìn)行所有的邏輯判斷、實(shí)例創(chuàng)建;如果不想在工廠類中進(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,先來看看它的定義:

我們定義一個(gè)類FlyFishFactoryBean來實(shí)現(xiàn)FactoryBean接口,主要是在getObject方法里new一個(gè)FlyFish對(duì)象。這樣我們通過getBean(id) 獲得的是該工廠所產(chǎn)生的FlyFish的實(shí)例,而不是FlyFishFactoryBean本身的實(shí)例,像下面這樣:

 
 
 
 
  1. BeanFactory bf = new ClassPathXmlApplicationContext("spring.xml"); 
  2. FlyFish flyFishBean = (FlyFish) bf.getBean("flyfishBean"); 

觀察者模式

Spring中實(shí)現(xiàn)的觀察者模式包含三部分:Event事件(相當(dāng)于消息)、Listener監(jiān)聽者(相當(dāng)于觀察者)、Publisher發(fā)送者(相當(dāng)于被觀察者)

我們通過一個(gè)例子來看下Spring提供的觀察者模式是怎么使用的

 
 
 
 
  1. // Event事件 
  2. public class DemoEvent extends ApplicationEvent { 
  3.   private String message; 
  4.  
  5.   public DemoEvent(Object source, String message) { 
  6.     super(source); 
  7.   } 
  8.  
  9.   public String getMessage() { 
  10.     return this.message; 
  11.   } 
  12.  
  13. // Listener監(jiān)聽者 
  14. @Component 
  15. public class DemoListener implements ApplicationListener { 
  16.   @Override 
  17.   public void onApplicationEvent(DemoEvent demoEvent) { 
  18.     String message = demoEvent.getMessage(); 
  19.     System.out.println(message); 
  20.   } 
  21.  
  22. // Publisher發(fā)送者 
  23. @Component 
  24. public class DemoPublisher { 
  25.   @Autowired 
  26.   private ApplicationContext applicationContext; 
  27.  
  28.   public void publishEvent(DemoEvent demoEvent) { 
  29.     this.applicationContext.publishEvent(demoEvent); 
  30.   } 

從代碼中,我們可以看出,主要包含三部分工作:

  • 定義一個(gè)繼承ApplicationEvent的事件(DemoEvent);
  • 定義一個(gè)實(shí)現(xiàn)了ApplicationListener的監(jiān)聽器(DemoListener);
  • 定義一個(gè)發(fā)送者(DemoPublisher),發(fā)送者調(diào)用ApplicationContext來發(fā)送事件消息。

在Spring的實(shí)現(xiàn)中,觀察者注冊(cè)到了哪里呢?又是如何注冊(cè)的呢?

Spring把觀察者注冊(cè)到了ApplicationContext對(duì)象中。

實(shí)際上,具體到源碼來說,ApplicationContext只是一個(gè)接口,具體的代碼實(shí)現(xiàn)包含在它的實(shí)現(xiàn)類AbstractApplicationContext中。我把跟觀察者模式相關(guān)的代碼,如下。你只需要關(guān)注它是如何發(fā)送事件和注冊(cè)監(jiān)聽者就好。

從上面的代碼中,我們發(fā)現(xiàn),真正的消息發(fā)送,實(shí)際上是通過ApplicationEventMulticaster這個(gè)類來完成的。

下面這個(gè)類的源碼我只摘抄了最關(guān)鍵的一部分,也就是multicastEvent()這個(gè)消息發(fā)送函數(shù),它通過線程池,支持異步非阻塞、同步阻塞這兩種類型的觀察者模式。

借助Spring提供的觀察者模式的骨架代碼,如果我們要在Spring下實(shí)現(xiàn)某個(gè)事件的發(fā)送和監(jiān)聽,只需要做很少的工作,定義事件、定義監(jiān)聽器、往ApplicationContext中發(fā)送事件就可以了,剩下的工作都由Spring框架來完成。

實(shí)際上,這也體現(xiàn)了Spring框架的擴(kuò)展性,也就是在不需要修改任何代碼的情況下,擴(kuò)展新的事件和監(jiān)聽。

模板模式

我們經(jīng)常在面試中被問到的一個(gè)問題:

請(qǐng)你說下Spring Bean的創(chuàng)建過程包含哪些主要的步驟。

這其中就涉及模板模式。它也體現(xiàn)了Spring的擴(kuò)展性。利用模板模式,Spring能讓用戶定制Bean的創(chuàng)建過程。

下面是Spring Bean的整個(gè)生命周期,一張圖,清晰明了:

如果你仔細(xì)看過源碼會(huì)發(fā)現(xiàn),實(shí)際上,這里的模板模式的實(shí)現(xiàn),并不是標(biāo)準(zhǔn)的抽象類的實(shí)現(xiàn)方式,而是有點(diǎn)類似Callback回調(diào)的實(shí)現(xiàn)方式,也就是將要執(zhí)行的函數(shù)封裝成對(duì)象(比如,初始化方法封裝成InitializingBean對(duì)象),傳遞給模板(BeanFactory)來執(zhí)行。

觀察者模式和模板模式,這兩種模式能夠幫助我們創(chuàng)建擴(kuò)展點(diǎn),讓框架的使用者在不修改源碼的情況下,基于擴(kuò)展點(diǎn)定制化框架功能。

適配器模式

在Spring MVC中,定義一個(gè)Controller最常用的方式是,通過@Controller注解來標(biāo)記某個(gè)類是Controller類,通過@RequesMapping注解來標(biāo)記函數(shù)對(duì)應(yīng)的URL

不過,我們還可以通過讓類實(shí)現(xiàn)Controller接口或者Servlet接口,來定義一個(gè)Controller。

針對(duì)這三種定義方式,我寫了三段示例代碼,如下所示:

 
 
 
 
  1. // 方法一:通過@Controller、@RequestMapping來定義 
  2. @Controller 
  3. public class DemoController { 
  4.     @RequestMapping("/FlyFish") 
  5.     public ModelAndView getEmployeeName() { 
  6.         ModelAndView model = new ModelAndView("FlyFish");         
  7.         model.addObject("message", "FlyFish");        
  8.         return model;  
  9.     }   
  10.  
  11. // 方法二:實(shí)現(xiàn)Controller接口 + xml配置文件:配置DemoController與URL的對(duì)應(yīng)關(guān)系 
  12. public class DemoController implements Controller { 
  13.     @Override 
  14.     public ModelAndView handleRequest(HttpServletRequest req, HttpServletResponse resp) throws Exception { 
  15.         ModelAndView model = new ModelAndView("FlyFish"); 
  16.         model.addObject("message", "FlyFish"); 
  17.         return model; 
  18.     } 
  19.  
  20. // 方法三:實(shí)現(xiàn)Servlet接口 + xml配置文件:配置DemoController類與URL的對(duì)應(yīng)關(guān)系 
  21. public class DemoServlet extends HttpServlet { 
  22.   @Override 
  23.   protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { 
  24.     this.doPost(req, resp); 
  25.   } 
  26.    
  27.   @Override 
  28.   protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { 
  29.     resp.getWriter().write("Hello World."); 
  30.   } 

在應(yīng)用啟動(dòng)的時(shí)候,Spring容器會(huì)加載這些Controller類,并且解析出URL對(duì)應(yīng)的處理函數(shù),封裝成Handler對(duì)象,存儲(chǔ)到HandlerMapping對(duì)象中。當(dāng)有請(qǐng)求到來的時(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)?、返回值?是不統(tǒng)一的。

DispatcherServlet調(diào)用的是service()方法,DispatcherServlet需要根據(jù)不同類型的Controller,調(diào)用不同的函數(shù)。

Spring利用適配器模式,我們將不同方式定義的Controller類中的函數(shù),適配為統(tǒng)一的函數(shù)定義。

我們?cè)倬唧w看下Spring的代碼實(shí)現(xiàn)。

Spring定義了統(tǒng)一的接口HandlerAdapter,并且對(duì)每種Controller定義了對(duì)應(yīng)的適配器類。

這些適配器類包括:AnnotationMethodHandlerAdapter、SimpleControllerHandlerAdapter、SimpleServletHandlerAdapter等。

在DispatcherServlet類中,我們就不需要區(qū)分對(duì)待不同的Controller對(duì)象了,統(tǒng)一調(diào)用HandlerAdapter的handle()函數(shù)就可以了


文章標(biāo)題:來自讀者的面試題:談?wù)凷pring用到了哪些設(shè)計(jì)模式?
路徑分享:http://m.5511xx.com/article/cdcseoi.html