天天看點

Java進階 | Proxy動态代理機制詳解

靜态代理明确定義了代理對象,即有一個代理對象的`.java`檔案加載到JVM的過程,很顯然的一個問題,在實際的開發過程中,不可能為每個目标對象都定義一個代理類,同樣也不能讓一個代理對象去代理多個目标對象,這兩種方式的維護成本都極高。代理模式的本質是在目标對象的方法前後置入增強操作,但是又不想修改目标類,通過前面反射機制可以知道,在運作的時候可以擷取對象的結構資訊,基于Class資訊去動态建立代理對象,這就是動态代理機制。

在說Java動态代理之前,還是要說一下Jvm加載對象的過程,這個依舊是了解動态代理的基礎性原理:

Java進階 | Proxy動态代理機制詳解

Java類即源代碼程式<code>.java</code>類型檔案,經過編譯器編譯之後就被轉換成位元組代碼<code>.class</code>類型檔案,類加載器負責讀取位元組代碼,并轉換成java.lang.Class對象,描述類在中繼資料空間的資料結構,類被執行個體化時,堆中存儲執行個體化的對象資訊,并且通過對象類型資料的指針找到類。

過程描述:源碼-&gt;.java檔案-&gt;.class檔案-&gt;Class對象-&gt;執行個體對象

是以通過New建立對象,獨斷其背後很多實作細節,了解上述過程之後,再了解一個常用的設計模式,即代理模式。

代理模式給某一個(目标)對象提供一個代理對象,并由代理對象持有目标對象的引用。所謂代理,就是一個對象代表另一個對象執行相應的動作程式。而代理對象可以在用戶端和目标對象之間起到中介的作用。

Java進階 | Proxy動态代理機制詳解

代理模式在實際的生活中場景很多,例如中介、律師、代購等行業,都是簡單的代理邏輯,在這個模式下存在兩個關鍵角色:

目标對象角色:即代理對象所代表的對象。

代理對象角色:内部含有目标對象的引用,可以操作目标對象;AOP程式設計就是基于這個思想。

靜态代理:在程式運作之前确定代理角色,并且明确代理類和目标類的關系。

動态代理:基于Java反射機制,在JVM運作時動态建立和生成代理對象。

基于上述靜态代理的概念,用一段代碼進行描述實作,基本邏輯如下:

明确目标對象即被代理的對象;

定義代理對象,通過構造器持有目标對象;

代理對象中定義前後置增強方法;

目标對象與前後置增強代碼就組成了代理對象,這樣就不用直接通路目标對象,像極了電視劇中那句話:我是律師,我的當事人不友善和你對話。

靜态代理明确定義了代理對象,即有一個代理對象的<code>.java</code>檔案加載到JVM的過程,很顯然的一個問題,在實際的開發過程中,不可能為每個目标對象都定義一個代理類,同樣也不能讓一個代理對象去代理多個目标對象,這兩種方式的維護成本都極高。

代理模式的本質是在目标對象的方法前後置入增強操作,但是又不想修改目标類,通過前面反射機制可以知道,在運作的時候可以擷取對象的結構資訊,基于Class資訊去動态建立代理對象,這就是動态代理機制。

順便說一句:技術的底層實作邏輯不好了解是衆所周知,然而基礎知識點并不複雜,例如代理模式的基本原理,但是結合到實際的複雜應用中(AOP模式),很難活靈活現的了解到是基于反射和動态代理的方式實作的。

基于一個場景來描述動态代理和靜态代理的差別,即最近幾年很火的概念,海外代購:

Java進階 | Proxy動态代理機制詳解

在代購剛興起的初期,是一些常去海外出差的人,會接代購需求,即代理人固定;後來就興起海外代購平台,海淘等一系列産品,即使用者代購需求(目标對象)由代購平台去實作,但是具體誰來操作這個就看即時配置設定,這個場景與動态代理的原理類似。

首先看兩個核心類,這裡簡述下概念,看完基本過程再細聊:

Proxy-建立代理對象,核心參數:

ClassLoader:(目标類)加載器;

Interfaces:(目标類)接口數組;

InvocationHandler:代理調用機制;

InvocationHandler-代理類調用機制:

invoke:這個上篇說的反射原理;

method:反射類庫中的核心API;

目标對象和接口

代理對象執行機制

具體組合方式

這裡之是以要生成代理類的結構資訊,因為從JVM加載的過程看不到相關内容,關鍵資訊再次被獨斷:

Java進階 | Proxy動态代理機制詳解

檢視代理類名稱

下意識輸出代理對象名稱,這裡即對應JVM機制,找到Class對象名,然後分析結構,這樣就明白動态代理具體的執行原理了。

生成代理類.class檔案

通過上面JVM加載對象的機制可知,描述代理類的Class對象一定存在,隻是在運作時并沒有生成顯式的<code>.class</code>檔案,通過上面生成代理類<code>.class</code>的文法,會在項目目錄的<code>/com/java/proxy</code>路徑下建立檔案。

順便說一句:作為一隻程式員,複雜總是和我們環環相繞,說好的簡單點呢?

繼承與實作

從代理類的功能來思考,可以想到需要繼承Proxy與實作IUser接口,還有就是持有調用機制的具體實作類,用來做業務增強。

構造方法

通過構造方法,持有UserHandler具體的執行機制對象。

接口實作

目标類的基本需求<code>update()</code>方法,通過代理類進行承接,并基于UserHandler實作具體的增強業務處理。

基礎方法

基于Object類,定義Java中幾個常用方法equals()判斷,toString()方法,hashCode()值,這個在分析Map源碼的時候有說過為什麼這幾個方法通常都是一起出現。

上面是案例執行的過程和原理,還有一個關鍵點要明白,即JDK源碼的邏輯:

Proxy提供的靜态方法<code>newProxyInstance()</code>,通過各個參數的傳入,建構一個新的代理Class對象,即$Proxy0類的結構資訊,這裡再回首看下三個核心參數:

ClassLoader:基于JVM運作過程,是以需要擷取目标類UserService的類加載器;

Interfaces:目标類UserService實作的接口,從面向對象來考慮,接口與實作分離,代理類通過實作IUser接口,模拟目标類的需求;

InvocationHandler:代理類提供的功能封裝即UserHandler,可以在目标方法調用前後做增強處理;

最後總結一下動态代理的實作的核心技術點:Jvm加載原理、反射機制、面向對象思想;每次閱讀JDK的源碼都會驚歎設計者的鬼斧神工,滴水穿石堅持才會有收獲。

JVM類加載機制 | 代理模式 | AOP切面程式設計 | 自定義日志記錄 | Map源碼分析

Java進階 | Proxy動态代理機制詳解

閱讀标簽

【Java基礎】【設計模式】【結構與算法】【Linux系統】【資料庫】

【分布式架構】【微服務】【大資料元件】【SpringBoot進階】【Spring&amp;Boot基礎】

【資料分析】【技術導圖】【 職場】