spring Ioc及aop

IoC

概念

  所謂控制反轉,指的是獲取對象的方式發生了反轉。在傳統面向對象編程中,咱們都是在要使用某一個對象實例時建立一個對象實例,對象的控制權在咱們本身手裏,若是對於一個接口的多個實現類,咱們要本身選擇判斷使用具體的實現類,使得咱們進行軟件開發耦合度高,維護起來不方便;spring的IOC則是將某一接口的具體實現類的選擇控制權從調用者中移除,轉由spring容器進行控制。在spring初始化對象的關聯關係的過程是經過「依賴注入」實現的。 html

依賴注入類型

  一、 構造器注入;java

  二、 屬性注入;spring

  三、 接口注入。sql

實現原理

  咱們通常是經過ApplicationContext來完成spring中bean的初始化,以下圖所示,這看似簡單的一個建立對象操做,實踐上裏面涉及的技術很是複雜。編程

 

  在實例化context對象時,spring容器作了以下圖所示的操做。c#

 

  一、 首先經過解析XML配置文件取得bean的配置信息,並將bean註冊到bean定義註冊表裏;設計模式

  二、 將註冊表裏的bean根據註冊信息實例化(經過工廠模式+反射),並進行依賴注入,若是是bean依賴,先初始化依賴的bean。安全

  三、 將實例化的bean放入spring容器(bean存放的數據結構本質爲map)。數據結構

  ApplicationContext接口爲對BeanFactory接口的擴展。ApplicationContext 在初始化時就把 xml 的配置信息讀入內存,對 XML 文件進行檢驗,若是配置文件沒有錯誤,就建立全部的Bean ,直接爲應用程序服務;而BeanFactory是延遲加載,是在調用getBean方法時才進行bean的實例化,若是Bean的某一個屬性沒有注入,BeanFacotry加載後,直至第一次使用調用getBean方法纔會拋出異常。app

  詳細的源碼講解請參考 https://javadoop.com/post/spring-ioc#toc18

設計思想

一、 單例模式

二、 工廠模式

三、 反射思想

使用IoC的好處

一、不用本身組裝,拿來就用。

二、享受單例的好處,效率高,不浪費空間。

三、便於單元測試,方便切換mock組件。

四、便於進行AOP操做,對於使用者是透明的。

五、統一配置,便於修改。

AOP

概念

    面向切面編程(AOP),適合那些具備橫切邏輯的應用場合,如性能監測、訪問控制、事務管理及日誌記錄。AOP能夠說是對OOP的補充和完善。OOP引入封裝、繼承和多態性等概念來創建一種對象層次結構,用以模擬公共行爲的一個集合。當咱們須要爲分散的對象引入公共行爲的時候,OOP則顯得無能爲力。也就是說,OOP容許你定義從上到下的關係,但並不適合定義從左到右的關係。例如日誌功能。日誌代碼每每水平地散佈在全部對象層次中,而與它所散佈到的對象的核心功能毫無關係。在OOP設計中,它致使了大量代碼的重複,而不利於各個模塊的重用。將程序中的交叉業務邏輯(好比安全,日誌,事務等),封裝成一個切面,而後注入到目標對象(具體業務邏輯)中去。實現AOP的技術,主要分爲兩大類:一是採用動態代理技術,利用截取消息的方式,對該消息進行裝飾,以取代原有對象行爲的執行;二是採用靜態織入的方式,引入特定的語法建立方面,從而使得編譯器能夠在編譯期間織入有關方面的代碼。

AOP究竟是什麼?先看看下圖

觀察上圖,能夠發現createForum方法和removeTopic方法除了①②處代碼不同外,其餘代碼同樣。咱們知道,咱們開發軟件講究的是「懶」,重複代碼通常都會抽出來造成一個類或方法,但上面示例的咱們沒法抽出,兩個不一樣的業務代碼被相同非業務代碼包圍,因此咱們沒法經過抽象父類進行抽取。這時候,AOP閃亮登場,AOP經過橫向切取(底層經過代理模式實現)將那些重複的代碼抽取出來,讓業務邏輯代碼不摻雜其餘非業務邏輯代碼而且能夠不改變原來的業務流程,這就是AOP要作的事。這種將分散在各個邏輯代碼的相同代碼經過橫向切取的方式抽取到一個獨立模塊的編程方式就叫AOP。

實現原理

  一、 基於JDK的動態代理;下面幾張圖是樣例程序

  接口:

  

  實現類

  

  代理類

  

  使用樣例

  

  JDK代理的底層實現過程:

  主要在調用Proxy.newProxyInstance過程當中會根據傳入的類加載器、接口動態生成一個叫$proxy0類的字節碼,編譯後加入到jvm中。動態生成的過程就是利用java的反射機制獲取的到類加載器的名稱,接口名稱,接口方法等而後拼接成字符串存入磁盤後進行編譯這樣大體的過程。看看下面的反編譯字節碼的結果,在構造方法處使用到了傳入的InvocationHadler實現類MyAOP,而後在doSomething方法是調用MyAOPinvoke方法。

package first_maven; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; import java.lang.reflect.UndeclaredThrowableException; public final class $Proxy0 extends Proxy implements TestService { private static Method m1; private static Method m3; private static Method m2; private static Method m0; public $Proxy0(InvocationHandler var1) throws { super(var1); } public final boolean equals(Object var1) throws { try { return ((Boolean)super.h.invoke(this, m1, new Object[]{var1})).booleanValue(); } catch (RuntimeException | Error var3) { throw var3; } catch (Throwable var4) { throw new UndeclaredThrowableException(var4); } } public final void doSomething() throws { try { super.h.invoke(this, m3, (Object[])null); } catch (RuntimeException | Error var2) { throw var2; } catch (Throwable var3) { throw new UndeclaredThrowableException(var3); } } public final String toString() throws { try { return (String)super.h.invoke(this, m2, (Object[])null); } catch (RuntimeException | Error var2) { throw var2; } catch (Throwable var3) { throw new UndeclaredThrowableException(var3); } } public final int hashCode() throws { try { return ((Integer)super.h.invoke(this, m0, (Object[])null)).intValue(); } catch (RuntimeException | Error var2) { throw var2; } catch (Throwable var3) { throw new UndeclaredThrowableException(var3); } } static { try { m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object")); m3 = Class.forName("first_maven.TestService").getMethod("doSomething"); m2 = Class.forName("java.lang.Object").getMethod("toString"); m0 = Class.forName("java.lang.Object").getMethod("hashCode"); } catch (NoSuchMethodException var2) { throw new NoSuchMethodError(var2.getMessage()); } catch (ClassNotFoundException var3) { throw new NoClassDefFoundError(var3.getMessage()); } } }
View Code

  參考連接 https://www.jianshu.com/p/84ffb8d0a338

  jdk動態代理是經過繼承proxy類而後實現接口動態建立代理類,因此不能建立沒有實現接口的類的代理類。

  與jdk靜態代理比較:jdk靜態代理只能代理一個接口(代理類內部需存在該接口方法的調用),若是其餘接口的實現類也須要相似的加強,只能在建立一個代理類;而動態代理能夠代理多個接口(由於代理類內部並不存在對接口實現類的實例方法的直接調用)。

   Java靜態代理

  

  二、 基於CGlib的動態代理,下面是樣例程序

  代理類

  

  被代理類及使用樣例

  

  

  底層實現原理請參考 

  https://www.jianshu.com/p/9a61af393e41?from=timeline&isappinstalled=0

  cglib採用的是動態建立子類生成代理對象,因此被代理類不能是final 或 private,但cglib代理的對象並不須要實現接口。

使用aop的好處

  1. 通知自定義
  2. 解耦和
  3. 統一管理權限,統一管理異常拋出
  4. 不須要咱們本身編寫複雜的代理類

相關設計模式

1、工廠模式

  一、簡單工廠模式

  就是創建一個工廠類,對實現了同一接口的一些類進行實例的建立。簡單工廠模式的實質是由一個工廠類根據傳入的參數,動態決定應該建立哪個產品類(這些產品類繼承自一個父類或接口)的實例。

  運算類接口

  

  運算類實現類

  

  

  工廠類

  

  使用樣例

  

  當咱們須要增長其餘運算如乘法或除法時,只需獲取到operateServive就能夠進行編寫,而與AddOperateImpl及SubOperateImpl無關,而且這些實現類都有很好的複用性。

  優勢:

  工廠類是整個模式的關鍵。包含了必要的邏輯判斷,根據外界給定的信息,決定究竟應該建立哪一個具體類的對象。經過使用工廠類,外界能夠從直接建立具體產品對象的尷尬局面擺脫出來,僅僅須要負責「消費」對象就能夠了。而沒必要管這些對象究竟如何建立及如何組織的。明確了各自的職責和權利,有利於整個軟件體系結構的優化。

  缺點:

  因爲工廠類集中了全部實例的建立邏輯,違反了高內聚責任分配原則,將所有建立邏輯集中到了一個工廠類中;它所能建立的類只能是事先考慮到的,若是須要添加新的類,則就須要改變工廠類了。當系統中的具體產品類不斷增多時候,可能會出現要求工廠類根據不一樣條件建立不一樣實例的需求.這種對條件的判斷和對具體產品類型的判斷交錯在一塊兒,很難避免模塊功能的蔓延,對系統的維護和擴展很是不利。

(摘抄自 http://www.javashuo.com/article/p-vlwdczaj-ez.html

 

  也正是由於當咱們增長新的運算時,工廠方法須要增長case分支語句,而且若是有不少「產品」工廠方法裏就有大量的代碼,雖然工廠對擴展開放,當同時也開放了修改,這就違反了面向對象的「開放-封閉原則」,因而就有了工廠方法模式。

  二、工廠方法模式

  定義一個建立對象的接口,讓子類決定實例化哪個類。工廠方法使一個類的加載延遲到子類。

   先看看使用:

   抽象工廠

  

  具體工廠:

  

  

  工廠方法使用樣例

   

  工廠方法優勢:

  一方面解決了簡單工廠違反「開放-封閉原則」,另外一方面某一工廠生產具體某一產品,減輕了工廠類負擔,使得程序維護性更高。

  工廠方法缺點:

  每加一個產品類,就要相應增長一個產品工廠類,增長了額外開發量;

  一個工廠只能生產一個版本的產品,而不能生產多個產品,這時抽象工廠的出現就爲解決這個問題。

  三、 抽象工廠模式

  提供一個建立一系列相關或相互依賴對象的接口,而無需指定他們具體的類。

 

  抽象工廠接口,用戶只需到該工廠取到具體產品而不須要去了解其餘方面

  

  

  

  

  

  Department表相似,這裏再也不貼出

 

  Mysql生產車間

  

  Oracle生產車間

  

  客戶端使用樣例

  

 

  抽象工廠優勢:

  一、 便於交換產品系列,能夠方便的在不一樣產品系列間切換;

  二、 具體的建立實例過程與客戶端分離。

  抽象工廠缺點:

  當咱們增長某一類產品時,就要增長每個生產車間對該類產品的具體實現,同時要把該產品發佈到咱們的工廠(即在工廠接口提供取得該產品的抽象方法)

2、單例模式

  保證一個類只有一個實例,並暴露出一個獲取該實例的渠道(公共靜態方法)

  餓漢式

  

  懶漢式

  

  

  單例模式優勢

  一、 因爲單例模式在內存中只有一個實例,減小內存開支,特別是一個對象須要頻繁地建立銷燬時,並且建立或銷燬時性能又沒法優化,單例模式就很是明顯了;

  二、 因爲單例模式只生成一個實例,因此,減小系統的性能開銷,當一個對象產生須要比較多的資源時,如讀取配置,產生其餘依賴對象時,則能夠經過在應用啓動時直接產生一個單例對象,而後永久駐留內存的方式來解決;

  三、 單例模式能夠避免對資源的多重佔用,例如一個寫文件操做,因爲只有一個實例存在內存中,避免對同一個資源文件的同時寫操做;

  四、 單例模式能夠在系統設置全局的訪問點,優化和共享資源訪問,例如,能夠設計一個單例類,負責全部數據表的映射處理。

  單例模式缺點:

  一、 單例模式通常沒有接口,擴展很困難,若要擴展,除了修改代碼基本上沒有第二種途徑能夠實現;

  二、 單例對象若是持有Context,那麼很容易引起內存泄漏,此時須要注意傳遞給單例對象的Context最好是Application Context。

  (摘抄自 https://blog.csdn.net/lijizhi19950123/article/details/78150213/ )

 

  主要應用場景

  一、 資源共享的狀況下,避免因爲資源操做時致使的性能或損耗等。如上述中的日誌文件,應用配置;

  二、 控制資源的狀況下,方便資源之間的互相通訊。如線程池等

3、  代理模式

  爲其餘對象提供一種代理以控制對這個對象的訪問。

 

  Java中的三種代理模式樣例代碼見上面AOP實現原理模塊。

  應用場景:

  一、 遠程代理:爲一個對象在不一樣地址空間提供局部表明,這樣能夠隱藏一個對象存在於不一樣地址空間的事實。例如Java的RMI。

  二、 虛擬代理:是根據須要建立開銷很大的對象,經過它來存放實例化須要很長時間的真實對象。例如當咱們打開一個網頁,考慮到有些網頁圖片比較多,並且某些圖片文件比較大,所以將先以圖標的方式顯示圖片,不一樣類型的圖片使用不一樣的圖標,而且在圖標下面標註該圖片的文件名,用戶單擊圖標後可查看真正的圖片,此時代理存放的是圖片的真實路徑,但它並無把要代理的圖片存在本身內存中,而是在用戶須要是去被代理裏「取貨「。

  三、 安全代理:用來控制真實對象訪問時的權限。例如,對某一對象,不一樣的用戶有不一樣的訪問權限,那麼咱們就能夠建立擁有不一樣權限的代理類來進行代理。

  四、 智能代理:是指當調用真實的對象時,代理去處理另一些事情。我對這種代理的理解就是spring中的aop,加強被代理類。

相關文章
相關標籤/搜索