java动态代理中的invoke方法是如何被自动调用的
相關(guān)文章:靜態(tài)代理和動態(tài)代理的區(qū)別和聯(lián)系
一、動態(tài)代理與靜態(tài)代理的區(qū)別。
(1)Proxy類的代碼被固定下來,不會因為業(yè)務(wù)的逐漸龐大而龐大;
(2)可以實現(xiàn)AOP編程,這是靜態(tài)代理無法實現(xiàn)的;
(3)解耦,如果用在web業(yè)務(wù)下,可以實現(xiàn)數(shù)據(jù)層和業(yè)務(wù)層的分離。
(4)動態(tài)代理的優(yōu)勢就是實現(xiàn)無侵入式的代碼擴展。
靜態(tài)代理這個模式本身有個大問題,如果類方法數(shù)量越來越多的時候,代理類的代碼量是十分龐大的。所以引入動態(tài)代理來解決此類問題
二、動態(tài)代理
Java中動態(tài)代理的實現(xiàn),關(guān)鍵就是這兩個東西:Proxy、InvocationHandler,下面從InvocationHandler接口中的invoke方法入手,簡單說明一下Java如何實現(xiàn)動態(tài)代理的。
首先,invoke方法的完整形式如下:
Java代碼
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable
{
Java代碼
A method invocation on a proxy instance through one of its proxy interfaces will be dispatched to the invoke method of the instance’s invocation handler, passing the proxy instance,a java.lang.reflect.Method object identifying the method that was invoked, and an array of type Object containing the arguments.
Java代碼
//抽象角色(動態(tài)代理只能代理接口)
public interface Subject {
}
Java代碼
//真實角色:實現(xiàn)了Subject的request()方法
public class RealSubject implements Subject{
}
Java代碼
//實現(xiàn)了InvocationHandler
public class DynamicSubject implements InvocationHandler
{
private Object obj;//這是動態(tài)代理的好處,被封裝的對象是Object類型,接受任意類型的對象
}
Java代碼
//客戶端:生成代理實例,并調(diào)用了request()方法
public class Client {
}
Xml代碼
運行結(jié)果如下:此處省略了包名,***代替
true
subject的Class類是:class $Proxy0
subject中的屬性有:m1, m3, m0, m2,
subject中的方法有:request, hashCode, equals, toString,
subject的父類是:class java.lang.reflect.Proxy
subject實現(xiàn)的接口是:cn.edu.ustc.dynamicproxy.Subject,
運行結(jié)果為:
before calling public abstract void ***.Subject.request()
From real subject.
after calling public abstract void ***.Subject.request()
PS:這個結(jié)果的信息非常重要,至少對我來說。因為我在動態(tài)代理犯暈的根源就在于將上面的subject.request()理解錯了,至少是被表面所迷惑,沒有發(fā)現(xiàn)這個subject和Proxy之間的聯(lián)系,一度糾結(jié)于最后調(diào)用的這個request()是怎么和invoke()聯(lián)系上的,而invoke又是怎么知道request存在的。其實上面的true和class Proxy0就能解決很多的疑問,再加上下面將要說的Proxy0就能解決很多的疑問,再加上下面將要說的Proxy0就能解決很多的疑問,再加上下面將要說的Proxy0的源碼,完全可以解決動態(tài)代理的疑惑了。
從以上代碼和結(jié)果可以看出,我們并沒有顯示的調(diào)用invoke()方法,但是這個方法確實執(zhí)行了。下面就整個的過程進行分析一下: 從Client中的代碼看,可以從newProxyInstance這個方法作為突破口,我們先來看一下Proxy類中newProxyInstance方法的源代碼:Java代碼
public static Object newProxyInstance(ClassLoader loader,
Class<?>[] interfaces,
InvocationHandler h)
throws IllegalArgumentException
{
if (h == null) {
throw new NullPointerException();
}
}
Proxy.newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)做了以下幾件事. (1)根據(jù)參數(shù)loader和interfaces調(diào)用方法 getProxyClass(loader, interfaces)創(chuàng)建代理類$Proxy0.$Proxy0類 實現(xiàn)了interfaces的接口,并繼承了Proxy類. (2)實例化$Proxy0并在構(gòu)造方法中把DynamicSubject傳過去,接著$Proxy0調(diào)用父類Proxy的構(gòu)造器,為h賦值,如下:Java代碼
class Proxy{
InvocationHandler h=null;
protected Proxy(InvocationHandler h) {
this.h = h;
}
…
}
Java代碼
public final class $Proxy0 extends Proxy implements Subject {
private static Method m1;
private static Method m0;
private static Method m3;
private static Method m2;
}
接著把得到的$Proxy0實例強制轉(zhuǎn)換成Subject,并將引用賦給subject。當執(zhí)行subject.request()方法時,就調(diào)用了$Proxy0類中的request()方法,進而調(diào)用父類Proxy中的h的invoke()方法.即InvocationHandler.invoke()。PS:1、需要說明的一點是,Proxy類中g(shù)etProxyClass方法返回的是Proxy的Class類。之所以說明,是因為我一開始犯了個低級錯誤,以為返回的是“被代理類的Class類”- -!推薦看一下getProxyClass的源碼,很長=。=
2、從$Proxy0的源碼可以看出,動態(tài)代理類不僅代理了顯示定義的接口中的方法,而且還代理了java的根類Object中的繼承而來的equals()、hashcode()、toString()這三個方法,并且僅此三個方法。
Q:到現(xiàn)在為止,還有一個疑問,invoke方法中的第一個參數(shù)是Proxy的實例(準確說,最終用到的是$Proxy0的實例),但是有什么用呢?或者說,程序內(nèi)是怎樣顯示出作用的?
A:就本人目前的水平看來,這個proxy參數(shù)并沒有什么作用,在整個動態(tài)代理機制中,并沒有用到InvocationHandler中invoke方法的proxy參數(shù)。而傳入的這個參數(shù)實際是代理類的一個實例。我想可能是為了讓程序員在invoke方法中使用反射來獲取關(guān)于代理類的一些信息吧。
原文:https://blog.csdn.net/zcc_0015/article/details/22695647
版權(quán)聲明:本文為博主原創(chuàng)文章,轉(zhuǎn)載請附上博文鏈接!
總結(jié)
以上是生活随笔為你收集整理的java动态代理中的invoke方法是如何被自动调用的的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 百变工程之消防车
- 下一篇: selenium原理和尝试