導讀
代理模式就是自己做不了或不想做的事情找別人做迹蛤,比如我們買不到票,找黃牛買襟士,這就是代理模式盗飒。
代理模式分為調用方、代理陋桂、目標三部分逆趣。
我們常用的Java代理模式主要有兩種:
靜態(tài)代理
動態(tài)代理
靜態(tài)代理是設計模式中的一種,也就是硬編碼嗜历,一旦需要代理的類或方法多了宣渗,操作使用很不方便。今天主要講動態(tài)代理 梨州。
基本用法
場景痕囱,普通粉絲通過黃牛購買演唱會的門票,
先看一下代碼實現(xiàn)
接口類 TicketCenter.java
public interface TicketCenter {
void buyTicket(Integer amount);
void refundTicket();
void sellTicket();
}
接口實現(xiàn)類 RealTicketCenter.java
public class RealTicketCenter implements TicketCenter {
@Override
public void buyTicket(Integer amount) {
System.out.println("buyTicket,need ¥" + amount);
}
@Override
public void refundTicket() {
System.out.println("refundTicket");
}
@Override
public void sellTicket() {
System.out.println("sellTicket");
}
}
代理類 ProxyTicketCenter.java
public class ProxyTicketCenter implements InvocationHandler {
RealTicketCenter realTicketCenter;
public ProxyTicketCenter(RealTicketCenter realTicketCenter) {
this.realTicketCenter = realTicketCenter;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("before");
Object object = null;
object=method.invoke(realTicketCenter,args);
System.out.println("after");
return object;
}
}
使用 Main.java
public class Main {
public static void main(String args[]){
//保留生成的字節(jié)碼文件
System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles", "true");
TicketCenter ticketCenter = (TicketCenter) Proxy.newProxyInstance(Main.class.getClassLoader(),new Class[]{TicketCenter.class},new ProxyTicketCenter(new RealTicketCenter()));
ticketCenter.buyTicket(10);
}
}
執(zhí)行結果如下
before
buyTicket,need ¥10
after
源碼分析
我們可以看到在執(zhí)行buyTicket方法前后執(zhí)行了我們代理需要做的事情暴匠。
我們進入到newProxyInstance方法
/*
* Look up or generate the designated proxy class.
*/
Class<?> cl = getProxyClass0(loader, intfs);
這里是找到或生成指定的代理類鞍恢,
進入到getProxyClass0方法
/**
* Generate a proxy class. Must call the checkProxyAccess method
* to perform permission checks before calling this.
*/
private static Class<?> getProxyClass0(ClassLoader loader,
Class<?>... interfaces) {
if (interfaces.length > 65535) {
throw new IllegalArgumentException("interface limit exceeded");
}
// If the proxy class defined by the given loader implementing
// the given interfaces exists, this will simply return the cached copy;
// otherwise, it will create the proxy class via the ProxyClassFactory
return proxyClassCache.get(loader, interfaces);
}
從緩存中獲取,如果沒有則生成每窖,我們看一下proxyClassCache這個對象帮掉,存的是代理類緩存
/**
* a cache of proxy classes
*/
private static final WeakCache<ClassLoader, Class<?>[], Class<?>>
proxyClassCache = new WeakCache<>(new KeyFactory(), new ProxyClassFactory());
這里重點要看下ProxyClassFactory,代理類工廠岛请,是如何生成的
/*
* Generate the specified proxy class.
*/
byte[] proxyClassFile = ProxyGenerator.generateProxyClass(
proxyName, interfaces, accessFlags);
看到這段代碼旭寿,就豁然開朗了,生成代理類的字節(jié)碼崇败,那么一個代理類就生成了盅称,有興趣的同學可以進入generateProxyClass方法看他是怎么生成的肩祥。
那生成的代碼是如何的呢?
我們在Main類有一段代碼就起作用了
System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles", "true");
這段代碼會將生成的字節(jié)碼保存到本地
一起看下生成的關鍵代碼
public final class $Proxy0 extends Proxy implements TicketCenter {
private static Method m1;
private static Method m5;
private static Method m2;
private static Method m3;
private static Method m4;
private static Method m0;
public $Proxy0(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 void refundTicket() throws {
try {
super.h.invoke(this, m5, (Object[])null);
} catch (RuntimeException | Error var2) {
throw var2;
} catch (Throwable var3) {
throw new UndeclaredThrowableException(var3);
}
}
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 buyTicket(Integer var1) throws {
try {
super.h.invoke(this, m3, new Object[]{var1});
} catch (RuntimeException | Error var3) {
throw var3;
} catch (Throwable var4) {
throw new UndeclaredThrowableException(var4);
}
}
public final void sellTicket() throws {
try {
super.h.invoke(this, m4, (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"));
m5 = Class.forName("TicketCenter").getMethod("refundTicket");
m2 = Class.forName("java.lang.Object").getMethod("toString");
m3 = Class.forName("TicketCenter").getMethod("buyTicket", Class.forName("java.lang.Integer"));
m4 = Class.forName("TicketCenter").getMethod("sellTicket");
m0 = Class.forName("java.lang.Object").getMethod("hashCode");
} catch (NoSuchMethodException var2) {
throw new NoSuchMethodError(var2.getMessage());
} catch (ClassNotFoundException var3) {
throw new NoClassDefFoundError(var3.getMessage());
}
}
}
我們在Main方法調用的 ticketCenter.buyTicket(10);
其實就是調用了這個類里面的buyTicket方法缩膝,在buyTicket方法里調用了InvocationHandler 的invoke方法混狠。這樣一來,我們就明白了是動態(tài)代理是如何實現(xiàn)的了疾层。
源碼 見lean-proxy模塊 https://github.com/HuangPugang/Java-lean