黑马程序员技术交流社区
标题:
java的代理问题一
[打印本页]
作者:
lonely_zb
时间:
2013-9-7 17:34
标题:
java的代理问题一
先贴上代码:
package com.itcast.day3;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.ArrayList;
import java.util.Collection;
public class ProxyTest2 {
public static void main(String[] args) {
Collection proxy= (Collection) Proxy.newProxyInstance(Collection.class.getClassLoader(), new Class[]{Collection.class}, new InvocationHandler() {
Collection target = new ArrayList();
@Override
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
long startTime = System.currentTimeMillis();
Object retVal = method.invoke(target, args);
long endTime = System.currentTimeMillis();
System.out.println(endTime-startTime);
return retVal;
}
});
proxy.add("333");
proxy.add("555");
proxy.add("777");
System.out.println(proxy.size());
//?既然代理对象调用方法都回去执行InvocationHandler的invoke方法,为什么proxy.getClass()却没有去执行呢
//hashcode().equals(),和toString()这3个方法,其它的继承自Object的方法是不会去调用invoke的
System.out.println(proxy.getClass().getName());
}
}
问题我在代码中已经描述了,可以我有疑问的是既然这样的话,只有hashcode().equals(),和toString()3个方法回去执行invode的过程,那么为什么我之前的add()方法都有去执行invode方法啊?
作者:
张聪珉
时间:
2013-9-7 17:59
其实你都已经说得很明白了,你查看一下API文档Object类,只有那三个方法委托给invocationHandeler去执行,add方法是目标类自己的方法Object并没有这个方法,又或者说如果目标类复写了Object的某些方法,那么调用这些复写的方法也是会交给invocationHandeler去执行的,这是本人粗劣的理解
作者:
张歆明
时间:
2013-9-7 18:31
*******
1. 先解答你的add方法为什么执行的是invoke方法 --- 这个靠理解就行
{1}. 定义上理解:代理的目的在执行目标方法不变的前提下,加上交叉业务的代码。
{2}. 在Java的动态代理中,代理要执行的目标一定是在newProxyInstance(ClassLoader, Class<?>[], InvocationHandler)中的第二个接口字节码对象数组中指定你要代理的目标。注意,在Class<?>[]指定的需要代理的目标都是接口,那么代理的方法都是可以由接口的实现子类来实现的。因此这个参数中涉及到的所有接口综合起来的所有的方法都要去执行接口的实现类对象(也就是目标类对象)的方法,也就是invoke方法。
你这里面吧目标类对象是ArrayList对象,ArrayLIst和你的动态代理类都要接口是Collection。所以生成的代理类对象要执行Collection的add方法的时候,由于代理的是Collection接口的实现子类ArrayList的对象,所以就是执行target.add(xxxx)方法。但是这样写是传统的形式调用对象的方法,如果换成其他的方法,就没有办法通用了。所以invoke方法中使用反射的形式method.invoke(target, args)的方式调用target.add(xxx)方法。
2. 这点我自己的理解
{1}. Object不是你在定义动态代理类在Class<?>[]中指定的接口的字节码,同时Oblect是所有类的父类,也不是接口。所以也不能传入这个参数!!
{2}. Object类是Java中所有类的直接或者间接的父类,所以只要你是在Java中使用的类,那么你的直接或者间接父类就一定是Object类。那么动态代理类也不例外。
{3}. 动态代理类的直接父类是java.lang.reflect.Proxy类,这个类的直接父类是Object 那么由于继承关系,动态代理类就必然会继承到Object的所有方法。 其中hashcode().equals(),和toString() 被继承到动态代理类,可以指定并重写。因为这三个方法分别表示哈希吗(一种和内存地址的对应关系)、对象比较的条件以System.out.println(动态代理类对象)的时候,应该给出什么样的信息来体现出这个是动态代理类。比如 System.out.println(new Object());执行之后 打印结果就是 java.lang.Object@xxxx 你也可以按照自己的意愿来打印出这个代理对应的字符串,所以这三个方法是可以被重写的。所以 你就在执行 动态代理类实例.toString(), 动态代理类实例.hashCode(), 动态代理类.equals() 执行目标类的相应的方法就是合理的
【反正I】
但是 对于getClass()的这个方法,由于返回的是Class对象 。每个类一一对应一个Class对象!!!如果你这个时候让动态代理类的getClass()去调用invoke方法,那岂不是返回的是目标的Class对象来作为这个代理类的字节码了吗?
也就是:假设这里面System.out.println(proxy.getClass()) 在执行proxy.getClass()走的是invoke方法 那么 invoke中调用的不就是
method.invoke(target, null);[此时这个method方法已经是在JVM内部被getClass关联了 ,也就是JVM内部执行了Method method =clazz.getMethod("getClass");] 这样 相当于 target.getClass() ------> 最后打印出来的不就是 java.util.ArrayList了么?
但是 动态代理类的直接父类一定是java.lang.reflect.Proxy类 ,但是按照假设“proxy.getClass()走的是invoke方法 ” 返回来的动态代理类的Class不就成了java.util.ArrayList了么? ArrayList这个字节码和Proxy的字节码没有任何关系,这不就矛盾了么?
[反正II]
动态代理类的类名是: $Proxy数字 , Class对象的toString方法(println打印对象的时候 调用对象的toString方法)返回的是包名.类名 但是 如果按照假设 说明动态代理类的类名就是java.util.ArrayList了,也不正确。因为动态代理类的字节码仅存在于内存,没有包。所以java.util没法匹配上。就算动态代理类有包名,它的类名也不叫ArrayList,叫$Proxy数字 这个名字
这么看也是矛盾的
所以 Java就规定了 动态代理类从Object的方法中,只有那三个方法可以总InvocationHandler中的invoke方法,像getClass这样的不方法是不可以的!!
欢迎光临 黑马程序员技术交流社区 (http://bbs.itheima.com/)
黑马程序员IT技术论坛 X3.2