1)工廠設計模式(簡單工廠和工廠方法)
Spring使用工廠模式可以通過BeanFactory或ApplicationContext創建bean對象。
兩者對比:
- BeanFactory :延遲注入(使用到某個 bean 的時候纔會注入),相比於BeanFactory來說會佔用更少的內存,程序啓動速度更快。
- ApplicationContext :容器啓動的時候,不管你用沒用到,一次性創建所有 bean 。BeanFactory 僅提供了最基本的依賴注入支持,ApplicationContext 擴展了 BeanFactory ,除了有BeanFactory的功能還有額外更多功能,所以一般開發人員使用ApplicationContext會更多。
2)單例設計模式
Spring中bean的默認作用域就是singleton。除了singleton作用域,Spring bean還有下面幾種作用域:
- prototype : 每次請求都會創建一個新的 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下默認的bean均爲singleton,可以通過singleton=“true|false” 或者 scope=“?”來指定
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就是基於動態代理的,如果要代理的對象,實現了某個接口,那麼Spring AOP會使用JDK Proxy,去創建代理對象,而對於沒有實現接口的對象,就無法使用JDK Proxy去進行代理了,這時候Spring AOP會使用Cglib,這時候Spring AOP會使用Cglib生成一個被代理對象的子類來作爲代理。
1、JDK動態代理
利用攔截器(攔截器必須實現InvocationHanlder)加上反射機制生成一個實現代理接口的匿名類,
在調用具體方法前調用InvokeHandler來處理。
2、CGLiB動態代理
利用ASM開源包,對代理對象類的class文件加載進來,通過修改其字節碼生成子類來處理。
如下圖所示:
Spring如何選擇用JDK還是CGLiB?
- 1)當Bean實現接口時,Spring就會用JDK的動態代理。
- 2)當Bean沒有實現接口時,Spring使用CGlib是實現。
- 3)可以強制使用CGlib(在spring配置中加入<aop:aspectj-autoproxy proxy-target-class="true"/>)。
當然你也可以使用AspectJ,Spring AOP已經繼承了AspectJ,AspectJ應該算的上是java生態系統中最完整的AOP框架了。
Spring AOP和AspectJ AOP有什麼區別?
Spring AOP屬於運行時增強,而AspectJ是編譯時增強。Spring AOP基於代理,而AspectJ基於字節碼操作。
Spring AOP已經集成了AspectJ,AsectJ應該算的上是Java生態系統中最完整的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)觀察者設計模式
觀察者設計模式是一種對象行爲模式。它表示的是一種對象與對象之間具有依賴關係,當一個對象發生改變時,這個對象鎖依賴的對象也會做出反應。Spring事件驅動模型就是觀察者模式很經典的應用。
- 事件角色:ApplicationEvent(org.springframework.context包下)充當事件的角色,這是一個抽象類。
- 事件監聽者角色:ApplicationListener充當了事件監聽者的角色,它是一個接口,裏面只定義了一個onApplicationEvent()方法來處理ApplicationEvent。
- 事件發佈者角色:ApplicationEventPublisher充當了事件的發佈者,它也是個接口。
Spring事件流程總結:
- 定義一個事件: 實現一個繼承自 ApplicationEvent,並且寫相應的構造函數;
- 定義一個事件監聽者:實現 ApplicationListener 接口,重寫 onApplicationEvent() 方法;
- 使用事件發佈者發佈消息: 可以通過 ApplicationEventPublisher 的 publishEvent() 方法發佈消息。
例如:
// 定義一個事件,繼承自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;
}
// 定義一個事件監聽者,實現ApplicationListener接口,重寫 onApplicationEvent() 方法;
@Component
public class DemoListener implements ApplicationListener<DemoEvent>{
//使用onApplicationEvent接收消息
@Override
public void onApplicationEvent(DemoEvent event) {
String msg = event.getMessage();
System.out.println("接收到的信息是:"+msg);
}
}
// 發佈事件,可以通過ApplicationEventPublisher 的 publishEvent() 方法發佈消息。
@Component
public class DemoPublisher {
@Autowired
ApplicationContext applicationContext;
public void publish(String message){
//發佈事件
applicationContext.publishEvent(new DemoEvent(this, message));
}
}
6)適配器設計模式
適配器設計模式將一個接口轉換成客戶希望的另一個接口,適配器模式使得接口不兼容的那些類可以一起工作,其別名爲包裝器。在Spring MVC中,DispatcherServlet根據請求信息調用HandlerMapping,解析請求對應的Handler,解析到對應的Handler(也就是我們常說的Controller控制器)後,開始由HandlerAdapter適配器處理。
爲什麼要在Spring MVC中使用適配器模式?
Spring MVC中的Controller種類衆多不同類型的Controller通過不同的方法來對請求進行處理,有利於代碼的維護拓展。
7)裝飾者設計模式
裝飾模式:動態的給一個對象添加一些額外的職責,就增加功能來說,裝飾者到相比子類更加靈活。
Spring 的 ApplicationContext 中配置所有的 DataSource
。 這些 DataSource 可能是各種不同類型的, 比如不同的數據庫: Oracle、 SQL Server、 MySQL 等, 也可能是不同的數據源: 比如Apache 提 供 的 org.apache.commons.dbcp.BasicDataSource 、 Spring 提 供 的org.springframework.jndi.JndiObjectFactoryBean 等。 然後 SessionFactory 根據客戶的每次請求, 將 DataSource 屬性設置成不同的數據源, 以到達切換數據源的目的。
在spring的命名體現:Spring 中用到的包裝器模式在類名上有兩種表現: 一種是類名中含有 Wrapper
, 另一種是類名中含有Decorator
。 基本上都是動態地給一個對象添加一些額外的職責。
8)策略設計模式
Spring 框架的資源訪問接口就是基於策略設計模式實現的。該接口提供了更強的資源訪問能力,Spring框架本身大量使用了Resource接口來訪問底層資源。Resource接口本身沒有提供訪問任何底層資源的實現邏輯,針對不同的額底層資源,Spring將會提供不同的Resource實現類,不同的實現類負責不同的資源訪問類型。
Spring 爲 Resource 接口提供瞭如下實現類:
UrlResource:訪問網絡資源的實現類。
ClassPathResource:訪問類加載路徑裏資源的實現類。
FileSystemResource:訪問文件系統裏資源的實現類。
ServletContextResource:訪問相對於 ServletContext 路徑裏的資源的實現類.
InputStreamResource:訪問輸入流資源的實現類。
ByteArrayResource:訪問字節數組資源的實現類。
這些 Resource 實現類,針對不同的的底層資源,提供了相應的資源訪問邏輯,並提供便捷的包裝,以利於客戶端程序的資源訪問。