互聯(lián)網(wǎng)/程序員/技術(shù)/資料共享
來自: iCoding91 blog.csdn.net/caoxiaohong1005
Spring中涉及的設(shè)計(jì)模式總結(jié),建議,收藏轉(zhuǎn)發(fā),保存 1.簡單工廠(非23種設(shè)計(jì)模式中的一種) 實(shí)現(xiàn)方式: BeanFactory。Spring中的BeanFactory就是簡單工廠模式的體現(xiàn),根據(jù)傳入一個唯一的標(biāo)識來獲得Bean對象,但是否是在傳入?yún)?shù)后創(chuàng)建還是傳入?yún)?shù)前創(chuàng)建這個要根據(jù)具體情況來定。 實(shí)質(zhì): 由一個工廠類根據(jù)傳入的參數(shù),動態(tài)決定應(yīng)該創(chuàng)建哪一個產(chǎn)品類。 實(shí)現(xiàn)原理: 1、讀取bean的xml配置文件,將bean元素分別轉(zhuǎn)換成一個BeanDefinition對象。 2、然后通過BeanDefinitionRegistry將這些bean注冊到beanFactory中,保存在它的一個ConcurrentHashMap中。 3、將BeanDefinition注冊到了beanFactory之后,在這里Spring為我們提供了一個擴(kuò)展的切口,允許我們通過實(shí)現(xiàn)接口BeanFactoryPostProcessor 在此處來插入我們定義的代碼。 4、典型的例子就是:PropertyPlaceholderConfigurer,我們一般在配置數(shù)據(jù)庫的dataSource時使用到的占位符的值,就是它注入進(jìn)去的。 實(shí)例化階段主要是通過反射或者CGLIB對bean進(jìn)行實(shí)例化,在這個階段Spring又給我們暴露了很多的擴(kuò)展點(diǎn): 1、各種的Aware接口 ,比如 BeanFactoryAware,對于實(shí)現(xiàn)了這些Aware接口的bean,在實(shí)例化bean時Spring會幫我們注入對應(yīng)的BeanFactory的實(shí)例。2、BeanPostProcessor接口 ,實(shí)現(xiàn)了BeanPostProcessor接口的bean,在實(shí)例化bean時Spring會幫我們調(diào)用接口中的方法。3、InitializingBean接口 ,實(shí)現(xiàn)了InitializingBean接口的bean,在實(shí)例化bean時Spring會幫我們調(diào)用接口中的方法。4、DisposableBean接口 ,實(shí)現(xiàn)了BeanPostProcessor接口的bean,在該bean死亡時Spring會幫我們調(diào)用接口中的方法。設(shè)計(jì)意義: 松耦合。 可以將原來硬編碼的依賴,通過Spring這個beanFactory這個工廠來注入依賴,也就是說原來只有依賴方和被依賴方,現(xiàn)在我們引入了第三方——spring這個beanFactory,由它來解決bean之間的依賴問題,達(dá)到了松耦合的效果.bean的額外處理。 通過Spring接口的暴露,在實(shí)例化bean的階段我們可以進(jìn)行一些額外的處理,這些額外的處理只需要讓bean實(shí)現(xiàn)對應(yīng)的接口即可,那么spring就會在bean的生命周期調(diào)用我們實(shí)現(xiàn)的接口來處理該bean。[非常重要] 2.工廠方法 實(shí)現(xiàn)方式: 實(shí)現(xiàn)原理: 實(shí)現(xiàn)了FactoryBean接口的bean是一類叫做factory的bean。其特點(diǎn)是,spring會在使用getBean()調(diào)用獲得該bean時,會自動調(diào)用該bean的getObject()方法,所以返回的不是factory這個bean,而是這個bean.getOjbect()方法的返回值。 例子: 典型的例子有spring與mybatis的結(jié)合。 我們看上面該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)建。 public Object getSingleton (String beanName) { //參數(shù)true設(shè)置標(biāo)識允許早期依賴 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)某些方法需要提前初始化的時候則會調(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 ); }ps:spring依賴注入時,使用了 雙重判斷加鎖 的單例模式 單例模式定義: 保證一個類僅有一個實(shí)例,并提供一個訪問它的全局訪問點(diǎn)。spring對單例的實(shí)現(xiàn): spring中的單例模式完成了后半句話,即提供了全局的訪問點(diǎn)BeanFactory。但沒有從構(gòu)造器級別去控制單例,這是因?yàn)閟pring管理的是任意的java對象。4.適配器模式 實(shí)現(xiàn)方式: SpringMVC中的適配器HandlerAdatper。 實(shí)現(xiàn)原理: HandlerAdatper根據(jù)Handler規(guī)則執(zhí)行不同的Handler。 實(shí)現(xiàn)過程: DispatcherServlet根據(jù)HandlerMapping返回的handler,向HandlerAdatper發(fā)起請求,處理Handler。 HandlerAdapter根據(jù)規(guī)則找到對應(yīng)的Handler并讓其執(zhí)行,執(zhí)行完畢后Handler會向HandlerAdapter返回一個ModelAndView,最后由HandlerAdapter向DispatchServelet返回一個ModelAndView。 實(shí)現(xiàn)意義: HandlerAdatper使得Handler的擴(kuò)展變得容易,只需要增加一個新的Handler和一個對應(yīng)的HandlerAdapter即可。 因此Spring定義了一個適配接口,使得每一種Controller有一種對應(yīng)的適配器實(shí)現(xiàn)類,讓適配器代替controller執(zhí)行相應(yīng)的方法。這樣在擴(kuò)展Controller時,只需要增加一個適配器類就完成了SpringMVC的擴(kuò)展了。 5.裝飾器模式 實(shí)現(xiàn)方式: Spring中用到的包裝器模式在類名上有兩種表現(xiàn):一種是類名中含有Wrapper,另一種是類名中含有Decorator。 實(shí)質(zhì): 動態(tài)地給一個對象添加一些額外的職責(zé)。 就增加功能來說,Decorator模式相比生成子類更為靈活。 6.代理模式 實(shí)現(xiàn)方式: AOP底層,就是動態(tài)代理模式的實(shí)現(xiàn)。 動態(tài)代理: 在內(nèi)存中構(gòu)建的,不需要手動編寫代理類 靜態(tài)代理: 實(shí)現(xiàn)原理: 切面在應(yīng)用運(yùn)行的時刻被織入。一般情況下,在織入切面時,AOP容器會為目標(biāo)對象創(chuàng)建動態(tài)的創(chuàng)建一個代理對象。SpringAOP就是以這種方式織入切面的。 織入:把切面應(yīng)用到目標(biāo)對象并創(chuàng)建新的代理對象的過程。 7.觀察者模式 實(shí)現(xiàn)方式: spring的事件驅(qū)動模型使用的是 觀察者模式 ,Spring中Observer模式常用的地方是listener的實(shí)現(xiàn)。 具體實(shí)現(xiàn): 事件機(jī)制的實(shí)現(xiàn)需要三個部分,事件源,事件,事件監(jiān)聽器 繼承自jdk的EventObject,所有的事件都需要繼承ApplicationEvent,并且通過構(gòu)造器參數(shù)source得到事件源. 該類的實(shí)現(xiàn)類ApplicationContextEvent表示ApplicaitonContext的容器事件. publicabstract class ApplicationEvent extends EventObject { privatestaticfinallong serialVersionUID = 7099057708183571937L ; privatefinallong timestamp; public ApplicationEvent (Object source) { super (source); this .timestamp = System.currentTimeMillis(); } public final long getTimestamp () { returnthis .timestamp; } }ApplicationListener接口[事件監(jiān)聽器] 繼承自jdk的EventListener,所有的監(jiān)聽器都要實(shí)現(xiàn)這個接口。 這個接口只有一個onApplicationEvent()方法,該方法接受一個ApplicationEvent或其子類對象作為參數(shù),在方法體中,可以通過不同對Event類的判斷來進(jìn)行相應(yīng)的處理。 當(dāng)事件觸發(fā)時所有的監(jiān)聽器都會收到消息。 public interface ApplicationListener <E extends ApplicationEvent > extends EventListener { void onApplicationEvent (E event) ; }ApplicationContext接口[事件源] ApplicationContext是spring中的全局容器,翻譯過來是”應(yīng)用上下文”。 實(shí)現(xiàn)了ApplicationEventPublisher接口。 職責(zé): 負(fù)責(zé)讀取bean的配置文檔,管理bean的加載,維護(hù)bean之間的依賴關(guān)系,可以說是負(fù)責(zé)bean的整個生命周期,再通俗一點(diǎn)就是我們平時所說的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)聽器. publicabstract 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框架的資源訪問Resource接口。該接口提供了更強(qiáng)的資源訪問能力,Spring 框架本身大量使用了 Resource 接口來訪問底層資源。 Resource 接口介紹 source 接口是具體資源訪問策略的抽象,也是所有資源訪問類所實(shí)現(xiàn)的接口。 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 對象。最后兩個方法通常無須使用,僅在通過簡單方式訪問無法實(shí)現(xiàn)時,Resource 提供傳統(tǒng)的資源訪問的功能。 Resource 接口本身沒有提供訪問任何底層資源的實(shí)現(xiàn)邏輯,針對不同的底層資源,Spring 將會提供不同的 Resource 實(shí)現(xiàn)類,不同的實(shí)現(xiàn)類負(fù)責(zé)不同的資源訪問邏輯。 Spring 為 Resource 接口提供了如下實(shí)現(xiàn)類: UrlResource: 訪問網(wǎng)絡(luò)資源的實(shí)現(xiàn)類。ClassPathResource: 訪問類加載路徑里資源的實(shí)現(xiàn)類。FileSystemResource: 訪問文件系統(tǒng)里資源的實(shí)現(xiàn)類。ServletContextResource: 訪問相對于 ServletContext 路徑里的資源的實(shí)現(xiàn)類.InputStreamResource: 訪問輸入流資源的實(shí)現(xiàn)類。ByteArrayResource: 訪問字節(jié)數(shù)組資源的實(shí)現(xiàn)類。這些 Resource 實(shí)現(xiàn)類,針對不同的的底層資源,提供了相應(yīng)的資源訪問邏輯,并提供便捷的包裝,以利于客戶端程序的資源訪問。 9.模版方法模式 經(jīng)典模板方法定義: 父類定義了骨架(調(diào)用哪些方法及順序),某些特定方法由子類實(shí)現(xiàn)。 最大的好處:代碼復(fù)用,減少重復(fù)代碼。除了子類要實(shí)現(xiàn)的特定方法,其他方法及方法調(diào)用順序都在父類中預(yù)先寫好了。 鉤子方法:父類中是一個空方法,子類繼承了默認(rèn)也是空的 注:為什么叫鉤子,子類可以通過這個鉤子(方法),控制父類,因?yàn)檫@個鉤子實(shí)際是父類的方法(空方法)! Spring模板方法模式實(shí)質(zhì): 是模板方法模式和回調(diào)模式的結(jié)合,是Template Method不需要繼承的另一種實(shí)現(xiàn)方式。Spring幾乎所有的外接擴(kuò)展都采用這種模式。 具體實(shí)現(xiàn): JDBC的抽象和對Hibernate的集成,都采用了一種理念或者處理方式,那就是模板方法模式與相應(yīng)的Callback接口相結(jié)合。 采用模板方法模式是為了以一種統(tǒng)一而集中的方式來處理資源的獲取和釋放,以JdbcTempalte為例: publicabstract class JdbcTemplate { publicfinal 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); } } protectedabstract Object executeWithStatement(Statement stmt, String sql); }引入回調(diào)原因: JdbcTemplate是抽象類,不能夠獨(dú)立使用,我們每次進(jìn)行數(shù)據(jù)訪問的時候都要給出一個相應(yīng)的子類實(shí)現(xiàn),這樣肯定不方便,所以就引入了回調(diào)。 public interface StatementCallback { Object doWithStatement(Statement stmt); }利用回調(diào)方法重寫JdbcTemplate方法 public class JdbcTemplate { publicfinal 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); } } ...//其它方法定義 }JdbcTemplate jdbcTemplate=...; final String sql=...; StatementCallback callback=new StatementCallback(){ public Object=doWithStatement(Statement stmt){ return ...; } } jdbcTemplate.execute(callback); 為什么JdbcTemplate沒有使用繼承? 因?yàn)檫@個類的方法太多,但是我們還是想用到JdbcTemplate已有的穩(wěn)定的、公用的數(shù)據(jù)庫連接,那么我們怎么辦呢? 我們可以把變化的東西抽出來作為一個參數(shù)傳入JdbcTemplate的方法中。但是變化的東西是一段代碼,而且這段代碼會用到JdbcTemplate中的變量。怎么辦? 那我們就用回調(diào)對象吧。在這個回調(diào)對象中定義一個操縱JdbcTemplate中變量的方法,我們?nèi)?shí)現(xiàn)這個方法,就把變化的東西集中到這里了。然后我們再傳入這個回調(diào)對象到JdbcTemplate,從而完成了調(diào)用。 推薦閱讀:
Spring Boot vue完整的外賣系統(tǒng),手機(jī)端和后臺管理api 源碼贈送
Java 對象不使用時,為什么要賦值為 null?
5T技術(shù)資源大放送!包括但不限于:C/C++,Linux,Python,Java,PHP,人工智能,單片機(jī),樹莓派,等等。在公眾號內(nèi)回復(fù)「 2048 」,即可免費(fèi)獲取!!
微信掃描二維碼,關(guān)注我的公眾號
朕已閱