一篇文章徹底搞懂java動態代理的實現
網上有太多文章介紹動態代理是什麼,這裡就不介紹了,本文目的是讓大家弄懂動態代理是如何做到這些神奇的功能的。
先來一個小demo,通過這個demo來講解,動態代理需要三個類:
- 一個介面類;
- 一個實現介面的業務類;
- 一個生成動態代理類,並通過動態代理類來執行業務方法的測試類;
下面我們就一一實現它們。
- 介面類ITaskService
package com.zqz.jdkproxy;
public interface ITaskService {
public void doTask();
}
- 實現ITaskService 介面的業務類TaskServiceImpl
package com.zqz.jdkproxy;
public class TaskServiceImpl implements ITaskService{
public String doTask(){
System.out.println("do task");
return "do task";
}
}
- 實現動態代理
package com.zqz.jdkproxy;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
public class TestMain {
public static void main(String[] args) {
/**
* 通過Proxy.newProxyInstance()方法為我們的業務類生成動態代理類例項proxyInstance物件
* 它需要三個引數
* 當前的Classloader,用來載入動態生成代理類;動態代理類要實現的業務介面;InvocationHandler執行代理操作,並呼叫真正的業務類來執行業務方法;
*/
ITaskService proxyInstance = (ITaskService) Proxy.newProxyInstance(
TestMain.class.getClassLoader(), //裝載生成的動態代理類的classloader物件
new Class[]{ITaskService.class}, //生成的動態代理類需要實現的業務介面
new InvocationHandler(){ //InvocationHandler,呼叫代理操作,並執行真正的業務方法;
//被代理的目標物件
ITaskService taskService = new TaskServiceImpl();
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
try{
//業務方法執行前列印
System.out.println("before");
//呼叫業務方法
Object obj = method.invoke(taskService, args);
//業務方法執行後列印
System.out.println("after");
return obj;
} catch (Throwable e){
//業務方法丟擲異常列印
System.out.println("exception");
throw e;
} finally{
//業務方法執行完成列印(無論是否發生異常)
System.out.println("finally");
}
}
}
);
//使用代理物件執行代理方法
proxyInstance.doTask();
}
}
三個類完成了,通過執行TestMain ,就能輸出執行結果,大家先回答一個問題,以下兩種情況,控制檯會列印什麼內容?
- 業務方法正常執行;
- 業務方法丟擲異常;
如果你知道上面的答案,那麼我們一起往下探索,為什麼會有這樣的輸出結果(如果不知道,說明你還不清楚動態代理是什麼,開篇說了,本文對此不做講解,請自行度娘、谷歌,然後執行程式碼自己尋找答案);
假設你已經知道答案了,然後該如何入手呢?切入點就在Proxy.newProxyInstance()生成的動態代理類例項proxyInstance 上,如果我們拿到了它的原始碼,一切真相就浮出水面了,如何去拿呢?不賣關子,直接上程式碼:
byte[] bytes = ProxyGenerator.generateProxyClass("TaskService$proxy", new Class[]{ITaskService.class});
FileOutputStream fos = new FileOutputStream(new File("c://TaskService$proxy.class"));
fos.write(bytes);
fos.flush();
fos.close();
Object obj = new Object();
這段程式碼就是獲取動態代理生成的代理類,大家肯定看出來了,關鍵點就在ProxyGenerator.generateProxyClass()這個方法,為什麼是這個方法?如果我們去跟蹤Proxy.newProxyInstance()原始碼就會發現代理類的位元組碼最終是由這個方法構造出來的,它的執行過程非常簡單,如下:
Proxy類有一個靜態成員變數proxyClassCache,它就是代理類的快取,首先根據當前Classloader和業務類的介面類呼叫快取的get方法,查詢快取裡是否存在代理類的Class物件(這個快取是為了提高執行效率,避免每次呼叫都生成一個新的代理類),有就直接返回,如果不存在,則會呼叫Proxy內部類ProxyClassFactory的apply方法來構建並載入這個類,構建類就是呼叫了ProxyGenerator.generateProxyClass()方法來構建代理類的位元組碼,然後執行defineClass0()方法定義類(不清楚這個方法作用的童鞋,建議去檢視下Classloader載入類的過程,當然本文不理解這個地方是沒有影響,自己看著辦);
我們很容就拿到了它的位元組碼陣列,並把它生成class檔案儲存到了我們的硬碟上c://TaskService$proxy.class,然後通過反編譯工具就獲取到了它的原始碼:
import com.zqz.jdkproxy.ITaskService;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.lang.reflect.UndeclaredThrowableException;
public final class TaskService$proxy extends Proxy
implements ITaskService
{
private static Method m1;
private static Method m2;
private static Method m3;
private static Method m0;
public TaskService$proxy(InvocationHandler paramInvocationHandler)
throws
{
super(paramInvocationHandler);
}
public final boolean equals(Object paramObject)
throws
{
try
{
return ((Boolean)this.h.invoke(this, m1, new Object[] { paramObject })).booleanValue();
}
catch (RuntimeException localRuntimeException)
{
throw localRuntimeException;
}
catch (Throwable localThrowable)
{
}
throw new UndeclaredThrowableException(localThrowable);
}
public final String toString()
throws
{
try
{
return (String)this.h.invoke(this, m2, null);
}
catch (RuntimeException localRuntimeException)
{
throw localRuntimeException;
}
catch (Throwable localThrowable)
{
}
throw new UndeclaredThrowableException(localThrowable);
}
public final void doTask()
throws
{
try
{
this.h.invoke(this, m3, null);
return;
}
catch (RuntimeException localRuntimeException)
{
throw localRuntimeException;
}
catch (Throwable localThrowable)
{
}
throw new UndeclaredThrowableException(localThrowable);
}
public final int hashCode()
throws
{
try
{
return ((Integer)this.h.invoke(this, m0, null)).intValue();
}
catch (RuntimeException localRuntimeException)
{
throw localRuntimeException;
}
catch (Throwable localThrowable)
{
}
throw new UndeclaredThrowableException(localThrowable);
}
static
{
try
{
m1 = Class.forName("java.lang.Object").getMethod("equals", new Class[] { Class.forName("java.lang.Object") });
m2 = Class.forName("java.lang.Object").getMethod("toString", new Class[0]);
m3 = Class.forName("com.zqz.jdkproxy.ITaskService").getMethod("doTask", new Class[0]);
m0 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]);
return;
}
catch (NoSuchMethodException localNoSuchMethodException)
{
throw new NoSuchMethodError(localNoSuchMethodException.getMessage());
}
catch (ClassNotFoundException localClassNotFoundException)
{
}
throw new NoClassDefFoundError(localClassNotFoundException.getMessage());
}
}
本文重點來了,我們一步一步的解析這個類
public final class TaskService$proxy extends Proxy implements ITaskService
這個代理類是final類,繼承自Proxy類,並且實現了ITaskService,我提兩個問題,帶著問題去找答案:
1. 為什麼要繼承Proxy類?
2. 為什麼要實現ITaskService介面?
我相信第二個問題,大家肯定都知道答案,我就不費口舌了,我們只去找第一個問題的答案,接著往下看:
private static Method m1;
private static Method m2;
private static Method m3;
private static Method m0;
四個Method成員變數,這又是什麼鬼?答案在程式碼最後的static塊中:
static
{
try
{
m1 = Class.forName("java.lang.Object").getMethod("equals", new Class[] { Class.forName("java.lang.Object") });
m2 = Class.forName("java.lang.Object").getMethod("toString", new Class[0]);
m3 = Class.forName("com.zqz.jdkproxy.ITaskService").getMethod("doTask", new Class[0]);
m0 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]);
return;
}
catch (NoSuchMethodException localNoSuchMethodException)
{
throw new NoSuchMethodError(localNoSuchMethodException.getMessage());
}
catch (ClassNotFoundException localClassNotFoundException)
{
}
throw new NoClassDefFoundError(localClassNotFoundException.getMessage());
}
M1就是Object類的equals方法;
M2就是Object類的toString方法Object類的toString方法;
M3就是我們業務介面類的doTask方法;
M4就是Object類的hashCode方法;
這裡又丟擲一個問題,為什麼要獲取這四個方法呢?
M3大家肯定是沒有疑問,業務方法就是我們要代理的方法;
那麼equals,toString,hashCode為什麼也要代理呢?其實我們想一想這三個方法的作用就知道了,如果不對這三個方法做代理,當呼叫這三個方法的時候,返回的就是新生成的代理物件的toString,equals,hashCode的方法,而不是我們原來業務物件的方法,執行結果肯定是不一致的,拿hashCode方法來說,動態生成的代理類與我們原來業務類是兩個不同的類,所以hashCode肯定也是不一樣的,這樣就和我們預期結果不一樣了,所以這三個方法也是需要代理的;
這裡還有一個問題不知道大家有沒有注意,即使我們需要代理這四個方法,為什麼要專門把它們賦給成員變數呢?原因就在於執行效率,如果熟悉反射的話,就會知道getMethod方法要做各種檢查、查詢操作,比直接呼叫方法要慢很多,所以在初始化的時候獲取到它們,並賦給成員變數,起到一個快取作用;
剩下的就是業務介面實現的方法了,他們實現程式碼基本是一模一樣,我們只拿實現的業務介面的方法來分析:
public final void doTask()
throws
{
try
{
this.h.invoke(this, m3, null);
return;
}
catch (RuntimeException localRuntimeException)
{
throw localRuntimeException;
}
catch (Throwable localThrowable)
{
}
throw new UndeclaredThrowableException(localThrowable);
}
關鍵程式碼在this.h這個物件上,它又是何方神聖,如果我們去動態代理類的父類Proxy類中一找就會發現,原來它就是Proxy中的一個成員變數:
protected InvocationHandler h;
還記得我們在TestMain 方法裡面建立的那個InvocationHandler例項物件嗎? 這個h就是我們傳入的InvocationHandler物件的例項,在介面實現方法中,就是通過它呼叫我們自己實現的invoke方法來實現代理邏輯。
所以流程就是,生成動態代理物件繼承了Proxy類,Proxy類裡面有一個成員變數持有我們自己建立的InvocationHandler物件,動態代理類在實現了業務介面的方法中,呼叫我們在InvocationHandler物件中實現的invoke方法來實現最終的代理邏輯;
簡單吧?貌似也不是很簡單,但是也沒有我們想象中的那麼神奇,完!
相關文章
- 徹底搞懂 Channel 實現原理
- 徹底搞懂徹底搞懂事件驅動模型 - Reactor事件模型React
- 徹底搞懂訪問者模式的靜態、動態和偽動態分派模式
- 混合移動App乾貨:一篇就可以徹底搞懂!APP
- 徹底搞懂 RxJavaRxJava
- 乾貨預警,一篇文章帶你徹底搞懂 Laravel 框架的執行原理!!!Laravel框架
- 一篇文章讓你徹底瞭解Java內部類Java
- 徹底搞懂Spring狀態機原理,實現訂單與物流解耦Spring解耦
- 從原理到實戰,徹底搞懂NginxNginx
- SAP ABAP和Java的動態代理實現Java
- 徹底搞懂https原理HTTP
- 徹底搞懂JavaScript作用域JavaScript
- 徹底搞懂Bean載入Bean
- 徹底搞懂 Git-RebaseGit
- 徹底搞懂Composer自動載入原理
- 一篇文章搞懂 Activity 啟動模式模式
- 一篇文章讓你徹底掌握 shell 語言
- 徹底搞懂JavaScript中的繼承JavaScript繼承
- 徹底搞懂Python中的類Python
- go如何實現類似java的動態代理GoJava
- PC微信機器人介面api之徹底搞懂hook原理及手動實現機器人APIHook
- 這篇文章讓你徹底搞懂ES6中的Class(全面解析)
- 深入JavaScript系列(四):徹底搞懂thisJavaScript
- 徹底搞懂 RxJava — 基礎篇RxJava
- 徹底搞懂 RxJava — 中級篇RxJava
- 徹底搞懂 RxJava — 高階篇RxJava
- Java使用Porxy和InvocationHandler實現動態代理Java
- 用es5實現es6的promise,徹底搞懂promise的原理Promise
- 徹底理解Netty,這一篇文章就夠了Netty
- 徹底搞懂Scrapy的中介軟體(三)
- 徹底搞懂Scrapy的中介軟體(二)
- 徹底搞懂Scrapy的中介軟體(一)
- 徹底搞懂HTTPS的加密機制HTTP加密
- 徹底搞懂Object和Function的關係ObjectFunction
- Java代理設計模式(Proxy)的四種具體實現:靜態代理和動態代理Java設計模式
- 4個實驗,徹底搞懂TCP連線的斷開TCP
- 徹底搞懂 etcd 系列文章(三):etcd 叢集運維部署運維
- Java JDK 動態代理使用及實現原理分析JavaJDK