一、代理相關概念:被代理類(真實類),代理類
二、代理的作用:
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 動態代理機制分析及擴展