<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 框架中使用的設(shè)計(jì)模式

          共 9766字,需瀏覽 20分鐘

           ·

          2021-03-22 13:36

            Java大聯(lián)盟

            幫助萬(wàn)千Java學(xué)習(xí)者持續(xù)成長(zhǎng)

          關(guān)注



          作者|icoding91

          blog.csdn.net/caoxiaohong1005


          B 站搜索:楠哥教你學(xué)Java

          獲取更多優(yōu)質(zhì)視頻教程


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

          實(shí)現(xiàn)方式:

          BeanFactory。Spring中的BeanFactory就是簡(jiǎn)單工廠模式的體現(xiàn),根據(jù)傳入一個(gè)唯一的標(biāo)識(shí)來(lái)獲得Bean對(duì)象,但是否是在傳入?yún)?shù)后創(chuàng)建還是傳入?yún)?shù)前創(chuàng)建這個(gè)要根據(jù)具體情況來(lái)定。
          實(shí)質(zhì):
          由一個(gè)工廠類根據(jù)傳入的參數(shù),動(dòng)態(tài)決定應(yīng)該創(chuàng)建哪一個(gè)產(chǎn)品類。
          實(shí)現(xiàn)原理:
          bean容器的啟動(dòng)階段:
          • 讀取bean的xml配置文件,將bean元素分別轉(zhuǎn)換成一個(gè)BeanDefinition對(duì)象。
          • 然后通過(guò)BeanDefinitionRegistry將這些bean注冊(cè)到beanFactory中,保存在它的一個(gè)ConcurrentHashMap中。
          • 將BeanDefinition注冊(cè)到了beanFactory之后,在這里Spring為我們提供了一個(gè)擴(kuò)展的切口,允許我們通過(guò)實(shí)現(xiàn)接口BeanFactoryPostProcessor 在此處來(lái)插入我們定義的代碼。
            典型的例子就是:PropertyPlaceholderConfigurer,我們一般在配置數(shù)據(jù)庫(kù)的dataSource時(shí)使用到的占位符的值,就是它注入進(jìn)去的。
          容器中bean的實(shí)例化階段:
          實(shí)例化階段主要是通過(guò)反射或者CGLIB對(duì)bean進(jìn)行實(shí)例化,在這個(gè)階段Spring又給我們暴露了很多的擴(kuò)展點(diǎn):
          • 各種的Aware接口 ,比如 BeanFactoryAware,對(duì)于實(shí)現(xiàn)了這些Aware接口的bean,在實(shí)例化bean時(shí)Spring會(huì)幫我們注入對(duì)應(yīng)的BeanFactory的實(shí)例。
          • BeanPostProcessor接口 ,實(shí)現(xiàn)了BeanPostProcessor接口的bean,在實(shí)例化bean時(shí)Spring會(huì)幫我們調(diào)用接口中的方法。
          • InitializingBean接口 ,實(shí)現(xiàn)了InitializingBean接口的bean,在實(shí)例化bean時(shí)Spring會(huì)幫我們調(diào)用接口中的方法。
          • DisposableBean接口 ,實(shí)現(xiàn)了BeanPostProcessor接口的bean,在該bean死亡時(shí)Spring會(huì)幫我們調(diào)用接口中的方法。

          設(shè)計(jì)意義:

          松耦合??梢詫⒃瓉?lái)硬編碼的依賴,通過(guò)Spring這個(gè)beanFactory這個(gè)工廠來(lái)注入依賴,也就是說(shuō)原來(lái)只有依賴方和被依賴方,現(xiàn)在我們引入了第三方——spring這個(gè)beanFactory,由它來(lái)解決bean之間的依賴問(wèn)題,達(dá)到了松耦合的效果.
          bean的額外處理。通過(guò)Spring接口的暴露,在實(shí)例化bean的階段我們可以進(jìn)行一些額外的處理,這些額外的處理只需要讓bean實(shí)現(xiàn)對(duì)應(yīng)的接口即可,那么spring就會(huì)在bean的生命周期調(diào)用我們實(shí)現(xiàn)的接口來(lái)處理該bean。[非常重要]

          2.工廠方法
          實(shí)現(xiàn)方式:FactoryBean接口。
          實(shí)現(xiàn)原理:實(shí)現(xiàn)了FactoryBean接口的bean是一類叫做factory的bean。其特點(diǎn)是,spring會(huì)在使用getBean()調(diào)用獲得該bean時(shí),會(huì)自動(dòng)調(diào)用該bean的getObject()方法,所以返回的不是factory這個(gè)bean,而是這個(gè)bean.getOjbect()方法的返回值。
          例子:典型的例子有spring與mybatis的結(jié)合。
          代碼示例:
          說(shuō)明:
          我們看上面該bean,因?yàn)閷?shí)現(xiàn)了FactoryBean接口,所以返回的不是 SqlSessionFactoryBean 的實(shí)例,而是它的 SqlSessionFactoryBean.getObject() 的返回值。

          3.單例模式
          Spring依賴注入Bean實(shí)例默認(rèn)是單例的。
          Spring的依賴注入(包括lazy-init方式)都是發(fā)生在AbstractBeanFactory的getBean里。getBean的doGetBean方法調(diào)用getSingleton進(jìn)行bean的創(chuàng)建。
          分析 getSingleton() 方法
          public Object getSingleton(String beanName){    //參數(shù)true設(shè)置標(biāo)識(shí)允許早期依賴    return getSingleton(beanName,true);}protected Object getSingleton(String beanName, boolean allowEarlyReference) {    //檢查緩存中是否存在實(shí)例    Object singletonObject = this.singletonObjects.get(beanName);    if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {        //如果為空,則鎖定全局變量并進(jìn)行處理。        synchronized (this.singletonObjects) {            //如果此bean正在加載,則不處理            singletonObject = this.earlySingletonObjects.get(beanName);            if (singletonObject == null && allowEarlyReference) {                //當(dāng)某些方法需要提前初始化的時(shí)候則會(huì)調(diào)用addSingleFactory 方法將對(duì)應(yīng)的ObjectFactory初始化策略存儲(chǔ)在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()過(guò)程圖

          ps:spring依賴注入時(shí),使用了 雙重判斷加鎖 的單例模式
          單例模式定義:保證一個(gè)類僅有一個(gè)實(shí)例,并提供一個(gè)訪問(wèn)它的全局訪問(wèn)點(diǎn)。
          spring對(duì)單例的實(shí)現(xiàn):spring中的單例模式完成了后半句話,即提供了全局的訪問(wèn)點(diǎn)BeanFactory。但沒(méi)有從構(gòu)造器級(jí)別去控制單例,這是因?yàn)閟pring管理的是任意的java對(duì)象。

          4. 適配器模式
          實(shí)現(xiàn)方式:SpringMVC中的適配器HandlerAdatper。
          實(shí)現(xiàn)原理:HandlerAdatper根據(jù)Handler規(guī)則執(zhí)行不同的Handler。
          實(shí)現(xiàn)過(guò)程:DispatcherServlet根據(jù)HandlerMapping返回的handler,向HandlerAdatper發(fā)起請(qǐng)求,處理Handler。
          HandlerAdapter根據(jù)規(guī)則找到對(duì)應(yīng)的Handler并讓其執(zhí)行,執(zhí)行完畢后Handler會(huì)向HandlerAdapter返回一個(gè)ModelAndView,最后由HandlerAdapter向DispatchServelet返回一個(gè)ModelAndView。
          實(shí)現(xiàn)意義:
          HandlerAdatper使得Handler的擴(kuò)展變得容易,只需要增加一個(gè)新的Handler和一個(gè)對(duì)應(yīng)的HandlerAdapter即可。
          因此Spring定義了一個(gè)適配接口,使得每一種Controller有一種對(duì)應(yīng)的適配器實(shí)現(xiàn)類,讓適配器代替controller執(zhí)行相應(yīng)的方法。這樣在擴(kuò)展Controller時(shí),只需要增加一個(gè)適配器類就完成了SpringMVC的擴(kuò)展了。


          5. 裝飾器模式

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

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


          6. 代理模式

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

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

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

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

          織入:把切面應(yīng)用到目標(biāo)對(duì)象并創(chuàng)建新的代理對(duì)象的過(guò)程。


          7. 觀察者模式

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

          具體實(shí)現(xiàn):

          事件機(jī)制的實(shí)現(xiàn)需要三個(gè)部分,事件源,事件,事件監(jiān)聽(tīng)器

          ApplicationEvent抽象類[事件]
          繼承自jdk的EventObject,所有的事件都需要繼承ApplicationEvent,并且通過(guò)構(gòu)造器參數(shù)source得到事件源.
          該類的實(shí)現(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)聽(tīng)器]
          繼承自jdk的EventListener,所有的監(jiān)聽(tīng)器都要實(shí)現(xiàn)這個(gè)接口。
          這個(gè)接口只有一個(gè)onApplicationEvent()方法,該方法接受一個(gè)ApplicationEvent或其子類對(duì)象作為參數(shù),在方法體中,可以通過(guò)不同對(duì)Event類的判斷來(lái)進(jìn)行相應(yīng)的處理。
          當(dāng)事件觸發(fā)時(shí)所有的監(jiān)聽(tīng)器都會(huì)收到消息。
          代碼:
          public interface ApplicationListener<E extends ApplicationEvent> extends EventListener {     void onApplicationEvent(E event);}
          ApplicationContext接口[事件源]
          ApplicationContext是spring中的全局容器,翻譯過(guò)來(lái)是”應(yīng)用上下文”。
          實(shí)現(xiàn)了ApplicationEventPublisher接口。

          職責(zé):

          負(fù)責(zé)讀取bean的配置文檔,管理bean的加載,維護(hù)bean之間的依賴關(guān)系,可以說(shuō)是負(fù)責(zé)bean的整個(gè)生命周期,再通俗一點(diǎn)就是我們平時(shí)所說(shuō)的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)聽(tīng)器.
          代碼:
          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. 策略模式

          實(shí)現(xiàn)方式:

          Spring框架的資源訪問(wèn)Resource接口。該接口提供了更強(qiáng)的資源訪問(wèn)能力,Spring 框架本身大量使用了 Resource 接口來(lái)訪問(wèn)底層資源。

          Resource 接口介紹

          source 接口是具體資源訪問(wèn)策略的抽象,也是所有資源訪問(wèn)類所實(shí)現(xiàn)的接口。
          Resource 接口主要提供了如下幾個(gè)方法:
          • getInputStream():定位并打開(kāi)資源,返回資源對(duì)應(yīng)的輸入流。每次調(diào)用都返回新的輸入流。調(diào)用者必須負(fù)責(zé)關(guān)閉輸入流。
          • exists():返回 Resource 所指向的資源是否存在。
          • isOpen():返回資源文件是否打開(kāi),如果資源文件不能多次讀取,每次讀取結(jié)束應(yīng)該顯式關(guān)閉,以防止資源泄漏。
          • getDescription():返回資源的描述信息,通常用于資源處理出錯(cuò)時(shí)輸出該信息,通常是全限定文件名或?qū)嶋H URL。
          • getFile:返回資源對(duì)應(yīng)的 File 對(duì)象。
          • getURL:返回資源對(duì)應(yīng)的 URL 對(duì)象。
          最后兩個(gè)方法通常無(wú)須使用,僅在通過(guò)簡(jiǎn)單方式訪問(wèn)無(wú)法實(shí)現(xiàn)時(shí),Resource 提供傳統(tǒng)的資源訪問(wèn)的功能。
          Resource 接口本身沒(méi)有提供訪問(wèn)任何底層資源的實(shí)現(xiàn)邏輯,針對(duì)不同的底層資源,Spring 將會(huì)提供不同的 Resource 實(shí)現(xiàn)類,不同的實(shí)現(xiàn)類負(fù)責(zé)不同的資源訪問(wèn)邏輯。
          Spring 為 Resource 接口提供了如下實(shí)現(xiàn)類:
          • UrlResource:訪問(wèn)網(wǎng)絡(luò)資源的實(shí)現(xiàn)類。
          • ClassPathResource:訪問(wèn)類加載路徑里資源的實(shí)現(xiàn)類。
          • FileSystemResource:訪問(wèn)文件系統(tǒng)里資源的實(shí)現(xiàn)類。
          • ServletContextResource:訪問(wèn)相對(duì)于 ServletContext 路徑里的資源的實(shí)現(xiàn)類.
          • InputStreamResource:訪問(wèn)輸入流資源的實(shí)現(xiàn)類。
          • ByteArrayResource:訪問(wèn)字節(jié)數(shù)組資源的實(shí)現(xiàn)類。
          這些 Resource 實(shí)現(xiàn)類,針對(duì)不同的的底層資源,提供了相應(yīng)的資源訪問(wèn)邏輯,并提供便捷的包裝,以利于客戶端程序的資源訪問(wèn)。


          9. 模版方法模式

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

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

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

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

          具體實(shí)現(xiàn):

          JDBC的抽象和對(duì)Hibernate的集成,都采用了一種理念或者處理方式,那就是模板方法模式與相應(yīng)的Callback接口相結(jié)合。
          采用模板方法模式是為了以一種統(tǒng)一而集中的方式來(lái)處理資源的獲取和釋放,以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是抽象類,不能夠獨(dú)立使用,我們每次進(jìn)行數(shù)據(jù)訪問(wèn)的時(shí)候都要給出一個(gè)相應(yīng)的子類實(shí)現(xiàn),這樣肯定不方便,所以就引入了回調(diào)。
          回調(diào)代碼
          public interface StatementCallback{    Object doWithStatement(Statement stmt);
          利用回調(diào)方法重寫(xiě)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沒(méi)有使用繼承?
          因?yàn)檫@個(gè)類的方法太多,但是我們還是想用到JdbcTemplate已有的穩(wěn)定的、公用的數(shù)據(jù)庫(kù)連接,那么我們?cè)趺崔k呢?
          我們可以把變化的東西抽出來(lái)作為一個(gè)參數(shù)傳入JdbcTemplate的方法中。但是變化的東西是一段代碼,而且這段代碼會(huì)用到JdbcTemplate中的變量。怎么辦?
          那我們就用回調(diào)對(duì)象吧。在這個(gè)回調(diào)對(duì)象中定義一個(gè)操縱JdbcTemplate中變量的方法,我們?nèi)?shí)現(xiàn)這個(gè)方法,就把變化的東西集中到這里了。然后我們?cè)賯魅脒@個(gè)回調(diào)對(duì)象到JdbcTemplate,從而完成了調(diào)用。


          推薦閱讀

          1、Spring Boot+Vue項(xiàng)目實(shí)戰(zhàn)

          2、B站:4小時(shí)上手MyBatis Plus

          3、一文搞懂前后端分離

          4、快速上手Spring Boot+Vue前后端分離


          楠哥簡(jiǎn)介

          資深 Java 工程師,微信號(hào) southwindss

          《Java零基礎(chǔ)實(shí)戰(zhàn)》一書(shū)作者

          騰訊課程官方 Java 面試官,今日頭條認(rèn)證大V

          GitChat認(rèn)證作者,B站認(rèn)證UP主(楠哥教你學(xué)Java)

          致力于幫助萬(wàn)千 Java 學(xué)習(xí)者持續(xù)成長(zhǎng)。




          有收獲,就點(diǎn)個(gè)在看 
          瀏覽 43
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評(píng)論
          圖片
          表情
          推薦
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <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>
                  影音先锋色资源网 | 日韩黄色电影在线观看 | 久久国产福利 | 午夜久久电影 | 插逼国产视频 |