<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          Spring 中經(jīng)典的 9 種設(shè)計模式!收藏了

          共 13944字,需瀏覽 28分鐘

           ·

          2021-03-19 11:42

          往期熱門文章:

          1、高仿小米商城項目,愛了!

          2、為什么有些公司不讓用 Lombok ?

          3、厲害了,為了干掉 HTTP ,Spring 團隊開源了 nohttp 項目!

          4、別瞎學(xué)了,這幾門語言要被淘汰了!

          5、一個基于 Spring Boot 的項目骨架,非常舒服!

          來源:blog.csdn.net/caoxiaohong1005

          1. 簡單工廠(非23種設(shè)計模式中的一種)

          實現(xiàn)方式:

          BeanFactory。Spring中的BeanFactory就是簡單工廠模式的體現(xiàn),根據(jù)傳入一個唯一的標(biāo)識來獲得Bean對象,但是否是在傳入?yún)?shù)后創(chuàng)建還是傳入?yún)?shù)前創(chuàng)建這個要根據(jù)具體情況來定。
          實質(zhì):
          由一個工廠類根據(jù)傳入的參數(shù),動態(tài)決定應(yīng)該創(chuàng)建哪一個產(chǎn)品類。
          實現(xiàn)原理:
          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會幫我們注入對應(yīng)的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)用接口中的方法。

          設(shè)計意義:

          松耦合。 可以將原來硬編碼的依賴,通過Spring這個beanFactory這個工廠來注入依賴,也就是說原來只有依賴方和被依賴方,現(xiàn)在我們引入了第三方——spring這個beanFactory,由它來解決bean之間的依賴問題,達到了松耦合的效果.
          bean的額外處理。 通過Spring接口的暴露,在實例化bean的階段我們可以進行一些額外的處理,這些額外的處理只需要讓bean實現(xiàn)對應(yīng)的接口即可,那么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實例默認(rèn)是單例的。
          Spring的依賴注入(包括lazy-init方式)都是發(fā)生在AbstractBeanFactory的getBean里。getBean的doGetBean方法調(diào)用getSingleton進行bean的創(chuàng)建。
          分析 getSingleton() 方法
          public Object getSingleton(String beanName){
              //參數(shù)true設(shè)置標(biāo)識允許早期依賴
              return getSingleton(beanName,true);
          }
          protected Object 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) {
                          //當(dāng)某些方法需要提前初始化的時候則會調(diào)用addSingleFactory 方法將對應(yīng)的ObjectFactory初始化策略存儲在singletonFactories
                          ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
                          if (singletonFactory != null) {
                              //調(diào)用預(yù)先設(shè)定的getObject方法
                              singletonObject = singletonFactory.getObject();
                              //記錄在緩存中,earlysingletonObjects和singletonFactories互斥
                              this.earlySingletonObjects.put(beanName, singletonObject);
                              this.singletonFactories.remove(beanName);
                          }
                      }
                  }
              }
              return (singletonObject != NULL_OBJECT ? singletonObject : null);
          }

          getSingleton()過程圖

          ps:spring依賴注入時,使用了 雙重判斷加鎖 的單例模式
          單例模式定義: 保證一個類僅有一個實例,并提供一個訪問它的全局訪問點。
          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ī)則找到對應(yīng)的Handler并讓其執(zhí)行,執(zhí)行完畢后Handler會向HandlerAdapter返回一個ModelAndView,最后由HandlerAdapter向DispatchServelet返回一個ModelAndView。
          實現(xiàn)意義:
          HandlerAdatper使得Handler的擴展變得容易,只需要增加一個新的Handler和一個對應(yīng)的HandlerAdapter即可。
          因此Spring定義了一個適配接口,使得每一種Controller有一種對應(yīng)的適配器實現(xiàn)類,讓適配器代替controller執(zhí)行相應(yīng)的方法。這樣在擴展Controller時,只需要增加一個適配器類就完成了SpringMVC的擴展了。

          5. 裝飾器模式

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

          實質(zhì):動態(tài)地給一個對象添加一些額外的職責(zé)。就增加功能來說,Decorator模式相比生成子類更為靈活。

          6. 代理模式

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

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

          靜態(tài)代理:需要手工編寫代理類,代理類引用被代理對象。

          實現(xiàn)原理:切面在應(yīng)用運行的時刻被織入。一般情況下,在織入切面時,AOP容器會為目標(biāo)對象創(chuàng)建動態(tài)的創(chuàng)建一個代理對象。SpringAOP就是以這種方式織入切面的。

          織入:把切面應(yīng)用到目標(biāo)對象并創(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的容器事件.
          代碼:
          public abstract class ApplicationEvent extends EventObject {
              private static final long serialVersionUID = 7099057708183571937L;
              private final long timestamp;
              public ApplicationEvent(Object source) {
              super(source);
              this.timestamp = System.currentTimeMillis();
              }
              public final long getTimestamp() {
                  return this.timestamp;
              }
          }


          ApplicationListener接口[事件監(jiān)聽器]
          繼承自jdk的EventListener,所有的監(jiān)聽器都要實現(xiàn)這個接口。
          這個接口只有一個onApplicationEvent()方法,該方法接受一個ApplicationEvent或其子類對象作為參數(shù),在方法體中,可以通過不同對Event類的判斷來進行相應(yīng)的處理。
          當(dāng)事件觸發(fā)時所有的監(jiān)聽器都會收到消息。
          代碼:
          public interface ApplicationListener<E extends ApplicationEvent> extends EventListener {
               void onApplicationEvent(E event);
          }
          ApplicationContext接口[事件源]
          ApplicationContext是spring中的全局容器,翻譯過來是”應(yīng)用上下文”。
          實現(xiàn)了ApplicationEventPublisher接口。

          職責(zé):

          負(fù)責(zé)讀取bean的配置文檔,管理bean的加載,維護bean之間的依賴關(guān)系,可以說是負(fù)責(zé)bean的整個生命周期,再通俗一點就是我們平時所說的IOC容器。
          代碼:
          public interface ApplicationEventPublisher {
                  void publishEvent(ApplicationEvent event);
          }

          public void publishEvent(ApplicationEvent event) {
              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)聽器.
          代碼:
          public abstract class AbstractApplicationContext extends DefaultResourceLoader
              implements ConfigurableApplicationContext, DisposableBean 
          {
              private ApplicationEventMulticaster applicationEventMulticaster;
              protected void registerListeners() {
              // 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(): 定位并打開資源,返回資源對應(yīng)的輸入流。每次調(diào)用都返回新的輸入流。調(diào)用者必須負(fù)責(zé)關(guān)閉輸入流。
          • exists(): 返回 Resource 所指向的資源是否存在。
          • isOpen(): 返回資源文件是否打開,如果資源文件不能多次讀取,每次讀取結(jié)束應(yīng)該顯式關(guān)閉,以防止資源泄漏。
          • getDescription(): 返回資源的描述信息,通常用于資源處理出錯時輸出該信息,通常是全限定文件名或?qū)嶋H URL。
          • getFile: 返回資源對應(yīng)的 File 對象。
          • getURL: 返回資源對應(yīng)的 URL 對象。
          最后兩個方法通常無須使用,僅在通過簡單方式訪問無法實現(xiàn)時,Resource 提供傳統(tǒng)的資源訪問的功能。
          Resource 接口本身沒有提供訪問任何底層資源的實現(xiàn)邏輯,針對不同的底層資源,Spring 將會提供不同的 Resource 實現(xiàn)類,不同的實現(xiàn)類負(fù)責(zé)不同的資源訪問邏輯。
          Spring 為 Resource 接口提供了如下實現(xiàn)類:
          • UrlResource: 訪問網(wǎng)絡(luò)資源的實現(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)類,針對不同的的底層資源,提供了相應(yīng)的資源訪問邏輯,并提供便捷的包裝,以利于客戶端程序的資源訪問。

          9. 模版方法模式

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

          父類定義了骨架(調(diào)用哪些方法及順序),某些特定方法由子類實現(xiàn)。
          最大的好處:代碼復(fù)用,減少重復(fù)代碼。除了子類要實現(xiàn)的特定方法,其他方法及方法調(diào)用順序都在父類中預(yù)先寫好了。
          所以父類模板方法中有兩類方法:
          共同的方法: 所有子類都會用到的代碼
          不同的方法: 子類要覆蓋的方法,分為兩種:
          • 抽象方法:父類中的是抽象方法,子類必須覆蓋
          • 鉤子方法:父類中是一個空方法,子類繼承了默認(rèn)也是空的
          注:為什么叫鉤子,子類可以通過這個鉤子(方法),控制父類,因為這個鉤子實際是父類的方法(空方法)!

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

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

          具體實現(xiàn):

          JDBC的抽象和對Hibernate的集成,都采用了一種理念或者處理方式,那就是模板方法模式與相應(yīng)的Callback接口相結(jié)合。
          采用模板方法模式是為了以一種統(tǒng)一而集中的方式來處理資源的獲取和釋放,以JdbcTempalte為例:
          public abstract class JdbcTemplate {
               public final Object execute(String sql){
                  Connection con=null;
                  Statement stmt=null;
                  try{
                      con=getConnection();
                      stmt=con.createStatement();
                      Object retValue=executeWithStatement(stmt,sql);
                      return retValue;
                  }catch(SQLException e){
                       ...
                  }finally{
                      closeStatement(stmt);
                      releaseConnection(con);
                  }
              }
              protected abstract Object executeWithStatement(Statement stmt, String sql);
          }

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

          JdbcTemplate是抽象類,不能夠獨立使用,我們每次進行數(shù)據(jù)訪問的時候都要給出一個相應(yīng)的子類實現(xiàn),這樣肯定不方便,所以就引入了回調(diào)。
          回調(diào)代碼
          public interface StatementCallback{
              Object doWithStatement(Statement stmt);
          }
          利用回調(diào)方法重寫JdbcTemplate方法
          public class JdbcTemplate {
              public final Object execute(StatementCallback callback){
                  Connection con=null;
                  Statement stmt=null;
                  try{
                      con=getConnection();
                      stmt=con.createStatement();
                      Object retValue=callback.doWithStatement(stmt);
                      return retValue;
                  }catch(SQLException e){
                      ...
                  }finally{
                      closeStatement(stmt);
                      releaseConnection(con);
                  }
              }

              ...//其它方法定義
          }
          Jdbc使用方法如下:
          JdbcTemplate jdbcTemplate=...;
              final String sql=...;
              StatementCallback callback=new StatementCallback(){
              public Object=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)用。

          最近熱文閱讀:

          1、高仿小米商城項目,愛了!
          2、為什么有些公司不讓用 Lombok ?
          3、厲害了,為了干掉 HTTP ,Spring 團隊開源了 nohttp 項目!
          4、別瞎學(xué)了,這幾門語言要被淘汰了!
          5、一個基于 Spring Boot 的項目骨架,非常舒服!
          6、Redis 低成本、高可用設(shè)計,牛逼!
          7、2020年國內(nèi)互聯(lián)網(wǎng)公司的薪酬排名!
          8、IDEA 真牛逼,900行 "又臭又長" 的類重構(gòu),幾分鐘搞定!
          9、舒服!一個注解,搞定 SpringBoot 操作日志
          10、不要再封裝各種Util工具類了,這個神級框架值得擁有!
          關(guān)注公眾號,你想要的Java都在這里

          瀏覽 66
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          評論
          圖片
          表情
          推薦
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  一个人看的区二区不卡视频 | 欧美黑人XXXX高潮交 | 爱草av| 欧美丰满熟妇XXX | 99久久精品互换人妻 |