java 基於JDK動態代理簡單示例源碼以及詳細解析

一、代理相關概念:被代理類(真實類),代理類
二、代理的作用:
1、無侵入式擴展代碼,既增強功能,且做到解耦,代理邏輯與業務邏輯是互相獨立,在不改變真實類代碼情況下做一些額外處理(Spring AOP 事務控制、日誌管理)。
2、保護真實類(被代理類):代理類攔截對真實類的直接訪問,對真實類做訪問檢查和控制,調用者只需要和代理類進行交互即可,不必關心真實類的實現細節。
3、懶加載,程序啓動時加載的是輕量級代理類,真實類只有在通過代理類調用的時候纔會創建(Hibernate 的延遲加載(lazy load)本質上就是代理模式的應用,我們就經常通過代理模式來降低系統的內存開銷、提升應用的運行性能。Hibernate 充分利用了代理模式的這種優勢,並結合了 Javassist 或 CGLIB 來動態地生成代理對象,這更加增加了代理模式的靈活性,Hibernate 給這種用法一個新名稱:延遲加載)。

三、動態代理靜態代理區別
動態代理是一對多,靜態代理一對一
動態代理是在運行時動態成代理類放在內存中,靜態代理運行前就已經生成代理類的.class文件

四、jdk動態代理簡單實現以及底層原理
4-1 jdk動態代理簡單實現:
接口:Cooker.java

public interface Cooker {
	public void cook();
}

真實類(被代理類):ElectricCooker.java

public class ElectricCooker implements Cooker {
	@Override
	public void cook() {
		System.out.println("電飯鍋煮飯");
	}
}

InvocationHandler接口實現類:DynamicProxy.java

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
/**
 * Class<?> cl = getProxyClass0(loader, intfs);生成代理類
 */
public class DynamicProxy implements InvocationHandler {
	private Cooker target;
	public Cooker getInstance(Cooker cooker ) {
		this.target = cooker;
		Class<?> clazz = target.getClass();
		return (Cooker) Proxy.newProxyInstance(clazz.getClassLoader(), clazz.getInterfaces(), this);
	}
	@Override
	public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
		before();
		Object obj = method.invoke(this.target, args);
		after();
		return obj;
	}
	private void before() {
		System.out.println("before。。。插上電源");
	}
	private void after() {
		System.out.println("after。。。拔掉電源");
	}
}

測試類:DynamicProxyTest.java

public class DynamicProxyTest {
	String basePath = "C:/Users/zhou/Desktop/Jad/";

	@Test
	public void testCook() {
		Cooker cooker = new DynamicProxy().getInstance(new ElectricCooker());
		cooker.cook();
	}

	@Test
	public void testGetClassFile() {
		Cooker cooker = new DynamicProxy().getInstance(new ElectricCooker());
		byte[] classFile = sun.misc.ProxyGenerator.generateProxyClass(cooker.getClass().getName(),
				cooker.getClass().getInterfaces());
		String path = basePath + cooker.getClass().getSimpleName() + ".class";
		try (FileOutputStream fos = new FileOutputStream(path)) {
			fos.write(classFile);
			fos.flush();
			System.out.println("代理類class文件寫入成功,classNme:" + cooker.getClass().getName());
		} catch (Exception e) {
			e.printStackTrace();
			System.out.println("寫文件錯誤");
		}
	}
}

測試結果 testCook():

before。。。插上電源
電飯鍋煮飯
after。。。拔掉電源

測試結果 testGetClassFile(),通過jad反編譯得到動態生成的代理類$Proxy4代碼:

package com.uniz.study.proxy.source;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.lang.reflect.UndeclaredThrowableException;

import com.uniz.study.proxy.Cooker;

public final class $Proxy4 extends Proxy implements Cooker {
	private static Method m1;
	private static Method m2;
	private static Method m3;
	private static Method m0;

	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.uniz.study.proxy.Cooker").getMethod("cook", new Class[0]);
			m0 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]);
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
	public $Proxy4(InvocationHandler paramInvocationHandler) {
		super(paramInvocationHandler);
	}

	public final void cook() {
		try {
			this.h.invoke(this, m3, null);
			return;
		} catch (RuntimeException localRuntimeException) {
			throw localRuntimeException;
		} catch (Throwable localThrowable) {
			throw new UndeclaredThrowableException(localThrowable);
	}
	}

	public final boolean equals(Object paramObject)
  {
    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()
  {
    try
    {
      return (String)this.h.invoke(this, m2, null);
    }
    catch (RuntimeException localRuntimeException)
    {
      throw localRuntimeException;
    }
    catch (Throwable localThrowable)
    {
      throw new UndeclaredThrowableException(localThrowable);
    }
  }
	public final int hashCode()
  {
    try
    {
      return ((Integer)this.h.invoke(this, m0, null)).intValue();
    }
    catch (RuntimeException localRuntimeException)
    {
      throw localRuntimeException;
    }
    catch (Throwable localThrowable)
    {
      throw new UndeclaredThrowableException(localThrowable);
    }
  }
}

Proxy.java
在這裏插入圖片描述
在這裏插入圖片描述

4-2 通過上面testGetClassFile()測試方法分析動態代理底層邏輯實現:

Cooker cooker = new DynamicProxy().getInstance(new ElectricCooker());
此處cooker的實際類型是動態生成的代理類$Proxy4,

1、$Proxy4的實現了Cooker 接口:

public final void cook() {
		try {
			this.h.invoke(this, m3, null);
			return;
		} catch (RuntimeException localRuntimeException) {
			throw localRuntimeException;
		} catch (Throwable localThrowable) {
			throw new UndeclaredThrowableException(localThrowable);
	}

2、方法中this.h是$Proxy4類的屬性protected InvocationHandler h,在生成動態代理類時,DynamicProxy 把自己作爲參數傳給了代理類,因此此處this.h = DynamicProxy對象,而DynamicProxy類剛好實現了InvocationHandler接口的invoke方法,並且invoke方法中執行method.invoke(target, args)時,參數target是真實類的對象引用,因而能執行真實對象的方法

五、debug跟蹤動態代理類的詳細創建過程:
在這裏插入圖片描述
在這裏插入圖片描述
在這裏插入圖片描述
在這裏插入圖片描述
在這裏插入圖片描述
在這裏插入圖片描述
在這裏插入圖片描述
在這裏插入圖片描述
在這裏插入圖片描述
在這裏插入圖片描述
至此,由上面debug過程發現,代理類$Proxy4的關鍵創建過程如下:

Cooker cooker = new DynamicProxy().getInstance(new ElectricCooker());
Proxy.newProxyInstance(clazz.getClassLoader(), clazz.getInterfaces(), this)
Class<?> cl = getProxyClass0(loader, intfs);
valueFactory.apply(key, parameter)
byte[] proxyClassFile = ProxyGenerator.generateProxyClass( proxyName, interfaces, accessFlags);
最後發現創建代理類的方法 ProxyGenerator.generateProxyClass( proxyName, interfaces, accessFlags);
測試方法testGetClassFile()正是調用此方法得到代理對象$Proxy4

小結:
1、真實類(被代理類)把自己委託給代理類,在代理類中可以對其功能增強,同時做到代理邏輯與業務邏輯的解耦,當真實類比較臃腫時,初始化時用輕量級的代理類替代直接初始化真實類,從而實現真實類的延遲加載。
2、jdk的動態代理簡單實現邏輯:

// InvocationHandlerImpl 實現了 InvocationHandler 接口,並能實現方法調用從代理類到委託類的分派轉發
InvocationHandler handler = new InvocationHandlerImpl(..); 
 
// 通過 Proxy 直接創建動態代理類實例
Interface proxy = (Interface)Proxy.newProxyInstance( classLoader, 
     new Class[] { Interface.class }, 
     handler );

3、常見的代理場景:
遠程代理:爲位於兩個不同地址空間對象的訪問提供了一種實現機制,可以將一些消耗資源較多的對象和操作移至性能更好的計算機上,提高系統的整體運行效率。
虛擬代理:通過一個消耗資源較少的對象來代表一個消耗資源較多的對象,可以在一定程度上節省系統的運行開銷
緩存代理:爲某一個操作的結果提供臨時的緩存存儲空間,以便在後續使用中能夠共享這些結果,優化系統性能,縮短執行時間
保護代理:可以控制對一個對象的訪問權限,爲不同用戶提供不同級別的使用權限。
智能引用代理:當需要爲一個對象的訪問(引用)提供一些額外的操作時可以使用智能引用代理

參考:
Java 動態代理機制分析及擴展

java動態代理實現與原理詳細分析

JAVA動態代理

代理模式 | 菜鳥教程

JDK動態代理|ProxyGenerator生成代理類的字節碼文件解析

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章