仔細想了想。。不必重複造輪子。
天天覆習啥了就直接CTRL CV了html
IoC(Inversion of Control,控制翻轉) 是Spring 中一個很是很是重要的概念,它不是什麼技術,而是一種解耦的設計思想。它的主要目的是藉助於「第三方」(Spring 中的 IOC 容器) 實現具備依賴關係的對象之間的解耦(IOC容易管理對象,你只管使用便可),從而下降代碼之間的耦合度。IOC 是一個原則,而不是一個模式,如下模式(但不限於)實現了IoC原則。git
Spring IOC 容器就像是一個工廠同樣,當咱們須要建立一個對象的時候,只須要配置好配置文件/註解便可,徹底不用考慮對象是如何被建立出來的。 IOC 容器負責建立對象,將對象鏈接在一塊兒,配置這些對象,並從建立中處理這些對象的整個生命週期,直到它們被徹底銷燬。web
在實際項目中一個 Service 類若是有幾百甚至上千個類做爲它的底層,咱們須要實例化這個 Service,你可能要每次都要搞清這個 Service 全部底層類的構造函數,這可能會把人逼瘋。若是利用 IOC 的話,你只須要配置好,而後在須要的地方引用就好了,這大大增長了項目的可維護性且下降了開發難度。關於Spring IOC 的理解,推薦看這一下知乎的一個回答:https://www.zhihu.com/question/23277575/answer/169698662 ,很是不錯。算法
控制翻轉怎麼理解呢? 舉個例子:"對象a 依賴了對象 b,當對象 a 須要使用 對象 b的時候必須本身去建立。可是當系統引入了 IOC 容器後, 對象a 和對象 b 以前就失去了直接的聯繫。這個時候,當對象 a 須要使用 對象 b的時候, 咱們能夠指定 IOC 容器去建立一個對象b注入到對象 a 中"。 對象 a 得到依賴對象 b 的過程,由主動行爲變爲了被動行爲,控制權翻轉,這就是控制反轉名字的由來。spring
DI(Dependecy Inject,依賴注入)是實現控制反轉的一種設計模式,依賴注入就是將實例變量傳入到一個對象中去。數據庫
Spring使用工廠模式能夠經過 BeanFactory
或 ApplicationContext
建立 bean 對象。編程
二者對比:設計模式
BeanFactory
:延遲注入(使用到某個 bean 的時候纔會注入),相比於BeanFactory
來講會佔用更少的內存,程序啓動速度更快。ApplicationContext
:容器啓動的時候,無論你用沒用到,一次性建立全部 bean 。BeanFactory
僅提供了最基本的依賴注入支持, ApplicationContext
擴展了 BeanFactory
,除了有BeanFactory
的功能還有額外更多功能,因此通常開發人員使用 ApplicationContext
會更多。ApplicationContext的三個實現類:spring-mvc
ClassPathXmlApplication
:把上下文文件當成類路徑資源。FileSystemXmlApplication
:從文件系統中的 XML 文件載入上下文定義信息。XmlWebApplicationContext
:從Web系統中的XML文件載入上下文定義信息。Example:
import org.springframework.context.ApplicationContext; import org.springframework.context.support.FileSystemXmlApplicationContext; public class App { public static void main(String[] args) { ApplicationContext context = new FileSystemXmlApplicationContext( "C:/work/IOC Containers/springframework.applicationcontext/src/main/resources/bean-factory-config.xml"); HelloApplicationContext obj = (HelloApplicationContext) context.getBean("helloApplicationContext"); obj.getMsg(); } }
在咱們的系統中,有一些對象其實咱們只須要一個,好比說:線程池、緩存、對話框、註冊表、日誌對象、充當打印機、顯卡等設備驅動程序的對象。事實上,這一類對象只能有一個實例,若是製造出多個實例就可能會致使一些問題的產生,好比:程序的行爲異常、資源使用過量、或者不一致性的結果。
使用單例模式的好處:
Spring 中 bean 的默認做用域就是 singleton(單例)的。 除了 singleton 做用域,Spring 中 bean 還有下面幾種做用域:
Spring 實現單例的方式:
<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)); } } }
AOP(Aspect-Oriented Programming:面向切面編程)可以將那些與業務無關,卻爲業務模塊所共同調用的邏輯或責任(例如事務處理、日誌管理、權限控制等)封裝起來,便於減小系統的重複代碼,下降模塊間的耦合度,並有利於將來的可拓展性和可維護性。
Spring AOP 就是基於動態代理的,若是要代理的對象,實現了某個接口,那麼Spring AOP會使用JDK Proxy,去建立代理對象,而對於沒有實現接口的對象,就沒法使用 JDK Proxy 去進行代理了,這時候Spring AOP會使用Cglib ,這時候Spring AOP會使用 Cglib 生成一個被代理對象的子類來做爲代理,以下圖所示:
固然你也可使用 AspectJ ,Spring AOP 已經集成了AspectJ ,AspectJ 應該算的上是 Java 生態系統中最完整的 AOP 框架了。
使用 AOP 以後咱們能夠把一些通用功能抽象出來,在須要用到的地方直接使用便可,這樣大大簡化了代碼量。咱們須要增長新功能時也方便,這樣也提升了系統擴展性。日誌功能、事務管理等等場景都用到了 AOP 。
Spring AOP 屬於運行時加強,而 AspectJ 是編譯時加強。 Spring AOP 基於代理(Proxying),而 AspectJ 基於字節碼操做(Bytecode Manipulation)。
Spring AOP 已經集成了 AspectJ ,AspectJ 應該算的上是 Java 生態系統中最完整的 AOP 框架了。AspectJ 相比於 Spring AOP 功能更增強大,可是 Spring AOP 相對來講更簡單,
若是咱們的切面比較少,那麼二者性能差別不大。可是,當切面太多的話,最好選擇 AspectJ ,它比Spring AOP 快不少。
模板方法模式是一種行爲設計模式,它定義一個操做中的算法的骨架,而將一些步驟延遲到子類中。 模板方法使得子類能夠不改變一個算法的結構便可重定義該算法的某些特定步驟的實現方式。
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 模式與模板方法模式配合,既達到了代碼複用的效果,同時增長了靈活性。
觀察者模式是一種對象行爲型模式。它表示的是一種對象與對象之間具備依賴關係,當一個對象發生改變的時候,這個對象所依賴的對象也會作出反應。Spring 事件驅動模型就是觀察者模式很經典的一個應用。Spring 事件驅動模型很是有用,在不少場景均可以解耦咱們的代碼。好比咱們每次添加商品的時候都須要從新更新商品索引,這個時候就能夠利用觀察者模式來解決這個問題。
ApplicationEvent
(org.springframework.context
包下)充當事件的角色,這是一個抽象類,它繼承了java.util.EventObject
並實現了 java.io.Serializable
接口。
Spring 中默認存在如下事件,他們都是對 ApplicationContextEvent
的實現(繼承自ApplicationContextEvent
):
ContextStartedEvent
:ApplicationContext
啓動後觸發的事件;ContextStoppedEvent
:ApplicationContext
中止後觸發的事件;ContextRefreshedEvent
:ApplicationContext
初始化或刷新完成後觸發的事件;ContextClosedEvent
:ApplicationContext
關閉後觸發的事件。ApplicationListener
充當了事件監聽者角色,它是一個接口,裏面只定義了一個 onApplicationEvent()
方法來處理ApplicationEvent
。ApplicationListener
接口類源碼以下,能夠看出接口定義看出接口中的事件只要實現了 ApplicationEvent
就能夠了。因此,在 Spring中咱們只要實現 ApplicationListener
接口實現 onApplicationEvent()
方法便可完成監聽事件
package org.springframework.context; import java.util.EventListener; @FunctionalInterface public interface ApplicationListener<E extends ApplicationEvent> extends EventListener { void onApplicationEvent(E var1); }
ApplicationEventPublisher
充當了事件的發佈者,它也是一個接口。
@FunctionalInterface public interface ApplicationEventPublisher { default void publishEvent(ApplicationEvent event) { this.publishEvent((Object)event); } void publishEvent(Object var1); }
ApplicationEventPublisher
接口的publishEvent()
這個方法在AbstractApplicationContext
類中被實現,閱讀這個方法的實現,你會發現實際上事件真正是經過ApplicationEventMulticaster
來廣播出去的。具體內容過多,就不在這裏分析了,後面可能會單獨寫一篇文章提到。
ApplicationEvent
,而且寫相應的構造函數;ApplicationListener
接口,重寫 onApplicationEvent()
方法;ApplicationEventPublisher
的 publishEvent()
方法發佈消息。Example:
// 定義一個事件,繼承自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)); } }
當調用 DemoPublisher
的 publish()
方法的時候,好比 demoPublisher.publish("你好")
,控制檯就會打印出:接收到的信息是:你好
。
適配器模式(Adapter Pattern) 將一個接口轉換成客戶但願的另外一個接口,適配器模式使接口不兼容的那些類能夠一塊兒工做,其別名爲包裝器(Wrapper)。
咱們知道 Spring AOP 的實現是基於代理模式,可是 Spring AOP 的加強或通知(Advice)使用到了適配器模式,與之相關的接口是AdvisorAdapter
。Advice 經常使用的類型有:BeforeAdvice
(目標方法調用前,前置通知)、AfterAdvice
(目標方法調用後,後置通知)、AfterReturningAdvice
(目標方法執行結束後,return以前)等等。每一個類型Advice(通知)都有對應的攔截器:MethodBeforeAdviceInterceptor
、AfterReturningAdviceAdapter
、AfterReturningAdviceInterceptor
。Spring預約義的通知要經過對應的適配器,適配成 MethodInterceptor
接口(方法攔截器)類型的對象(如:MethodBeforeAdviceInterceptor
負責適配 MethodBeforeAdvice
)。
在Spring MVC中,DispatcherServlet
根據請求信息調用 HandlerMapping
,解析請求對應的 Handler
。解析到對應的 Handler
(也就是咱們日常說的 Controller
控制器)後,開始由HandlerAdapter
適配器處理。HandlerAdapter
做爲指望接口,具體的適配器實現類用於對目標類進行適配,Controller
做爲須要適配的類。
爲何要在 Spring MVC 中使用適配器模式? Spring MVC 中的 Controller
種類衆多,不一樣類型的 Controller
經過不一樣的方法來對請求進行處理。若是不利用適配器模式的話,DispatcherServlet
直接獲取對應類型的 Controller
,須要的自行來判斷,像下面這段代碼同樣:
if(mappedHandler.getHandler() instanceof MultiActionController){ ((MultiActionController)mappedHandler.getHandler()).xxx }else if(mappedHandler.getHandler() instanceof XXX){ ... }else if(...){ ... }
假如咱們再增長一個 Controller
類型就要在上面代碼中再加入一行 判斷語句,這種形式就使得程序難以維護,也違反了設計模式中的開閉原則 – 對擴展開放,對修改關閉。
裝飾者模式能夠動態地給對象添加一些額外的屬性或行爲。相比於使用繼承,裝飾者模式更加靈活。簡單點兒說就是當咱們須要修改原有的功能,但咱們又不肯直接去修改原有的代碼時,設計一個Decorator套在原有代碼外面。其實在 JDK 中就有不少地方用到了裝飾者模式,好比 InputStream
家族,InputStream
類下有 FileInputStream
(讀取文件)、BufferedInputStream
(增長緩存,使讀取文件速度大大提高)等子類都在不修改InputStream
代碼的狀況下擴展了它的功能。
Spring 中配置 DataSource 的時候,DataSource 多是不一樣的數據庫和數據源。咱們可否根據客戶的需求在少修改原有類的代碼下動態切換不一樣的數據源?這個時候就要用到裝飾者模式(這一點我本身還沒太理解具體原理)。Spring 中用到的包裝器模式在類名上含有 Wrapper
或者 Decorator
。這些類基本上都是動態地給一個對象添加一些額外的職責
Spring 框架中用到了哪些設計模式?
BeanFactory
、ApplicationContext
建立 bean 對象。jdbcTemplate
、hibernateTemplate
等以 Template 結尾的對數據庫操做的類,它們就使用到了模板模式。Controller
。