對比靜態代理
靜態代理:是指在程序運行前就已經定義好了目標類的代理類。代理類與目標類的代理關係在程序運行之前就確立了。
動態代理:是指程序在整個運行過程中根本就不存在目標類的代理類,目標對象的代理對象只是代理生成工具(如代理工廠類)在程序運行時由JVM根據反射等機制動態生成的。代理對象與目標對象的代理關係在程序運行時才確立。
概念
動態代理類類似與普通當事人與聘請的律師間的關係。律師是在“官司”發生後,才由當事人聘請的。即代理關係是在“官司”發生後才確立的。
動態代理的實現方式常用的有兩種:使用JDK的Proxy,與通過CGLIB生產代理。
分析
①通過JDK的java.lang.relect.Proxy類實現動態代理,會使用其靜態方法newProxyInstance(),依據目標對象、業務接口及增強邏輯三者,自動生成一個動態代理對象。
public static Object newProxyInstance(ClassLoader loader,
Class<?>[] interfaces,
InvocationHandler h)
loader:目標類的類加載器,通過目標對象的反射可獲取
interfaces:目標類實現的接口數組,通過目標對象的反射可獲取
handler:業務增強邏輯,需要再定義
InvocationHandler 是個接口,其具體介紹如下:實現了InvocationHandler 接口的類用於加強目標類的主業務邏輯。這個接口中有一個方法invoke(),具體加強的代理邏輯就是定義在該方法中的。程序調用主業務邏輯時,會自動調用invoke()方法。
②invoke()方法的介紹如下:
public Object invoke(Object proxy, Method method, Object[] args)
proxy:代表生成的代理對象
method:代表目標方法
args:代表目標方法的參數
由於該方法是由代理對象自動調用的,所以這三個參數的值不用開發人員給出。
③第二個參數爲Method類對象,該類有一個方法也叫invoke(),可以調用目標類目標的方法。這兩個invoke()方法,雖然同名,但無關。
public Object invoke(Object obj, Object... args)
obj:表示目標對象
args:表示目標方法參數,就是其上一層invoke方法的第三個參數
該方法的作用是:調用執行obj對象所屬類的方法,這個方法由其調用者Method對象確定。
在代碼中,一般寫法爲method.invoke(target, args);
其中,method爲上一層invoke方法的第二個參數。這樣,即可調用了目標類的目標方法。
大家單看分析還不夠清晰,下面看一下 代理實現與解析
(1)定義業務接口GameService,其中含有抽象方法gameStart()
public interface GameService {
//主業務邏輯
void gameStart();
}
(2)定義目標類GameServiceImpl,該類實現了業務接口。在對接口方法的實現上,只實現主業務邏輯。這個方法稱爲目標方法。
public class GameServiceImpl implements GameService {
/**
* 目標方法
*/
@Override
public void gameStart() {
System.out.println("這裏調用Dao層等,完成gameStart()");
}
}
(3)定義主業務增強邏輯類JdkDynamic,該類需實現接口InvocationHandler。在該類中定義一個Object類型的成員變量,還要定義一個帶參的構造器,這個參數爲Object對象。目的是,將目標對象引入該類,以便通過反射調用目標方法。
當然,也可以將這個屬性定義爲GameService接口類型,但最好不要這樣做,最好將其定義爲Object。因爲,這樣這個主業務增強邏輯可以使用於本項目中的任何類型的目標類,而不僅僅拘泥於一個類。
/*
* 定義代理類,與目標類實現相同的業務接口,增強邏輯
*/
public class JdkDynamic implements InvocationHandler {
//聲明業務接口對象
private Object target;
public JdkDynamic(){}
public JdkDynamic(Object target){
this.target = target;
}
/* 代理方法,實現對目標方法的功能增強 */
@Override
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
// 增加主業務邏輯代碼
System.out.println("對用戶進行身份驗證");
// 無論主業務方法有無參數,有無返回值,下面的方法均可兼顧到
return method.invoke(target, args);
}
}
(4)定義客戶類Client。客戶類中主要語句有三句:
A、定義目標對象。在生成代理對象時會需要目標對象對其初始化。
B、定義代理對象。需要注意的是,代理類Proxy會通過反射機制,自動實現GameService接口。代理對象需要使用目標對象對其進行初始化。
C、代理對象調用主業務方法。
public static void main(String[] args) throws IOException {
//定義目標對象
GameService target = new GameServiceImpl();
//創建代理對象,並使用目標對象初始化它
GameService service = (GameService) Proxy.newProxyInstance(
target.getClass().getClassLoader(), // 獲取目標對象的類加載器
target.getClass().getInterfaces(), // 獲取目標類失效的所有接口
new JdkDynamic(target)); // 增強業務邏輯
//此時執行的內容,就是對目標對象增加過的內容
service.gameStart();
//將JDK動態代理生成的代理類,通過流的形式保存到磁盤中
System.out.println(service.getClass()); // class com.sun.proxy.$Proxy0
/**
* 上面生成的代理對象字節碼 com.sun.proxy.$Proxy0 是在內存中的
* 這裏將其對象寫到文件中,通過反編譯查看
*/
byte[] bytes = ProxyGenerator.generateProxyClass("$Proxy0", new Class[]{GameService.class});
FileOutputStream fos = new FileOutputStream("D://code/$Proxy0.class");
fos.write(bytes);
fos.flush();
System.out.println("文件寫入成功");
}
(5)將JDK動態生成的代理類,通過流的形式保存到磁盤中,使用反編譯工具查看:
import com.proxyDemo.jdkProxy.service.GameService;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.lang.reflect.UndeclaredThrowableException;
public final class $Proxy0 extends Proxy
implements GameService
{
private static Method m1;
private static Method m3;
private static Method m0;
private static Method m2;
public $Proxy0(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 void gameStart()
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);
}
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);
}
static
{
try
{
m1 = Class.forName("java.lang.Object").getMethod("equals", new Class[] { Class.forName("java.lang.Object") });
m3 = Class.forName("com.proxyDemo.jdkProxy.service.GameService").getMethod("gameStart", new Class[0]);
m0 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]);
m2 = Class.forName("java.lang.Object").getMethod("toString", new Class[0]);
return;
}
catch (NoSuchMethodException localNoSuchMethodException)
{
throw new NoSuchMethodError(localNoSuchMethodException.getMessage());
}
catch (ClassNotFoundException localClassNotFoundException)
{
}
throw new NoClassDefFoundError(localClassNotFoundException.getMessage());
}
}
分析JDK動態生成的代理類
1.生成的代理類繼承Proxy類並實現業務接口,並生成了被代理類的全部方法,包括toString、equals等等所有方法
2.Java是單繼承的,代理類實現了【需要被代理類的實現接口】以供我們可以將其強轉;
3.這裏生成的代理類繼承自Proxy,在Proxy中有這麼一段代碼:protected InvocationHandler h;
這是在生成代理類的時候傳入的InvocationHandler的實現,【開發的代理增強代碼(實現InvocationHandler重寫invoke()方法)】都寫在invoke()方法中,最終代理類執行代理也是通過調用父類Proxy中的InvocationHandler接口的invoke()方法;
在調用【被代理】方法時,代理類通過調用生成的同名方法然後調用super.h.invoke()方法,最終就到了我們實現InvocationHandler時所寫的invoke()增強方法;在此時,method.invoke()才真正通過反射調用了我們自身所寫的方法
可以看一下這個鏈接下JDK是如何動態生成代理類的代理類是通過Proxy類的ProxyClassFactory工廠生成的
希望對你有幫助,祝你有一個好心情,加油!
若有錯誤、不全、可優化的點,歡迎糾正與補充!