InvocationHandler中invoke()⽅法的调⽤问题
Java中动态代理的实现,关键就是这两个东西:Proxy、InvocationHandler,下⾯从InvocationHandler接⼝中的invoke⽅法⼊⼿,简单说明⼀下Java如何实现动态代理的。
⾸先,invoke⽅法的完整形式如下:
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable
{
method.invoke(obj, args);
return null;
}
⾸先猜测⼀下,method是调⽤的⽅法,即需要执⾏的⽅法;args是⽅法的参数;proxy,这个参数是什么?以上invoke()⽅法的实现即是⽐较标准的形式,我们看到,这⾥并没有
⽤到proxy参数。查看JDK⽂档中Proxy的说明,如下:
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 flect.Method object identifying the method that wa 由此可以知道以上的猜测是正确的,同时也知道,proxy参数传递的即是代理类的实例。
为了⽅便说明,这⾥写⼀个简单的例⼦来实现动态代理。
//抽象⾓⾊(动态代理只能代理接⼝)
public interface Subject {
public void request();
}
//真实⾓⾊:实现了Subject的request()⽅法
public class RealSubject implements Subject{
public void request(){
System.out.println("From real subject.");
}
}
//实现了InvocationHandler
public class DynamicSubject implements InvocationHandler
{
private Object obj;//这是动态代理的好处,被封装的对象是Object类型,接受任意类型的对象
public DynamicSubject()
{
}
public DynamicSubject(Object obj)
{
this.obj = obj;
}
//这个⽅法不是我们显⽰的去调⽤
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable
{
System.out.println("before calling " + method);
method.invoke(obj, args);
System.out.println("after calling " + method);
return null;
}
}
//客户端:⽣成代理实例,并调⽤了request()⽅法
public class Client {
public static void main(String[] args) throws Throwable{
// TODO Auto-generated method stub
Subject rs=new RealSubject();//这⾥指定被代理类
InvocationHandler ds=new DynamicSubject(rs);
Class<?> Class();
//以下是⼀次性⽣成代理
Subject subject=(Subject) wProxyInstance(
//这⾥可以通过运⾏结果证明subject是Proxy的⼀个实例,这个实例实现了Subject接⼝
System.out.println(subject instanceof Proxy);
//这⾥可以看出subject的Class类是$Proxy0,这个$Proxy0类继承了Proxy,实现了Subject接⼝
System.out.println("subject的Class类是:"+Class().toString());
System.out.print("subject中的属性有:");
Field[] Class().getDeclaredFields();
for(Field f:field){
System.out.Name()+", ");
}
System.out.print("\n"+"subject中的⽅法有:");
Method[] Class().getDeclaredMethods();
for(Method m:method){
System.out.Name()+", ");
}
System.out.println("\n"+"subject的⽗类是:"+Class().getSuperclass());
System.out.print("\n"+"subject实现的接⼝是:");
Class<?>[] Class().getInterfaces();
for(Class<?> i:interfaces){
System.out.Name()+", ");
}
System.out.println("\n\n"+"运⾏结果为:");
}
}
运⾏结果如下:此处省略了包名,***代替
true
subject的Class类是:class $Proxy0
subject中的属性有:m1, m3, m0, m2,
subject中的⽅法有:request, hashCode, equals, toString,
subject的⽗类是:class flect.Proxy
subject实现的接⼝是:cn.edu.ustc.dynamicproxy.Subject,
运⾏结果为:
before calling public abstract void ***.quest()
From real subject.
after calling public abstract void ***.quest()
PS:这个结果的信息⾮常重要,⾄少对我来说。因为我在动态代理犯晕的根源就在于将上⾯的quest()理解错了,⾄少是被表⾯所迷惑,没有发现这个subject和Proxy之间的联系,⼀度纠结于最后调⽤的这个request()是怎么和invoke()联系上的,⽽invoke⼜是怎么知道request存在的。其实上⾯的true和class $Proxy0就能解决很多的疑问,再加上下⾯将要说的$Proxy0的源码,完全可以解决动态代理的疑惑了。
从以上代码和结果可以看出,我们并没有显⽰的调⽤invoke()⽅法,但是这个⽅法确实执⾏了。下⾯就整个的过程进⾏分析⼀下:
从Client中的代码看,可以从newProxyInstance这个⽅法作为突破⼝,我们先来看⼀下Proxy类中newProxyInstance⽅法的源代码:
public static Object newProxyInstance(ClassLoader loader,
Class<?>[] interfaces,
InvocationHandler h)
throws IllegalArgumentException
{
if (h == null) {
throw new NullPointerException();
}
/*
* Look up or generate the designated proxy class.
*/
Class cl = getProxyClass(loader, interfaces);
/*
* Invoke its constructor with the designated invocation handler.
*/
try {
/*
* Proxy源码开始有这样的定义:
* private final static Class[] constructorParams = { InvocationHandler.class };
* cons即是形参为InvocationHandler类型的构造⽅法
*/
Constructor cons = cl.getConstructor(constructorParams);
return (Object) wInstance(new Object[] { h });
} catch (NoSuchMethodException e) {
throw new String());
} catch (IllegalAccessException e) {
throw new String());
} catch (InstantiationException e) {
throw new String());
} catch (InvocationTargetException e) {
throw new String());
}
}
(1)根据参数loader和interfaces调⽤⽅法 getProxyClass(loader, interfaces)创建代理类$Proxy0.$Proxy0类实现了interfaces的接⼝,并继承了Proxy类.
(2)实例化$Proxy0并在构造⽅法中把DynamicSubject传过去,接着$Proxy0调⽤⽗类Proxy的构造器,为h赋值,如下:
class Proxy{
InvocationHandler h=null;
protected Proxy(InvocationHandler h) {
this.h = h;
}
...
}
来看⼀下这个继承了Proxy的$Proxy0的源代码:
public final class $Proxy0 extends Proxy implements Subject {
private static Method m1;
private static Method m0;
private static Method m3;
private static Method m2;
static {
try {
m1 = Class.forName("java.lang.Object").getMethod("equals",
new Class[] { Class.forName("java.lang.Object") });
m0 = Class.forName("java.lang.Object").getMethod("hashCode",
new Class[0]);
m3 = Class.forName("***.RealSubject").getMethod("request",
new Class[0]);
m2 = Class.forName("java.lang.Object").getMethod("toString",
new Class[0]);
} catch (NoSuchMethodException nosuchmethodexception) {
throw new Message());
} catch (ClassNotFoundException classnotfoundexception) {
throw new Message());
}
} //static
public $Proxy0(InvocationHandler invocationhandler) {
super(invocationhandler);
}
@Override
public final boolean equals(Object obj) {
try {
return ((Boolean) super.h.invoke(this, m1, new Object[] { obj })) .booleanValue();
java的tostring方法} catch (Throwable throwable) {
throw new UndeclaredThrowableException(throwable);
}
}
@Override
public final int hashCode() {
try {
return ((Integer) super.h.invoke(this, m0, null)).intValue();
} catch (Throwable throwable) {
throw new UndeclaredThrowableException(throwable);
}
}
public final void request() {
try {
super.h.invoke(this, m3, null);
return;
} catch (Error e) {
} catch (Throwable throwable) {
throw new UndeclaredThrowableException(throwable);
}
}
@Override
public final String toString() {
try {
return (String) super.h.invoke(this, m2, null);
} catch (Throwable throwable) {
throw new UndeclaredThrowableException(throwable);
}
}
}
接着把得到的$Proxy0实例强制转换成Subject,并将引⽤赋给subject。当执⾏quest()⽅法时,就调⽤了$Proxy0类中的request()⽅法,进⽽调⽤⽗类Proxy中的h的invoke()⽅法.即InvocationHandler.invoke()。
PS:1、需要说明的⼀点是,Proxy类中getProxyClass⽅法返回的是Proxy的Class类。之所以说明,是因为我⼀开始犯了个低级错误,以为返回的是“被代理类的Class类”- -!推荐看⼀下getProxyClass的源码,很长=。=
2、从$Proxy0的源码可以看出,动态代理类不仅代理了显⽰定义的接⼝中的⽅法,⽽且还代理了java的根类Object中的继承⽽来的equals()、hashcode()、toString()这三个⽅法,并且仅此三个⽅法。
Q:到现在为⽌,还有⼀个疑问,invoke⽅法中的第⼀个参数是Proxy的实例(准确说,最终⽤到的是$Proxy0的实例),但是有什么⽤呢?或者说,程序内是怎样显⽰出作⽤的?
A:就本⼈⽬前的⽔平看来,这个proxy参数并没有什么作⽤,在整个动态代理机制中,并没有⽤到InvocationHandler中invoke⽅法的proxy参数。⽽传⼊的这个参数实际是代理类的⼀个实例。我想可能是为了让程序员在invoke⽅法中使⽤反射来获取关于代理类的⼀些信息吧。

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。