elk日誌分析管理系統的搭建(非叢集非docker安裝)
5. Spring AOP
5.1 AOP基本介紹
AOP(Aspect Oriented Programing,面向切面程式設計),利用AOP可以對業務邏輯的各個部分進行隔離,從而使得業務邏輯各部分之間的耦合度降低,提高程式的可重用性,同時提高開發的效率。想比較OOP面向物件程式設計來說,AOP關注的不再是程式程式碼中的某個類、某些方法,而是更多考慮的是一種面到面的切入,即層與層之間的一種切入。
通俗化的描述:不通過修改原始碼的方式,在主幹功能裡面新增新功能。
使用登入的例子說明AOP:
AOP能做什麼?
應用於日誌記錄、效能統計、安全控制、事務處理等方面,實現公共功能性的重複使用。
AOP的特點:
降低模組與模組之間的耦合度,提高業務程式碼的聚合度。(高內聚低耦合)
提高了程式碼的複用性。
提高系統的擴充套件性。(高版本相容低版本)
可以在不影響原有的功能基礎上新增新的功能。
AOP的底層實現:
動態代理(JDK+CGLIB)
AOP基本概念:
5.2 代理模式
5.2.1 基本介紹
代理模式在Java開發中是一種比較常見的設計模式。設計目的旨在為服務類與客戶類之間插入其他功能,插入的功能對於呼叫者是透明的,起到偽裝控制的作用。如租房的例子︰房客、中介、房東。對應於代理模式中即:客戶類、代理類、委託類(被代理類)。
為某一個物件(委託類)提供一個代理(代理類),用來控制對這個物件的訪問。
委託類和代理類有一個共同的父類或父介面。
代理類會對請求做預處理、過濾,將請求分配給指定物件。
生活中常見的代理模式:
房東委託中介出租房子、新人委託婚慶公司舉辦婚禮。
代理模式的兩個設計原則:
1. 委託類和代理類有共同的行為
2. 代理類能夠增強委託類的行為
常見的代理模式:
1. 靜態代理
2. 動態代理
實現高內聚、低耦合
5.2.2 靜態代理
某個物件提供一個代理,代理角色固定,以控制對這個物件的訪問。代理類和委託類有共同的父類或父介面
1. 代理的三要素(以租房為例子)
-
有共同行為(租房) —— 介面
-
目標角色(房東) —— 實現行為
-
代理角色(中介) —— 實現行為(增強目標物件行為)
下面舉Java程式碼示例:
介面類:租房子
public interface RentHouse { // 定義介面——行為 void toRentHouse(); }
目標類(需要增強方法的類):房東類,房東有租房子的需求
public class Owner implements RentHouse{ /** * 靜態物件 ——> 目標角色 * 1. 實現行為 */ @Override public void toRentHouse() { System.out.println("兩室一廳,月租五千!"); } }
代理類:中介類,中介實現租房子的需求,並增強方法
public class AgentProxy implements RentHouse{ /** * 靜態物件 ——> 代理角色 * 1. 實現行為 * 2. 增強目標角色行為 */ // 目標物件 private RentHouse target; // 使用的介面引用,利用介面的動態繫結機制,根據當前實現介面的物件呼叫所屬方法 // 通過帶參構造器獲取目標物件 public AgentProxy(RentHouse target) { this.target = target; } // 實現行為 @Override public void toRentHouse() { // 使用者增強行為 System.out.println("房型朝南,採光好!"); // 代理物件呼叫目標物件的方法 target.toRentHouse(); // 自動向下轉型,呼叫實現子類的方法,動態繫結機制 // 使用者增強行為 System.out.println("價格可議!"); } }
測試:
public class Test { public static void main(String[] args) { // 目標物件 Owner owner = new Owner(); // 注意!這裡是目標物件 // 代理物件 AgentProxy agentProxy = new AgentProxy(owner); // 通過代理物件呼叫目標物件中的方法 agentProxy.toRentHouse(); } } /** * 房型朝南,採光好! * 兩室一廳,月租五千! * 價格可議! */
需要注意!在代理類中,使用介面的引用,作為構造器傳參進來的目標物件的引用,而在使用中,要用目標物件例項傳進來,使用介面呼叫的動態繫結機制,根據當前介面實現類呼叫所屬的方法。
2. 靜態代理的特點
- 目標角色固定
- 在應用程式之前就得知目標角色
- 代理物件會增強目標物件的行為
- 有多少需求就需要多少個代理類,這樣容易產生大量的靜態代理類,導致“類爆炸”(缺點)
5.2.3 動態代理(AOP底層原理)
相比於靜態代理,動態代理在建立代理物件上更加的靈活,動態代理類的位元組碼在程式執行時,由Java反射機制動態產生。它會根據需要,通過反射機制在程式執行期,動態的為目標物件建立代理物件,無需程式設計師手動編寫它的原始碼。動態代理不僅簡化了程式設計工作,而且提高了軟體系統的可擴充套件性,因為反射機制可以生成任意型別的動態代理類。代理的行為可以代理多個方法,即滿足生產需要的同時又達到程式碼通用的目的。
動態代理的兩種實現方式:
1. JDK動態代理(JDK動態代理的目標物件必須有介面實現)
2. CGLIB動態代理(沒有介面情況)
5.2.3.1 動態代理的特點
- 目標物件不固定
- 在程式執行時,動態建立目標物件
- 代理物件會增強目標物件的行為
5.2.3.2 JDK 動態代理
1. newProxyInstance()方法
Proxy類:
Proxy類是專門完成代理的操作類,可以通過此類為一個介面或多個介面動態地生成實現類,此類提供瞭如下操作方法:
/**
* 返回一個指定介面的代理類的例項方法呼叫分派到指定的呼叫處理程式。(返回代理物件)
* loader:一個classLoader物件,定義了由哪個classLoader物件來對生成的代理物件進行載入
* interfaces:一個Interface物件的陣列,表示的是我將要給我需要代理的物件提供一組什麼介面,如果
* 我提供了一組介面給它,那麼這個代理物件就宣稱實現了該介面(多型),這樣我就能呼叫這組介面中的方法了
* h:一個InvocationHandler介面,表示代理例項的呼叫處理程式實現的介面。每個代理例項都具有一個
* 關聯的呼叫處理程式。對代理例項呼叫方法時,將對方法呼叫進行編碼並將其指派到它的呼叫處理程式的invoke方法(傳入工nvocationHandler介面的子類)
*/
public static Object newProxyInstance(ClassLoader loader,
Class<?>[] interfaces,
InvocationHandler h)
2. 獲取動態例項
每一個代理類都需要實現InvocationHandler介面。
補充:對invoke方法
5.2.3.3 CGLIB 動態代理
JDK的動態代理機制只能代理實現了介面的類,而不能實現介面的類就不能使用JDK的動態代理,cglib是針對類來實現代理的,它的原理是對指定的目標類生成一個子類,並覆蓋其中方法實現增強,但因為採用的是繼承,所以不能對final修飾的類進行代理。
實現原理:繼承思想
1. 新增依賴
2. 定義類
測試:
5.2.4 JDK動態代理與CGLIB動態代理的區別
- JDK動態代理實現介面,CGLIB動態代理繼承思想
- JDK動態代理(目標物件存在介面時)執行效率高於CGLIB
- 如果目標物件有介面實現,選擇JDK動態代理,如果沒有介面實現選擇CGLIB代理