JDK動態代理
先寫一個JDK動態代理,一個演員需要有經紀人,我們把演員當成被代理類,經紀人當成代理類
演員類Actor.java:
package com.lw.designpattern.proxy.dynamic;
public class Actor {
public void act() {
System.out.println("某毯星戛納走紅毯");
}
}
調用處理類ActorInvocation.java,爲什麼要設計這麼一個類,後面講
package com.lw.designpattern.proxy.dynamic;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
public class ActorInvocation<T> implements InvocationHandler {
T target;
public ActorInvocation(T target) {
this.target = target;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("經紀人準備");
method.invoke(this.target, args);
System.out.println("經紀人善後");
return null;
}
}
測試類:
package com.lw.designpattern.proxy.dynamic;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy;
public class DynamicProxyTest {
public static void main(String[] args) {
Actor actor = new Actor();
InvocationHandler actorHandler = new ActorInvocation<Actor>(actor);
Actor actorProxy = (Actor) Proxy.newProxyInstance(Actor.class.getClassLoader(), new Class<?>[] { Actor.class }, actorHandler);
actorProxy.act();
}
}
測試結果:
Exception in thread "main" java.lang.IllegalArgumentException: com.lw.designpattern.proxy.dynamic.Actor is not an interface
at java.lang.reflect.Proxy$ProxyClassFactory.apply(Proxy.java:590)
at java.lang.reflect.Proxy$ProxyClassFactory.apply(Proxy.java:557)
at java.lang.reflect.WeakCache$Factory.get(WeakCache.java:230)
at java.lang.reflect.WeakCache.get(WeakCache.java:127)
at java.lang.reflect.Proxy.getProxyClass0(Proxy.java:419)
at java.lang.reflect.Proxy.newProxyInstance(Proxy.java:719)
at com.lw.designpattern.proxy.dynamic.DynamicProxyTest.main(DynamicProxyTest.java:12)
咦,報錯了。。。。提示Actor類不是一個接口,原因是啥,暫且按下不表,先改成接口再說
package com.lw.designpattern.proxy.dynamic;
public interface Actor {
public void act();
}
增加實現類:
package com.lw.designpattern.proxy.dynamic;
public class BBFan implements Actor {
@Override
public void act() {
System.out.println("某毯星戛納走紅毯");
}
}
測試結果:
經紀人準備
某毯星戛納走紅毯
經紀人善後
OK,搞定,下面講講實現原理
JDK動態代理原理
在說原理之前,先搞清楚概念,所謂的動態,其實就是動態生成一個代理類Class,相比較靜態代理模式的手寫代理類,動態代理在這方面可謂是更加靈活。下面先看看生成的代理類是個什麼樣子。
代理類我是直接通過ProxyGenerator類生成的,這也是JDK源碼使用的類,所以生成的代理類中沒有包含自定義的代理功能。
當然也可以在測試方法中加上這句:System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles", "true");
byte[] proxyClassFile = ProxyGenerator.generateProxyClass("ActorProxy", new Class[]{Actor.class});
try {
FileOutputStream f = new FileOutputStream("ActorProxy.class");
f.write(proxyClassFile);
f.flush();
f.close();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
生成代理類class的反編譯源碼:
import com.lw.pattern.proxy.dynamic.Actor;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.lang.reflect.UndeclaredThrowableException;
public final class ActorProxy extends Proxy implements Actor {
private static Method m1;
private static Method m2;
private static Method m3;
private static Method m0;
public ActorProxy(InvocationHandler var1) throws {
super(var1);
}
public final boolean equals(Object var1) throws {
try {
return (Boolean)super.h.invoke(this, m1, new Object[]{var1});
} catch (RuntimeException | Error var3) {
throw var3;
} catch (Throwable var4) {
throw new UndeclaredThrowableException(var4);
}
}
public final String toString() throws {
try {
return (String)super.h.invoke(this, m2, (Object[])null);
} catch (RuntimeException | Error var2) {
throw var2;
} catch (Throwable var3) {
throw new UndeclaredThrowableException(var3);
}
}
// 業務相關的核心方法
public final void act() throws {
try {
// 這邊通過父類的h屬性調用實際業務方法
super.h.invoke(this, m3, (Object[])null);
} catch (RuntimeException | Error var2) {
throw var2;
} catch (Throwable var3) {
throw new UndeclaredThrowableException(var3);
}
}
public final int hashCode() throws {
try {
return (Integer)super.h.invoke(this, m0, (Object[])null);
} catch (RuntimeException | Error var2) {
throw var2;
} catch (Throwable var3) {
throw new UndeclaredThrowableException(var3);
}
}
static {
try {
m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object"));
m2 = Class.forName("java.lang.Object").getMethod("toString");
m3 = Class.forName("com.lw.pattern.proxy.dynamic.Actor").getMethod("act");
m0 = Class.forName("java.lang.Object").getMethod("hashCode");
} catch (NoSuchMethodException var2) {
throw new NoSuchMethodError(var2.getMessage());
} catch (ClassNotFoundException var3) {
throw new NoClassDefFoundError(var3.getMessage());
}
}
}
從這個源碼我們來解釋一下上面遺留的兩個問題:
一,代理類繼承了JDK的Proxy Class,因爲Java規範設計一個類只能繼承一個父類,所以,這也就解釋了爲什麼JDK的動態代理要求被代理類一定要實現接口。
二,Invocation的設計是爲了動態代理的調用,我們可以在Proxy類中找到關於Invocation的屬性定義:
/**
* the invocation handler for this proxy instance.
* @serial
*/
protected InvocationHandler h;
有了這個實例變量的定義,我們就可以把這個Invocation看作是動態代理的模板方法,這樣的話,每new一個新的被代理類,就不需要像靜態動態代理那樣修改代理類。
看到這裏,就會發現,動態代理真的沒有什麼高深的東西,本質上同靜態代理的實現思路也一致。