Spring中必定要掌握的9種設計模式

一、簡單工廠(非23種設計模式中的一種)

1)實現方式:java

BeanFactory。Spring中的BeanFactory就是簡單工廠模式的體現,根據傳入一個惟一的標識來得到Bean對象,可是否是在傳入參數後建立仍是傳入參數前建立這個要根據具體狀況來定。spring

實質:sql

由一個工廠類根據傳入的參數,動態決定應該建立哪個產品類。數據庫

2)實現原理:設計模式

bean容器的啓動階段:數組

讀取bean的xml配置文件,將bean元素分別轉換成一個BeanDefinition對象。緩存

而後經過BeanDefinitionRegistry將這些bean註冊到beanFactory中,保存在它的一個ConcurrentHashMap中。微信

將BeanDefinition註冊到了beanFactory以後,在這裏Spring爲咱們提供了一個擴展的切口,容許咱們經過實現接口BeanFactoryPostProcessor 在此處來插入咱們定義的代碼。典型的例子就是:PropertyPlaceholderConfigurer,咱們通常在配置數據庫的dataSource時使用到的佔位符的值,就是它注入進去的。網絡

容器中bean的實例化階段:mybatis

實例化階段主要是經過反射或者CGLIB對bean進行實例化,在這個階段Spring又給咱們暴露了不少的擴展點:

各類的Aware接口,好比 BeanFactoryAware,對於實現了這些Aware接口的bean,在實例化bean時Spring會幫咱們注入對應的BeanFactory的實例。

BeanPostProcessor接口,實現了BeanPostProcessor接口的bean,在實例化bean時Spring會幫咱們調用接口中的方法。
InitializingBean接口,實現了InitializingBean接口的bean,在實例化bean時Spring會幫咱們調用接口中的方法。

DisposableBean接口,實現了BeanPostProcessor接口的bean,在該bean死亡時Spring會幫咱們調用接口中的方法。

3)設計意義:

鬆耦合。

能夠將原來硬編碼的依賴,經過Spring這個beanFactory這個工廠來注入依賴,也就是說原來只有依賴方和被依賴方,如今咱們引入了第三方——spring這個beanFactory,由它來解決bean之間的依賴問題,達到了鬆耦合的效果.

bean的額外處理。

經過Spring接口的暴露,在實例化bean的階段咱們能夠進行一些額外的處理,這些額外的處理只須要讓bean實現對應的接口便可,那麼spring就會在bean的生命週期調用咱們實現的接口來處理該bean。

二、工廠方法

1)實現方式:

FactoryBean接口。

2)實現原理:

實現了FactoryBean接口的bean是一類叫作factory的bean。其特色是,spring會在使用getBean()調用得到該bean時,會自動調用該bean的getObject()方法,因此返回的不是factory這個bean,而是這個bean.getOjbect()方法的返回值。

例子:

典型的例子有spring與mybatis的結合。

3)代碼示例:

說明:

咱們看上面該bean,由於實現了FactoryBean接口,因此返回的不是 SqlSessionFactoryBean 的實例,而是它的 SqlSessionFactoryBean.getObject() 的返回值。

三、單例模式

Spring依賴注入Bean實例默認是單例的。

Spring的依賴注入(包括lazy-init方式)都是發生在AbstractBeanFactory的getBean裏。getBean的doGetBean方法調用getSingleton進行bean的建立。

分析getSingleton()方法:

public Object getSingleton(String beanName){
    //參數true設置標識容許早期依賴
    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) {  
                //當某些方法須要提早初始化的時候則會調用addSingleFactory 方法將對應的ObjectFactory初始化策略存儲在singletonFactories
                ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
                if (singletonFactory != null) {
                    //調用預先設定的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對單例的實現:spring中的單例模式完成了後半句話,即提供了全局的訪問點BeanFactory。但沒有從構造器級別去控制單例,這是由於spring管理的是任意的java對象。

四、適配器模式

1)實現方式:

SpringMVC中的適配器HandlerAdatper。

2)實現原理:

HandlerAdatper根據Handler規則執行不一樣的Handler。

3)實現過程:

DispatcherServlet根據HandlerMapping返回的handler,向HandlerAdatper發起請求,處理Handler。

HandlerAdapter根據規則找到對應的Handler並讓其執行,執行完畢後Handler會向HandlerAdapter返回一個ModelAndView,最後由HandlerAdapter向DispatchServelet返回一個ModelAndView。

4)實現意義:

HandlerAdatper使得Handler的擴展變得容易,只須要增長一個新的Handler和一個對應的HandlerAdapter便可。

所以Spring定義了一個適配接口,使得每一種Controller有一種對應的適配器實現類,讓適配器代替controller執行相應的方法。這樣在擴展Controller時,只須要增長一個適配器類就完成了SpringMVC的擴展了。

五、裝飾器模式

1)實現方式:

Spring中用到的包裝器模式在類名上有兩種表現:一種是類名中含有Wrapper,另外一種是類名中含有Decorator。

2)實質:

動態地給一個對象添加一些額外的職責。

就增長功能來講,Decorator模式相比生成子類更爲靈活。

六、代理模式

1)實現方式:

AOP底層,就是動態代理模式的實現。

2)動態代理:

在內存中構建的,不須要手動編寫代理類

3)靜態代理:

須要手工編寫代理類,代理類引用被代理對象。

4)實現原理:

切面在應用運行的時刻被織入。通常狀況下,在織入切面時,AOP容器會爲目標對象建立動態的建立一個代理對象。SpringAOP就是以這種方式織入切面的。

織入:把切面應用到目標對象並建立新的代理對象的過程。

七、觀察者模式

1)實現方式:

spring的事件驅動模型使用的是 觀察者模式 ,Spring中Observer模式經常使用的地方是listener的實現。

2)具體實現:

事件機制的實現須要三個部分,事件源,事件,事件監聽器

ApplicationEvent抽象類[事件]

繼承自jdk的EventObject,全部的事件都須要繼承ApplicationEvent,而且經過構造器參數source獲得事件源.

該類的實現類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接口[事件監聽器]

繼承自jdk的EventListener,全部的監聽器都要實現這個接口。

這個接口只有一個onApplicationEvent()方法,該方法接受一個ApplicationEvent或其子類對象做爲參數,在方法體中,能夠經過不一樣對Event類的判斷來進行相應的處理。

當事件觸發時全部的監聽器都會收到消息。

代碼:

public interface ApplicationListener<E extends ApplicationEvent> extends EventListener {
     void onApplicationEvent(E event);
}
ApplicationContext接口[事件源]

ApplicationContext是spring中的全局容器,翻譯過來是」應用上下文」。

實現了ApplicationEventPublisher接口。

職責:

負責讀取bean的配置文檔,管理bean的加載,維護bean之間的依賴關係,能夠說是負責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方法須要調用其方法getApplicationEventMulticaster]

屬於事件廣播器,它的做用是把Applicationcontext發佈的Event廣播給全部的監聽器.

代碼:

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);  
    }  
  }  
}

八、策略模式

1)實現方式:

Spring框架的資源訪問Resource接口。該接口提供了更強的資源訪問能力,Spring 框架自己大量使用了 Resource 接口來訪問底層資源。

2)Resource 接口介紹

source 接口是具體資源訪問策略的抽象,也是全部資源訪問類所實現的接口。

Resource 接口主要提供了以下幾個方法:

getInputStream():定位並打開資源,返回資源對應的輸入流。每次調用都返回新的輸入流。調用者必須負責關閉輸入流。

exists():返回 Resource 所指向的資源是否存在。

isOpen():返回資源文件是否打開,若是資源文件不能屢次讀取,每次讀取結束應該顯式關閉,以防止資源泄漏。

getDescription():返回資源的描述信息,一般用於資源處理出錯時輸出該信息,一般是全限定文件名或實際 URL。

getFile:返回資源對應的 File 對象。

getURL:返回資源對應的 URL 對象。

最後兩個方法一般無須使用,僅在經過簡單方式訪問沒法實現時,Resource 提供傳統的資源訪問的功能。

Resource 接口自己沒有提供訪問任何底層資源的實現邏輯,針對不一樣的底層資源,Spring 將會提供不一樣的 Resource 實現類,不一樣的實現類負責不一樣的資源訪問邏輯。

Spring 爲 Resource 接口提供了以下實現類:

UrlResource:訪問網絡資源的實現類。

ClassPathResource:訪問類加載路徑裏資源的實現類。

FileSystemResource:訪問文件系統裏資源的實現類。

ServletContextResource:訪問相對於 ServletContext 路徑裏的資源的實現類.

InputStreamResource:訪問輸入流資源的實現類。

ByteArrayResource:訪問字節數組資源的實現類。

這些 Resource 實現類,針對不一樣的的底層資源,提供了相應的資源訪問邏輯,並提供便捷的包裝,以利於客戶端程序的資源訪問。

九、模版方法模式

1)經典模板方法定義:

父類定義了骨架(調用哪些方法及順序),某些特定方法由子類實現。

最大的好處:代碼複用,減小重複代碼。除了子類要實現的特定方法,其餘方法及方法調用順序都在父類中預先寫好了。

因此父類模板方法中有兩類方法:

共同的方法:全部子類都會用到的代碼

不一樣的方法:子類要覆蓋的方法,分爲兩種:

抽象方法:父類中的是抽象方法,子類必須覆蓋

鉤子方法:父類中是一個空方法,子類繼承了默認也是空的

注:爲何叫鉤子,子類能夠經過這個鉤子(方法),控制父類,由於這個鉤子實際是父類的方法(空方法)!

2)Spring模板方法模式實質:

是模板方法模式和回調模式的結合,是Template Method不須要繼承的另外一種實現方式。Spring幾乎全部的外接擴展都採用這種模式。

具體實現:

JDBC的抽象和對Hibernate的集成,都採用了一種理念或者處理方式,那就是模板方法模式與相應的Callback接口相結合。

採用模板方法模式是爲了以一種統一而集中的方式來處理資源的獲取和釋放,以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);  
}

引入回調緣由:

JdbcTemplate是抽象類,不可以獨立使用,咱們每次進行數據訪問的時候都要給出一個相應的子類實現,這樣確定不方便,因此就引入了回調。

回調代碼

public interface StatementCallback{  
    Object doWithStatement(Statement stmt);  
}    
利用回調方法重寫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已有的穩定的、公用的數據庫鏈接,那麼咱們怎麼辦呢?

咱們能夠把變化的東西抽出來做爲一個參數傳入JdbcTemplate的方法中。可是變化的東西是一段代碼,並且這段代碼會用到JdbcTemplate中的變量。怎麼辦?

那咱們就用回調對象吧。在這個回調對象中定義一個操縱JdbcTemplate中變量的方法,咱們去實現這個方法,就把變化的東西集中到這裏了。而後咱們再傳入這個回調對象到JdbcTemplate,從而完成了調用。

​文源網絡,僅供學習之用,若有侵權,聯繫刪除。 關注微信公衆號:Java圈子,領取學習資料。
相關文章
相關標籤/搜索