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

#FormatImgID_1##FormatImgID_2#

實現方式:BeanFactory。Spring中的BeanFactory就是簡單工廠模式的體現,根據傳入一個唯一的標識來獲得Bean物件,但是否是在傳入引數後建立還是傳入引數前建立這個要根據具體情況來定。

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

實現原理:

bean容器的啟動階段:

讀取bean的xml配置檔案,將bean元素分別轉換成一個BeanDefinition物件。

然後透過BeanDefinitionRegistry將這些bean註冊到beanFactory中,儲存在它的一個ConcurrentHashMap中。

將BeanDefinition註冊到了beanFactory之後,在這裡Spring為我們提供了一個擴充套件的切口,允許我們透過實現介面BeanFactoryPostProcessor 在此處來插入我們定義的程式碼。典型的例子就是:PropertyPlaceholderConfigurer,我們一般在配置資料庫的dataSource時使用到的佔位符的值,就是它注入進去的。

容器中bean的例項化階段,例項化階段主要是透過反射或者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會幫我們呼叫介面中的方法。

設計意義:

松耦合。可以將原來硬編碼的依賴,透過Spring這個beanFactory這個工長來注入依賴,也就是說原來只有依賴方和被依賴方,現在我們引入了第三方——Spring這個beanFactory,由它來解決bean之間的依賴問題,達到了松耦合的效果。

bean的額外處理。透過Spring介面的暴露,在例項化bean的階段我們可以進行一些額外的處理,這些額外的處理只需要讓bean實現對應的介面即可,那麼spring就會在bean的生命週期呼叫我們實現的介面來處理該bean。

工廠方法

#FormatImgID_3##FormatImgID_4#

實現方式:FactoryBean介面。

實現原理:實現了FactoryBean介面的bean是一類叫做factory的bean。其特點是,spring會在使用getBean()呼叫獲得該bean時,會自動呼叫該bean的getObject()方法,所以返回的不是factory這個bean,而是這個bean。getOjbect()方法的返回值。

例子:

典型的例子有Spring與MyBatis的結合。

程式碼示例:

Spring中經典的9種設計模式

說明:我們看上面該bean,因為實現了FactoryBean介面,所以返回的不是SqlSessionFactoryBean的例項,而是它的 SqlSessionFactoryBean。getObject()的返回值。

單例模式

#FormatImgID_7##FormatImgID_8#

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中經典的9種設計模式

單例模式定義:保證一個類僅有一個例項,並提供一個訪問它的全域性訪問點。

Spring對單例的實現:Spring中的單例模式完成了後半句話,即提供了全域性的訪問點BeanFactory。但沒有從構造器級別去控制單例,這是因為Spring管理的是任意的Java物件。

介面卡模式

#FormatImgID_11##FormatImgID_12#

實現方式:SpringMVC中的介面卡HandlerAdatper。

實現原理:HandlerAdatper根據Handler規則執行不同的Handler。

實現過程:DispatcherServlet根據HandlerMapping返回的handler,向HandlerAdatper發起請求,處理Handler。HandlerAdapter根據規則找到對應的Handler並讓其執行,執行完畢後Handler會向HandlerAdapter返回一個ModelAndView,最後由HandlerAdapter向DispatchServelet返回一個ModelAndView。

實現意義:HandlerAdatper使得Handler的擴充套件變得容易,只需要增加一個新的Handler和一個對應的HandlerAdapter即可。因此Spring定義了一個適配介面,使得每一種Controller有一種對應的介面卡實現類,讓介面卡代替controller執行相應的方法。這樣在擴充套件Controller時,只需要增加一個介面卡類就完成了SpringMVC的擴充套件了。

裝飾器模式

#FormatImgID_13##FormatImgID_14#

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

實質:

動態地給一個物件新增一些額外的職責。

就增加功能來說,Decorator模式相比生成子類更為靈活。

代理模式

#FormatImgID_15##FormatImgID_16#

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

動態代理:在記憶體中構建的,不需要手動編寫代理類

靜態代理:需要手工編寫代理類,代理類引用被代理物件。

實現原理:切面在應用執行的時刻被織入。一般情況下,在織入切面時,AOP容器會為目標物件建立動態的建立一個代理物件。SpringAOP就是以這種方式織入切面的。

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

觀察者模式

#FormatImgID_17##FormatImgID_18#

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

具體實現:事件機制的實現需要三個部分,即:事件源、事件、事件監聽器。

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

}

}

}

策略模式

#FormatImgID_19##FormatImgID_20#

實現方式:Spring框架的資源訪問Resource介面。該介面提供了更強的資源訪問能力,Spring框架本身大量使用了Resource介面來訪問底層資源。

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實現類,針對不同的的底層資源,提供了相應的資源訪問邏輯,並提供便捷的包裝,以利於客戶端程式的資源訪問。

模版方法模式

#FormatImgID_21##FormatImgID_22#

經典模板方法定義:

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

最大的好處:程式碼複用,減少重複程式碼。除了子類要實現的特定方法,其他方法及方法呼叫順序都在父類中預先寫好了。

所以父類模板方法中有兩類方法:

共同的方法:所有子類都會用到的程式碼

不同的方法:子類要覆蓋的方法,分為兩種:

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

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

注:為什麼叫鉤子,子類可以透過這個鉤子(方法),控制父類,因為這個鉤子實際是父類的方法(空方法)!

Spring模板方法模式實質:是模板方法模式和回撥模式的結合,是Template Method不需要繼承的另一種實現方式。Spring幾乎所有的外接擴充套件都採用這種模式。

具體實現:JDBC的抽象和對Hibernate的整合,都採用了一種理念或者處理方式,那就是模板方法模式與相應的Callback介面相結合。

採用模板方法模式是為了以一種統一而集中的方式來處理資源的獲取和釋放,以JdbcTemplate為例:

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,從而完成了呼叫。

原文連結:

https://

blog。csdn。net/caoxiaoho

ng1005/article/details/80039656