一:概述
????Java的代理模式主要分為靜態(tài)代理和動態(tài)代理全陨,靜態(tài)代理非常簡單黍聂,簡單說明一下豆拨,重點是動態(tài)代理痹束。先來看下代理模式的類圖(盜圖):二:靜態(tài)代理
????理解靜態(tài)代理的經(jīng)典場景就是買火車票张症,火車票代售點和火車站之間的關(guān)系就是靜態(tài)代理的關(guān)系无畔。代售點具有賣出火車票的功能,火車站也有賣票的功能吠冤,但是代售點賣票是依賴于火車站實現(xiàn)的浑彰,而火車站自己就實現(xiàn)了賣票功能;既然代售點和火車站都有賣票功能拯辙,那么可以把這個功能提取出來郭变,封裝在一個接口里面,然后代售點和火車站都實現(xiàn)這個接口涯保,那么他們都有了賣票的功能:
public interface Ticket {
void saleTicket();
}
????下面分別實現(xiàn)火車站類和代售點類:
//火車站類
public class TrainStation implements Ticket {
@Override
public void saleTicket() {
System.out.println("我是火車站诉濒,1、2夕春、3未荒,賣票");
}
}
//代售點類
public class ProxyStation implements Ticket {
Ticket sale = new TrainStation();
@Override
public void saleTicket() {
System.out.println("我是代售點,開始賣票");
sale.saleTicket();
}
}
????從上面的代碼可以看出及志,代售點類持有了一個火車站類的引用片排,代售點賣票其實就是調(diào)用了火車站類的賣票方法,實際的賣票動作是在火車票進行的速侈;代售點類就是個大騙子率寡,什么事都使喚火車站類去做。測試代碼如下:
public static void main(String[] args) {
Ticket t1 = new ProxyStation();
t1.saleTicket();
}
????輸出結(jié)果如下:
我是代售點倚搬,開始賣票
我是火車站冶共,1、2、3捅僵,賣票
????通過上面的代碼家卖,我們可以歸納出代理模式的三大要素:公共接口(Ticket)、真實對象(TrainStation)和代理對象(ProxyStation)庙楚。代理對象是供外部調(diào)用的對象篡九,真實對象是真正實現(xiàn)公共接口邏輯的對象,公共接口就是抽象出代理對象和真實對象的公共功能的地方醋奠,這都很好理解榛臼。
????也許有人會說,為什么不能直接創(chuàng)建真實對象直接調(diào)用方法窜司,而非要弄出一個中間人角色的代理對象出來沛善?要知道,代理對象雖然是狐假虎威塞祈,但是他對隱藏真實對象是很有用的金刁,外部不能直接與真實對象接觸,這對保護數(shù)據(jù)什么的有很大作用议薪;而且尤蛮,在代理類里面也可以對方法進行增強:比如在賣票之前,檢查身份證什么的斯议;這樣真實對象就可以專注于自己業(yè)務(賣票)的實現(xiàn)了产捞。
????靜態(tài)代理就是這么簡單,沒什么好說的哼御。下面通過例子來說明動態(tài)代理坯临。在上面的例子中,現(xiàn)在要求代售點在賣票之前恋昼,先檢查身份證的有效性看靠,這樣就需要添加一個校驗身份證的功能(方法)。根據(jù)開閉原則液肌,既然接口挟炬、TrainStation和ProxyStation已經(jīng)寫好了,穩(wěn)定了嗦哆,那么就不應該再去該他了谤祖,怎么辦?當然是創(chuàng)建一個新的代理類吝秕,在新的代理類里面實現(xiàn)校驗身份證的功能:
public class ProxyStation1 implements Ticket{
Ticket sale = new TrainStation();
@Override
public void saleTicket() {
System.out.println("我是代售點泊脐,開始賣票");
//原來的邏輯不變,但是添加檢查身份證的功能
boolean pass = checkID();
if(pass) {
sale.saleTicket();
}
}
//檢查身份證
public boolean checkID() {
//假裝檢查通過
return true;
}
}
????測試代碼就不寫了烁峭。可以看到,這樣寫沒什么大問題约郁,僅僅是新增了一個類缩挑,增加了檢查身份證的方法,同時重寫了賣票的方法鬓梅。
????現(xiàn)在為了了解賣票過程的更具體的信息供置,需要計時功能,就是記錄買一張票要多長時間绽快,為鐵道部以后的服務政策提供數(shù)據(jù)支撐芥丧。還是根據(jù)開閉原則,原來的類不能動坊罢,只能再新增一個類:
public class ProxyStation1 implements Ticket{
Ticket sale = new TrainStation();
//開始賣票和結(jié)束賣票的時間戳
long start;
long end;
@Override
public void saleTicket() {
//開始計時
start = System.currentTimeMillis();
System.out.println("我是代售點续担,開始賣票");
//原來的邏輯不變,但是添加檢查身份證的功能
boolean pass = checkID();
if(pass) {
sale.saleTicket();
//結(jié)束計時
end = System.currentTimeMillis();
//計算賣一張票需要花費多少時間
long duration = end - start;
}
}
//檢查身份證
public boolean checkID() {
//假裝檢查通過
return true;
}
}
????為了增加一個功能活孩,又要實現(xiàn)一個類物遇;當然,上面的類可以通過基礎實現(xiàn)部分代碼的復用憾儒,這里僅僅是為了舉例询兴,沒去注意做。隨著功能的越來越多起趾,你就會發(fā)現(xiàn)新增的類就會越來越多诗舰,這樣做肯定是不合理的,況且維護起來也非常麻煩训裆。
????所以靜態(tài)代理就會有這樣的弊端始衅,靜態(tài)代理需要我們事先把代理類預先寫好,預先寫好有預先寫好的好處缭保,但是一旦需要擴展的時候汛闸,就會使得類的數(shù)量彭總;而且有些功能艺骂,比如檢查身份證的方法checkID诸老,在別的場景也能用得上,比如辦銀行卡钳恕;如果在一個辦卡的類里面也要實現(xiàn)檢查身份證功能别伏,怎么辦?還能咋地忧额,再寫一次唄厘肮,又不能把賣票的類引進來調(diào)用,畢竟賣票的類有很多功能和把銀行卡沒半毛錢關(guān)系睦番,引進來不合適类茂;計時功能可能在別的場景上遇得到耍属,比如去營業(yè)廳辦理業(yè)務,為了提高服務質(zhì)量巩检,首先要調(diào)查每次服務的時間厚骗,這個還是也要引入計時功能(假裝計時功能封裝在一個方法里面,而不是上面的獲取兩個時間戳然后相減)兢哭,怎么辦领舰?再寫一個與運營商有關(guān)的代理類唄,然后SB的把計時功能再寫一遍(或者Ctrl + c然后Ctrl + v)迟螺;可是這樣做累不累冲秽?所以靜態(tài)代理可能會導致代碼量的急劇碰撞,一些相同功能得不到復用矩父。
三:動態(tài)代理
????這個時候锉桑,動態(tài)代理就派上用場了。還是以賣票為例 浙垫,公共接口和真實對象不變刨仑,從增加身份證檢查的功能開始,先定義一個實現(xiàn)了InvocationHandler接口的類CheckIDHandler:
public class CheckIDHandler implements InvocationHandler {
//真實對象類夹姥,在本例中就是火車站類
private Object target;
//檢查身份證
public boolean checkID() {
//假裝檢查通過
System.out.println("身份證檢查通過");
return true;
}
//InvocationHandler接口唯一的方法
//要實現(xiàn)方法增強水醋,就在這個方法里面加自己的邏輯
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//方法增強
checkID();
Object result = method.invoke(target, args);
return result;
}
}
????動態(tài)代理的實現(xiàn)套路是這樣:1.創(chuàng)建一個實現(xiàn)了InvocationHandler接口的類寂嘉;2.增加自己的邏輯,用于方法增強;3.重寫invoke方法涌韩,在method.invoke方法的前面(或者后面)增加自己的邏輯衷旅,實現(xiàn)方法增強在扰。
????測試代碼:
public static void main(String[] args) {
//創(chuàng)建一個剛才新建的類的對象
CheckIDHandler handler = new CheckIDHandler();
//真實類對象
TrainStation ts = new TrainStation();
//創(chuàng)建真實類對象的代理對象胚股,遙想當年,我們都
//是先把代理類寫好士八,然后一個個創(chuàng)建他們的對象
//的容燕;這里系統(tǒng)幫我們創(chuàng)建好代理對象并返回給我們
Ticket p1 = (Ticket) Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(),
ts.getClass().getInterfaces(), handler);
//拿到代理對象后,當然可以直接調(diào)用真實對象的方法了
p1.saleTicket();
}
????動態(tài)代理的實現(xiàn)套路是這樣:1.創(chuàng)建一個增強類的對象婚度;2.創(chuàng)建一個真實類的對象蘸秘;3.調(diào)用Proxy的靜態(tài)方法newProxyInstance,傳入當前線程的上下文類加載器蝗茁,也可以是真實對象的類加載器醋虏,還有真實對象的接口(就是那個公共接口),最后一個就是增強類對象哮翘;4.經(jīng)過 前面3步颈嚼,就拿到了代理對象,然后就可以調(diào)用真實對象里面的方法了饭寺。
????輸出結(jié)果如下:
身份證檢查通過
我是火車站阻课,1叫挟、2、3柑肴,賣票
????可以看到霞揉,我們沒有定義代理類旬薯,更沒有手動創(chuàng)建代理類的對象晰骑,就這樣,在賣票之前做到了檢查身份證的功能绊序,實現(xiàn)了方法增強硕舆。
????也許你會說,臥槽骤公,上面明明拿到了真實對象的引用抚官,在真實對象的前面調(diào)用checkID()方法,不就是實現(xiàn)了檢查身份證的功能嗎阶捆?好吧凌节,那么檢查身份證的方法放在哪個類里面?調(diào)用者的類里面洒试?如果別的地方也要用到檢查身份證的功能呢倍奢?再寫一遍?也許你會說放在工具類里面垒棋,但是不是所有的類都可以放在工具類里面當做靜態(tài)方法來調(diào)的卒煞。
????把檢查身份證的方法放在CheckIDHandler里面是有好處的,比如如果營業(yè)廳在辦理業(yè)務時叼架,要增加檢查身份證的步驟畔裕,怎么辦?
//下面是偽代碼
//檢查身份證的類
CheckIDHandler handler = new CheckIDHandler();
//營業(yè)廳的真實類
BusinessHall bh = new BusinessHall();
//Business是營業(yè)廳的接口乖订,b1是返回的代理類
Business b1 = (Ticket) Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(),
ts.getClass().getInterfaces(), handler);
//調(diào)用辦理業(yè)務的接口
b1.doBusiness();
????可以看到扮饶,通過引入CheckIDHandler就可以在辦理業(yè)務之前,強行把檢查身份證的動作強行插入進去乍构,這也就是所謂的面向切面編程(AOP)甜无,同時可以看到的是,檢查身份證的邏輯得到了極大的復用蜡吧,另外上例中的計時功能也能封裝到一個類毫蚓,然后在買票創(chuàng)景和營業(yè)廳辦理業(yè)務場景中復用。
????通過上面的對比可以看到昔善,靜態(tài)代理在要進行方法增強的時候元潘,需要創(chuàng)建新的類,而且方法增強的越多君仆,新增的類也就越多翩概,同時這些增強的功能還沒法復用牲距,別的模塊用不了,耦合太高钥庇,也造成了大量的冗余代碼牍鞠;而動態(tài)代理,每增強一次评姨,也會創(chuàng)建一個類难述,但是這個類只關(guān)注增強的邏輯,與別的類耦合度較低吐句,同時此類的增強方法還能用于別的模塊胁后。
????我個人的理解是動態(tài)代理就是把要增強的功能封裝到一個類里面,然后在需要他的地方強行插入進入嗦枢,一來不破壞原有的代碼攀芯;二來增強功能能夠得到復用。
????再盜一張圖:四:動態(tài)代理的原理
????從上面例子中可以看出文虏,動態(tài)代理的核心在于Proxy的newProxyInstance方法里面侣诺,我們?nèi)ヒ惶骄烤梗?/p>
......
//注釋略,太長了氧秘。第一個參數(shù)是真實對象的類加載器年鸳;第二個
//參數(shù)是真實對象實現(xiàn)的接口最后一個參數(shù)是我們實現(xiàn)的增強類
@CallerSensitive
public static Object newProxyInstance(ClassLoader loader,
Class<?>[] interfaces,
InvocationHandler h)
throws IllegalArgumentException {
//增強類不能非空(判空新思路)
Objects.requireNonNull(h);
//將真實對象實現(xiàn)的接口拷到新數(shù)組
final Class<?>[] intfs = interfaces.clone();
//安全檢查,不管
final SecurityManager sm = System.getSecurityManager();
if (sm != null) {
checkProxyAccess(Reflection.getCallerClass(), loader, intfs);
}
/*
* Look up or generate the designated proxy class.
*
* 查找或者生成指定的代理類敏储,動態(tài)生成哦
*/
Class<?> cl = getProxyClass0(loader, intfs);
/*
* Invoke its constructor with the designated invocation handler.
*
* 使用指定的調(diào)用處理程序調(diào)用其構(gòu)造函數(shù)阻星。
*/
try {
//安全檢查,pass
if (sm != null) {
checkNewProxyPermission(Reflection.getCallerClass(), cl);
}
//獲取代理類的構(gòu)造器
final Constructor<?> cons = cl.getConstructor(constructorParams);
final InvocationHandler ih = h;
//getModifiers是Class的方法已添,返回的是該類的修飾符妥箕,比如public
//,final更舞,static畦幢,abstract,interface等,拿到這個修飾符后
//缆蝉,調(diào)用Modifier的isPublic方法判斷該類是否是public類
if (!Modifier.isPublic(cl.getModifiers())) {
//如果該類不是public類宇葱,那么就要放開對這個類的訪問權(quán)限,不管
AccessController.doPrivileged(new PrivilegedAction<Void>() {
public Void run() {
cons.setAccessible(true);
return null;
}
});
}
//拿到Class對象后刊头,就可以創(chuàng)建相應的實例了
return cons.newInstance(new Object[]{h});
} catch (IllegalAccessException | InstantiationException e) {
throw new InternalError(e.toString(), e);
} catch (InvocationTargetException e) {
Throwable t = e.getCause();
if (t instanceof RuntimeException) {
throw (RuntimeException) t;
} else {
throw new InternalError(t.toString(), t);
}
} catch (NoSuchMethodException e) {
throw new InternalError(e.toString(), e);
}
}
......
????newProxyInstance的實現(xiàn)思路是:1.首先調(diào)用getProxyClass0查找或者生成指定的代理類黍瞧;2.檢查該類是否是public的,如果不是原杂,放開權(quán)限印颤;3.調(diào)用Class的newInstance方法創(chuàng)建實例。最重要的是getProxyClass0方法:
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
//調(diào)用proxyClassCache的get方法穿肄;proxyClassCache對象是WeakCache類型的年局,他
//持有一個弱引用隊列际看,每次查找就去隊列里面找,如果沒找到就ProxyClassFactory的apply
//方法去創(chuàng)建
return proxyClassCache.get(loader, interfaces);
}
????下面直接看ProxyClassFactory的apply方法:
//第一個參數(shù)是真實對象的類加載器矢否;第二個參數(shù)是真實對象實現(xiàn)的接口
@Override
public Class<?> apply(ClassLoader loader, Class<?>[] interfaces) {
//創(chuàng)建一個Map容器仲闽,用于裝載真實對象實現(xiàn)的接口
Map<Class<?>, Boolean> interfaceSet = new IdentityHashMap<>(interfaces.length);
//遍歷這些接口,創(chuàng)建這些接口的Class對象僵朗,主要用于校驗這些接口的合法性
for (Class<?> intf : interfaces) {
/*
* Verify that the class loader resolves the name of this
* interface to the same Class object.
*/
Class<?> interfaceClass = null;
try {
//使用指定的類加載器創(chuàng)建一個與給定的名字關(guān)聯(lián)的類或者接口的Class對象
interfaceClass = Class.forName(intf.getName(), false, loader);
} catch (ClassNotFoundException e) {
}
//如果創(chuàng)建失敗赖欣,說明類加載器不對
if (interfaceClass != intf) {
throw new IllegalArgumentException(
intf + " is not visible from class loader");
}
/*
* Verify that the Class object actually represents an
* interface.
*/
//如果創(chuàng)建出來的Class不是不是接口類型的,那么死給你看
if (!interfaceClass.isInterface()) {
throw new IllegalArgumentException(
interfaceClass.getName() + " is not an interface");
}
/*
* Verify that this interface is not a duplicate.
*/
//如果interfaceClass已經(jīng)存在于容器中衣迷,說明重復創(chuàng)建了畏鼓,那么也死給你看
if (interfaceSet.put(interfaceClass, Boolean.TRUE) != null) {
throw new IllegalArgumentException(
"repeated interface: " + interfaceClass.getName());
}
}
//代理類的包名
String proxyPkg = null; // package to define proxy class in
//將該類設置成public final類型的類酱酬,網(wǎng)上有方法可以拿到這個代理類壶谒,從
//他反編譯的結(jié)果來看,確實是public final類型的類膳沽,有興趣的自己研究下
int accessFlags = Modifier.PUBLIC | Modifier.FINAL;
/*
* Record the package of a non-public proxy interface so that the
* proxy class will be defined in the same package. Verify that
* all non-public proxy interfaces are in the same package.
*/
//又遍歷汗菜,計算生成的代理類的包名
for (Class<?> intf : interfaces) {
//獲取該接口的Class對象的訪問修飾符
int flags = intf.getModifiers();
//如果不是public類型的
if (!Modifier.isPublic(flags)) {
//那么去掉上面的設置成public final類型的操作
accessFlags = Modifier.FINAL;
//返回該接口的名字(包括包名)
String name = intf.getName();
//獲取全限定名的最后的.號
int n = name.lastIndexOf('.');
//生成的代理類的包名,包名就是接口的全限定名 - 接口類的名字挑社,類似于"java.lang."
String pkg = ((n == -1) ? "" : name.substring(0, n + 1));
//將生成的包名賦值給代理類的包名
if (proxyPkg == null) {
proxyPkg = pkg;
} else if (!pkg.equals(proxyPkg)) {
throw new IllegalArgumentException(
"non-public interfaces from different packages");
}
}
}
//如果木有非公共代理接口陨界,那么使用默認的包名com.sun.proxy
if (proxyPkg == null) {
// if no non-public proxy interfaces, use com.sun.proxy package
proxyPkg = ReflectUtil.PROXY_PACKAGE + ".";
}
/*
* Choose a name for the proxy class to generate.
*/
//生成一個數(shù)字,過程不管
long num = nextUniqueNumber.getAndIncrement();
//拼接代理類的全限定名
String proxyName = proxyPkg + proxyClassNamePrefix + num;
/*
* Generate the specified proxy class.
*/
//生成代理類的byte數(shù)組痛阻,沒看到源碼菌瘪,不管了
byte[] proxyClassFile = ProxyGenerator.generateProxyClass(
proxyName, interfaces, accessFlags);
try {
//將byte數(shù)組轉(zhuǎn)換成Class對象并返回
return defineClass0(loader, proxyName,
proxyClassFile, 0, proxyClassFile.length);
} catch (ClassFormatError e) {
/*
* A ClassFormatError here means that (barring bugs in the
* proxy class generation code) there was some other
* invalid aspect of the arguments supplied to the proxy
* class creation (such as virtual machine limitations
* exceeded).
*/
throw new IllegalArgumentException(e.toString());
}
}
???? apply方法比較長,但是邏輯并不復雜阱当,不過出現(xiàn)了一些我們很少用到的接口俏扩,查看API就可以了,不過最后還是沒看到生成byte數(shù)組的generateProxyClass方法弊添,從網(wǎng)上摘抄了一部分資料:
public static byte[] generateProxyClass(final String name,
Class[] interfaces)
{
ProxyGenerator gen = new ProxyGenerator(name, interfaces);
// 這里動態(tài)生成代理類的字節(jié)碼
final byte[] classFile = gen.generateClassFile();
// 如果saveGeneratedFiles的值為true录淡,則會把所生成的代理類的字節(jié)碼保存到硬盤上
if (saveGeneratedFiles) {
java.security.AccessController.doPrivileged(
new java.security.PrivilegedAction<Void>() {
public Void run() {
try {
FileOutputStream file =
new FileOutputStream(dotToSlash(name) + ".class");
file.write(classFile);
file.close();
return null;
} catch (IOException e) {
throw new InternalError(
"I/O exception saving generated file: " + e);
}
}
});
}
// 返回代理類的字節(jié)碼
return classFile;
}
private byte[] generateClassFile() {
//第一步, 將所有的方法組裝成ProxyMethod對象
3 //首先為代理類生成toString, hashCode, equals等代理方法
4 addProxyMethod(hashCodeMethod, Object.class);
5 addProxyMethod(equalsMethod, Object.class);
6 addProxyMethod(toStringMethod, Object.class);
7 //遍歷每一個接口的每一個方法, 并且為其生成ProxyMethod對象
8 for (int i = 0; i < interfaces.length; i++) {
9 Method[] methods = interfaces[i].getMethods();
10 for (int j = 0; j < methods.length; j++) {
11 addProxyMethod(methods[j], interfaces[i]);
12 }
13 }
14 //對于具有相同簽名的代理方法, 檢驗方法的返回值是否兼容
15 for (List<ProxyMethod> sigmethods : proxyMethods.values()) {
16 checkReturnTypes(sigmethods);
17 }
18
19 //第二步, 組裝要生成的class文件的所有的字段信息和方法信息
20 try {
21 //添加構(gòu)造器方法
22 methods.add(generateConstructor());
23 //遍歷緩存中的代理方法
24 for (List<ProxyMethod> sigmethods : proxyMethods.values()) {
25 for (ProxyMethod pm : sigmethods) {
26 //添加代理類的靜態(tài)字段, 例如:private static Method m1;
27 fields.add(new FieldInfo(pm.methodFieldName,
28 "Ljava/lang/reflect/Method;", ACC_PRIVATE | ACC_STATIC));
29 //添加代理類的代理方法
30 methods.add(pm.generateMethod());
31 }
32 }
33 //添加代理類的靜態(tài)字段初始化方法
34 methods.add(generateStaticInitializer());
35 } catch (IOException e) {
36 throw new InternalError("unexpected I/O Exception");
37 }
38
39 //驗證方法和字段集合不能大于65535
40 if (methods.size() > 65535) {
41 throw new IllegalArgumentException("method limit exceeded");
42 }
43 if (fields.size() > 65535) {
44 throw new IllegalArgumentException("field limit exceeded");
45 }
46
47 //第三步, 寫入最終的class文件
48 //驗證常量池中存在代理類的全限定名
49 cp.getClass(dotToSlash(className));
50 //驗證常量池中存在代理類父類的全限定名, 父類名為:"java/lang/reflect/Proxy"
51 cp.getClass(superclassName);
52 //驗證常量池存在代理類接口的全限定名
53 for (int i = 0; i < interfaces.length; i++) {
54 cp.getClass(dotToSlash(interfaces[i].getName()));
55 }
56 //接下來要開始寫入文件了,設置常量池只讀
57 cp.setReadOnly();
58
59 ByteArrayOutputStream bout = new ByteArrayOutputStream();
60 DataOutputStream dout = new DataOutputStream(bout);
61 try {
62 //1.寫入魔數(shù)
63 dout.writeInt(0xCAFEBABE);
64 //2.寫入次版本號
65 dout.writeShort(CLASSFILE_MINOR_VERSION);
66 //3.寫入主版本號
67 dout.writeShort(CLASSFILE_MAJOR_VERSION);
68 //4.寫入常量池
69 cp.write(dout);
70 //5.寫入訪問修飾符
71 dout.writeShort(ACC_PUBLIC | ACC_FINAL | ACC_SUPER);
72 //6.寫入類索引
73 dout.writeShort(cp.getClass(dotToSlash(className)));
74 //7.寫入父類索引, 生成的代理類都繼承自Proxy
75 dout.writeShort(cp.getClass(superclassName));
76 //8.寫入接口計數(shù)值
77 dout.writeShort(interfaces.length);
78 //9.寫入接口集合
79 for (int i = 0; i < interfaces.length; i++) {
80 dout.writeShort(cp.getClass(dotToSlash(interfaces[i].getName())));
81 }
82 //10.寫入字段計數(shù)值
83 dout.writeShort(fields.size());
84 //11.寫入字段集合
85 for (FieldInfo f : fields) {
86 f.write(dout);
87 }
88 //12.寫入方法計數(shù)值
89 dout.writeShort(methods.size());
90 //13.寫入方法集合
91 for (MethodInfo m : methods) {
92 m.write(dout);
93 }
94 //14.寫入屬性計數(shù)值, 代理類class文件沒有屬性所以為0
95 dout.writeShort(0);
96 } catch (IOException e) {
97 throw new InternalError("unexpected I/O Exception");
98 }
99 //轉(zhuǎn)換成二進制數(shù)組輸出
100 return bout.toByteArray();
101 }
????至此,我們基本上了解了代理對象是怎么生成的油坝。
????還有個問題是嫉戚,那個invoke是什么時候調(diào)用的呢?要想解開這個謎題澈圈,只能查看動態(tài)生成的代理對象的源碼彬檀,同樣從網(wǎng)上摘抄了一個代理對象的源碼:
{
private static Method m1;
private static Method m2;
private static Method m3;
private static Method m0;
/**
*注意這里是生成代理類的構(gòu)造方法,方法參數(shù)為InvocationHandler類型瞬女,看到這窍帝,是不是就有點明白
*為何代理對象調(diào)用方法都是執(zhí)行InvocationHandler中的invoke方法,而InvocationHandler又持有一個
*被代理對象的實例拆魏,不禁會想難道是....盯桦? 沒錯慈俯,就是你想的那樣。
*
*super(paramInvocationHandler)拥峦,是調(diào)用父類Proxy的構(gòu)造方法贴膘。
*父類持有:protected InvocationHandler h;
*Proxy構(gòu)造方法:
* protected Proxy(InvocationHandler h) {
* Objects.requireNonNull(h);
* this.h = h;
* }
*
*/
public $Proxy0(InvocationHandler paramInvocationHandler)
throws
{
super(paramInvocationHandler);
}
//這個靜態(tài)塊本來是在最后的,我把它拿到前面來略号,方便描述
static
{
try
{
//看看這兒靜態(tài)塊兒里面有什么刑峡,是不是找到了giveMoney方法。請記住giveMoney通過反射得到的名字m3玄柠,其他的先不管
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("proxy.Person").getMethod("giveMoney", new Class[0]);
m0 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]);
return;
}
catch (NoSuchMethodException localNoSuchMethodException)
{
throw new NoSuchMethodError(localNoSuchMethodException.getMessage());
}
catch (ClassNotFoundException localClassNotFoundException)
{
throw new NoClassDefFoundError(localClassNotFoundException.getMessage());
}
}
/**
*
*這里調(diào)用代理對象的giveMoney方法突梦,直接就調(diào)用了InvocationHandler中的invoke方法,并把m3傳了進去羽利。
*this.h.invoke(this, m3, null);這里簡單宫患,明了。
*來这弧,再想想娃闲,代理對象持有一個InvocationHandler對象,InvocationHandler對象持有一個被代理的對象匾浪,
*再聯(lián)系到InvacationHandler中的invoke方法皇帮。嗯,就是這樣蛋辈。
*/
public final void giveMoney()
throws
{
try
{
this.h.invoke(this, m3, null);
return;
}
catch (Error|RuntimeException localError)
{
throw localError;
}
catch (Throwable localThrowable)
{
throw new UndeclaredThrowableException(localThrowable);
}
}
//注意属拾,這里為了節(jié)省篇幅,省去了toString冷溶,hashCode渐白、equals方法的內(nèi)容。原理和 giveMoney方法一毛一樣挂洛。
}
????上面代碼的giveMoney就相當于上面例子中的checkID()礼预。可以看到虏劲,當我們調(diào)用代理對象的checkID()方法時托酸,代理對象內(nèi)部調(diào)用了增強類的invoke方法,正式在這個invoke方法里面柒巫,我們才能實現(xiàn)方法的增強励堡,當然在調(diào)用invoke的時候,我們還把最終要調(diào)用的買票的方法saleTicket傳進去了堡掏,這個方法最終通過反射的機制得到調(diào)用应结。