A股上市公司传智教育(股票代码 003032)旗下技术交流社区北京昌平校区

 找回密码
 加入黑马

QQ登录

只需一步,快速开始

© 谢洋 高级黑马   /  2013-3-3 09:24  /  1196 人查看  /  4 人回复  /   0 人收藏 转载请遵从CC协议 禁止商业使用本文

1、我发现代理类只实现了目标类的接口上的基本功能,而没包括目标类的所有功能?是实现不了?还是有别的原因?想不通啊
2、如果我要让代理类具有目标类所有的功能,应该怎么办?

评分

参与人数 1技术分 +1 收起 理由
陈丽莉 + 1

查看全部评分

4 个回复

倒序浏览
本帖最后由 唐长智 于 2013-3-3 10:51 编辑

楼主的这个问题也困扰我很久了,于是早上我就找源代码开读。在Proxy的源代码里关于newProxyInstance是这么写的
  1. public static Object newProxyInstance(ClassLoader loader,
  2.                                           Class<?>[] interfaces,
  3.                                           InvocationHandler h)
  4.         throws IllegalArgumentException
  5.     {
  6.                 if (h == null) {
  7.                         throw new NullPointerException();
  8.                 }

  9.                 Class cl = getProxyClass(loader, interfaces);
  10.                 //通过getProxyClass方法得到Class对象

  11.                 try {
  12.                         Constructor cons = cl.getConstructor(constructorParams);
  13.                         //用Class对象的构造方法来创建实例对象,也就是代理实例
  14.                         return (Object) cons.newInstance(new Object[] { h });
  15.                 } catch (NoSuchMethodException e) {
  16.                         throw new InternalError(e.toString());
  17.                 } catch (IllegalAccessException e) {
  18.                         throw new InternalError(e.toString());
  19.                 } catch (InstantiationException e) {
  20.                         throw new InternalError(e.toString());
  21.                 } catch (InvocationTargetException e) {
  22.                         throw new InternalError(e.toString());
  23.                 }
  24.     }
复制代码
关键就在getProxyClass这个方法上,看他是怎么得到Class对象的。这个方法的源码实在太长,太复杂我不能完全看懂,就只能把关于ProxyClass相关的源码粘过来
  1. byte[] proxyClassFile = ProxyGenerator.generateProxyClass( proxyName, interfaces);
  2. //是根据接口的Class对象来生成的
  3. try {
  4.     // 动态地定义新生成的代理类
  5.     proxyClass = defineClass0(loader, proxyName, proxyClassFile, 0,
  6.         proxyClassFile.length);
  7. } catch (ClassFormatError e) {
  8.     throw new IllegalArgumentException(e.toString());
  9. }
复制代码
通过这两段源码你就能看出来在newProxyInstance()方法的设计中,就是用传入的接口的Class对象得到构造函数来生成代理实例的,所以也就只能具备接口的功能。

评分

参与人数 1技术分 +1 收起 理由
陈丽莉 + 1

查看全部评分

回复 使用道具 举报
谢谢楼上哥们
1、从
       Class cl = getProxyClass(loader, interfaces);
       Constructor cons = cl.getConstructor(constructorParams);

       //用Class对象的构造方法来创建实例对象,也就是代理实例
        return (Object) cons.newInstance(new Object[] { h });
可以看出你说的:就是用传入的接口的Class对象得到构造函数来生成代理实例的,所以也就只能具备接口的功能,有根有据;
2、但是我还有一点不明白:因为接口没有方法体,表明代理的方法体是从目标类身上获取的,也就说明loader可以搞到目标类的字节码;
   a、从这一点来看,即然可以拿到目标类的字节码,让代理类去拥有目标类的所有方法是可以行得通的,那么为什么代理类不去实现
       目标类的所有功能,再添加一些自已的功能?
   b、还是因为构造函数是接口的,就只能实现接口的中的方法?个人解理:因为对象的由构造函数构造出来,
        而构造函数里面的信息(记录着产生一个具有什么样功的的对象的信息)是固定的,所以说有什么样的构造函数,就有什么样的对象?
       这样理解合理不?
   c、还有,如果上面解释得通,那么只要搞到目标类的所有构造函数,那不就可以构造出拥有目标类所有功能的代理类了吗?
   
另外问一个简单的问题,我不知怎么找源码来看,谢谢!


回复 使用道具 举报
本帖最后由 唐长智 于 2013-3-3 14:36 编辑

好吧,先从简单的说起。
1.源码就在你的JDK里,是一个压缩文件。比如我的源码路径就是jdk1.6.0_35\src.rar
你双击进去就能看到像API里面一样的文件夹分布,直接打开看就行了。
2.在源码中
import sun.misc.ProxyGenerator;
byte[] proxyClassFile = ProxyGenerator.generateProxyClass( proxyName, interfaces)
因为这是sun公司的包,这个方法到底干了什么没办法知道。也就没办法知道字节码到底是怎么生成的。
3.为什么代理类只能实现接口的方法呢?
那是因为java本身就不支持多继承啊。
Objcet dynamicproxy = Proxy.newProxyInstance()
dynamicproxy 是Proxy的实例对象,继承了Objcet。你不能再让它去继承ArrayList或者HashSet等等的类了。
这是我在网上找的IBM的帖子,关于动态代理讲的很详细
http://www.ibm.com/developerworks/cn/java/j-lo-proxy1/index.html


回复 使用道具 举报
非常谢谢
回复 使用道具 举报
您需要登录后才可以回帖 登录 | 加入黑马