解讀Spring框架中常用的設計模式
一、淺談控制反轉(IOC)與依賴注入(DI)
IOC(Inversion of Control)是Spring中一個非常重要的概念,它不是什么技術,而是一種解耦的設計思想。
它主要的額目的是借助于第三方(Spring中的IOC容器)實現具有依賴關系的的對象之間的解耦(IOC容易管理對象,你只管使用即可),從而降低代碼之間的耦合度。
它不是一個模式,而是一種設計原則,但以下模式(但不限于)實現了IOC的設計原則。
補充:對控制反轉的理解:
舉個例子“對象A依賴了對象B,當對象A需要使用對象B的時候必須自己去創(chuàng)建。
但是當系統(tǒng)引入了IOC容器后,對象A和對象B之前就失去了直接的聯系。
這個時候,當對象A需要使用對象B的時候,我們可以指定IOC容器去創(chuàng)建一個對象B注入到對象A中”。
對象A獲得對象B的過程,由主動行為變成了被動行為,控制權實現了反轉,這就是控制反轉名字的由來。
DI(Dependency Inject)依賴注入實現控制反轉的的一種實現方式,依賴注入就是將實例變量傳入到一個對象中去。
二、Spring框架中的設計模式
1)工廠設計模式(簡單工廠和工廠方法)
Spring使用工廠模式可以通過BeanFactory或ApplicationContext創(chuàng)建bean對象。
兩者對比:
BeanFactory
:延遲注入(使用到某個 bean 的時候才會注入),相比于BeanFactory來說會占用更少的內存,程序啟動速度更快。ApplicationContext
:容器啟動的時候,不管你用沒用到,一次性創(chuàng)建所有 bean 。BeanFactory 僅提供了最基本的依賴注入支持,ApplicationContext 擴展了 BeanFactory ,除了有BeanFactory的功能還有額外更多功能,所以一般開發(fā)人員使用ApplicationContext會更多。
2)單例設計模式
Spring中bean的默認作用域就是singleton。
除了singleton作用域,Spring bean還有下面幾種作用域:
prototype
:每次請求都會創(chuàng)建一個新的 bean 實例。request
:每一次HTTP請求都會產生一個新的bean,該bean僅在當前HTTP request內有效。session
:每一次HTTP請求都會產生一個新的 bean,該bean僅在當前 HTTP session 內有效。global-session
:全局session作用域,僅僅在基于portlet的web應用中才有意義,Spring5已經沒有了。Portlet是能夠生成語義代碼(例如:HTML)片段的小型Java Web插件。它們基于portlet容器,可以像servlet一樣處理HTTP請求。但是,與 servlet 不同,每個 portlet 都有不同的會話。
Spring實現單例的方式:
xml格式:
<bean id="userService" class="top.snailclimb.UserService" scope="singleton"/>
注解:
@Scope(value = "singleton")
Spring通過ConcurrentHashMap實現單例注冊表的特殊方式實現單例模式。
Spring實現單例的核心代碼如下:
// 通過 ConcurrentHashMap(線程安全) 實現單例注冊表 private final Map<String, Object> singletonObjects = new ConcurrentHashMap<String, Object>(64); public Object getSingleton(String beanName, ObjectFactory<?> singletonFactory) { Assert.notNull(beanName, "'beanName' must not be null"); synchronized (this.singletonObjects) { // 檢查緩存中是否存在實例 Object singletonObject = this.singletonObjects.get(beanName); if (singletonObject == null) { //...省略了很多代碼 try { singletonObject = singletonFactory.getObject(); } //...省略了很多代碼 // 如果實例對象在不存在,我們注冊到單例注冊表中。 addSingleton(beanName, singletonObject); } return (singletonObject != NULL_OBJECT ? singletonObject : null); } } //將對象添加到單例注冊表 protected void addSingleton(String beanName, Object singletonObject) { synchronized (this.singletonObjects) { this.singletonObjects.put(beanName, (singletonObject != null ? singletonObject : NULL_OBJECT)); } } }
3)代理設計模式
Spring AOP就是基于動態(tài)代理的,如果要代理的對象,實現了某個接口,那么Spring AOP會使用JDK Proxy,去創(chuàng)建代理對象,而對于沒有實現接口的對象,就無法使用JDK Proxy去進行代理了,這時候Spring AOP會使用Cglib,這時候Spring AOP會使用Cglib生成一個被代理對象的子類來作為代理。
如下圖所示:
當然你也可以使用AspectJ,Spring AOP已經繼承了AspectJ,AspectJ應該算的上是java生態(tài)系統(tǒng)中最完整的AOP框架了。
Spring AOP和AspectJ AOP有什么區(qū)別?
Spring AOP屬于運行時增強,而AspectJ是編譯時增強。Spring AOP基于代理,而AspectJ基于字節(jié)碼操作。
Spring AOP已經集成了AspectJ,AsectJ應該算的上是Java生態(tài)系統(tǒng)中最完整的AOP框架了。
AspectJ相比于Spring AOP功能更加強大,但是Spring AOP相對來說更簡單,如果我們的切面比較少,那么兩者的性能差異不大。
但是當切面太多的話,最好選擇AspectJ,它比Spring AOP快很多。
4)模板方法設計模式
模板方法模式是一種行為設計模式,它定義一個操作中的算法的骨架,而將一些步驟延遲到子類中。
模板方法使得子類可以在不改變一個算法的結構即可重定義該算法的默寫特定步驟的實現方式。
例如:
public abstract class Template { //這是我們的模板方法 public final void TemplateMethod(){ PrimitiveOperation1(); PrimitiveOperation2(); PrimitiveOperation3(); } protected void PrimitiveOperation1(){ //當前類實現 } //被子類實現的方法 protected abstract void PrimitiveOperation2(); protected abstract void PrimitiveOperation3(); } public class TemplateImpl extends Template { @Override public void PrimitiveOperation2() { //當前類實現 } @Override public void PrimitiveOperation3() { //當前類實現 } }
Spring中jdbcTemplate、hibernateTemplate等以Template結尾的對數據庫操作的類,它們就使用到模板模式。
一般情況下,我們都是使用繼承的方式來實現模板模式,但是Spring并沒有使用這種方式,而是使用Callback模式與模板方法配合,既達到了代碼復用的效果,同時增加了靈活性。
5)觀察者設計模式
觀察者設計模式是一種對象行為模式。它表示的是一種對象與對象之間具有依賴關系,當一個對象發(fā)生改變時,這個對象鎖依賴的對象也會做出反應。Spring事件驅動模型就是觀察者模式很經典的應用。
事件角色:ApplicationEvent(org.springframework.context包下)充當事件的角色,這是一個抽象類。
事件監(jiān)聽者角色:ApplicationListener充當了事件監(jiān)聽者的角色,它是一個接口,里面只定義了一個onApplicationEvent()方法來處理ApplicationEvent。
事件發(fā)布者角色:ApplicationEventPublisher充當了事件的發(fā)布者,它也是個接口。
Spring事件流程總結:
- 定義一個事件: 實現一個繼承自 ApplicationEvent,并且寫相應的構造函數;
- 定義一個事件監(jiān)聽者:實現 ApplicationListener 接口,重寫 onApplicationEvent() 方法;
- 使用事件發(fā)布者發(fā)布消息: 可以通過 ApplicationEventPublisher 的 publishEvent() 方法發(fā)布消息。
例如:
// 定義一個事件,繼承自ApplicationEvent并且寫相應的構造函數 public class DemoEvent extends ApplicationEvent{ private static final long serialVersionUID = 1L; private String message; public DemoEvent(Object source,String message){ super(source); this.message = message; } public String getMessage() { return message; } // 定義一個事件監(jiān)聽者,實現ApplicationListener接口,重寫 onApplicationEvent() 方法; @Component public class DemoListener implements ApplicationListener<DemoEvent>{ //使用onApplicationEvent接收消息 @Override public void onApplicationEvent(DemoEvent event) { String msg = event.getMessage(); System.out.println("接收到的信息是:"+msg); } } // 發(fā)布事件,可以通過ApplicationEventPublisher 的 publishEvent() 方法發(fā)布消息。 @Component public class DemoPublisher { @Autowired ApplicationContext applicationContext; public void publish(String message){ //發(fā)布事件 applicationContext.publishEvent(new DemoEvent(this, message)); } }
6)適配器設計模式
適配器設計模式將一個接口轉換成客戶希望的另一個接口,適配器模式使得接口不兼容的那些類可以一起工作,其別名為包裝器。
在Spring MVC中,DispatcherServlet根據請求信息調用HandlerMapping,解析請求對應的Handler,解析到對應的Handler(也就是我們常說的Controller控制器)后,開始由HandlerAdapter適配器處理。
為什么要在Spring MVC中使用適配器模式?
Spring MVC中的Controller種類眾多不同類型的Controller通過不同的方法來對請求進行處理,有利于代碼的維護拓展。
7)裝飾者設計模式
裝飾者設計模式可以動態(tài)地給對象增加些額外的屬性或行為。
相比于使用繼承,裝飾者模式更加靈活。
Spring 中配置DataSource的時候,DataSource可能是不同的數據庫和數據源。
我們能否根據客戶的需求在少修改原有類的代碼下切換不同的數據源?
這個時候據需要用到裝飾者模式。
8)策略設計模式
Spring 框架的資源訪問接口就是基于策略設計模式實現的。該接口提供了更強的資源訪問能力,Spring框架本身大量使用了Resource接口來訪問底層資源。
Resource接口本身沒有提供訪問任何底層資源的實現邏輯,針對不同的額底層資源,Spring將會提供不同的Resource實現類,不同的實現類負責不同的資源訪問類型。
Spring 為 Resource 接口提供了如下實現類:
UrlResource
:訪問網絡資源的實現類。ClassPathResource
:訪問類加載路徑里資源的實現類。FileSystemResource
:訪問文件系統(tǒng)里資源的實現類。ServletContextResource
:訪問相對于 ServletContext 路徑里的資源的實現類.InputStreamResource
:訪問輸入流資源的實現類。ByteArrayResource
:訪問字節(jié)數組資源的實現類。
這些 Resource 實現類,針對不同的的底層資源,提供了相應的資源訪問邏輯,并提供便捷的包裝,以利于客戶端程序的資源訪問。
總結
以上為個人經驗,希望能給大家一個參考,也希望大家多多支持腳本之家。
相關文章
IDEA?Error:java:無效的源發(fā)行版:13的解決過程
之前用idea運行時,也會出現這種情況,后面通過網上的資料解決了這個問題,下面這篇文章主要給大家介紹了關于IDEA?Error:java:無效的源發(fā)行版:13的解決過程,需要的朋友可以參考下2023-01-01詳解Intellij IDEA中.properties文件中文顯示亂碼問題的解決
這篇文章主要介紹了詳解Intellij IDEA中.properties文件中文顯示亂碼問題的解決,具有一定的參考價值,感興趣的小伙伴們可以參考一下2017-11-11SpringMVC訪問controller報錯404的解決辦法(總結超詳細)
純注解配置SpringMVC程序,使用tomcat8.5.95版本啟動,能啟動成功并且訪問index.jsp頁面,但是訪問/save時出現404無法訪問,本文給大家介紹了SpringMVC訪問controller報錯404的解決辦法,文章總結的非常詳細,需要的朋友可以參考下2024-05-05