基础笔记-JDK动态代理

基础笔记-JDK动态代理

1.类关系图

 

2.创建动态代理的两种方式

创建某一接口 Foo 的代理:

1. 

 

   InvocationHandler handler = new MyInvocationHandler(...);   

   Class proxyClass = Proxy.getProxyClass(   Foo.class.getClassLoader(), new Class[] { Foo.class });   

   Foo f = (Foo) proxyClass.getConstructor(new Class[] { InvocationHandler.class }).newInstance(new Object[] { handler }); 

或使用以下更简单的方法:

2. 

     Foo f = (Foo) Proxy.newProxyInstance(Foo.class.getClassLoader(), new Class[] { Foo.class }, handler);

 

2.1第一种方式

测试

2.1.1getProxyClass方法生成动态类的Class对象

Class proxyClass = Proxy.getProxyClass(Collection.class.getClassLoader(), Collection.class);

System.out.println(proxyClass.getName());

结果打印com.sun.proxy.$Proxy0

这里获得的Class对象是我们生成的字节码对应的Class对象

2.1.2查询生成动态类的构造方法

Constructor[] cs = proxyClass.getConstructors();

for(Constructor c:cs){

String name = c.getName();

Class[] classes = c.getParameterTypes();

StringBuilder sb = new StringBuilder();

sb.append("(");

for(int i =0;classes!=null&&i<classes.length;i++){

sb.append(classes[i].getName()+",");

}

sb.deleteCharAt(sb.length()-1);

sb.append(")");

System.out.println(name+sb);

}

代理类构造函数:

com.sun.proxy.$Proxy0(java.lang.reflect.InvocationHandler)

注意这个生成的对象只有一个方法,并且是有参数的,因为这种反射方式获得的是公有的,因此可以说公有的构造函数只有一个,并且接受一个InvocationHandler类型的参数

2.1.3查询生成动态类的属性

Field[] fs = proxyClass.getFields();

System.out.println("属性数量"+fs.length);

for(Field f:fs){

String name = f.getName();

System.out.println(name);

}

控制台打印:

代理类的属性:

属性数量0

产生这个结果的原因

有可能成员属性不是公有的,因为不是公有的话就不能获取

而暴力获取之后获得16java.lang.reflect.Method类型的属性,这个我后面贴出一个链接,关于代理类内部到底是什么样子的。

这样还可能有一种情况,因为生成的类中有$Proxy0(java.lang.reflect.InvocationHandler)而一般这么设计在类中会有一个对应InvocationHandler类型的属性来接收。而从上面代码没有看到这个属性,是否属性定义在其父类中或接口中呢?

后面2.1.5可知其继承了java.lang.reflect.Proxy而正好这个对象中有一个属性就是protected InvocationHandler h;

2.1.4查询生成动态类的方法

Method[] ms = proxyClass.getDeclaredMethods();

for(Method m:ms){

m.setAccessible(true); 

String name = m.getName();

Class[] classes = m.getParameterTypes();

StringBuilder sb = new StringBuilder();

sb.append("(");

for(int i =0;classes!=null&&classes.length!=0&&i<classes.length;i++){

sb.append(classes[i].getName()+",");

}

if(classes!=null&&classes.length!=0)

sb.deleteCharAt(sb.length()-1);

sb.append(")");

System.out.println(name+sb);

}

结果显示代理类中的方法包括

重写java.lang.Object中的hashCode(),equals(java.lang.Object),toString()方法

实现接口java.util.Collection中方法

就是我们指定的接口的方法

2.1.5查询动态类的父类及接口

System.out.println("接口:");

Class[] interfaces = proxyClass.getInterfaces();

for(Class interf:interfaces){

System.out.println(interf.getName());

}

System.out.println(父类:");

Class Superclass = proxyClass.getSuperclass();

System.out.println(Superclass.getName());

 

接口:

java.util.Collection

父类:

java.lang.reflect.Proxy

 

2.1.6生成代理对象

Class proxyClass = Proxy.getProxyClass(Collection.class.getClassLoader(), Collection.class);

Constructor constructor = proxyClass.getConstructor(InvocationHandler.class);

Collection c = (Collection)constructor.newInstance(new InvocationHandler(){

public Object invoke(Object proxy, Method method, Object[] args)

throws Throwable {

return null;

}

});

c.clear();

c.size();

上面代码注意点:

由于动态生成的类只有一个有参数的构造方法,所以创建其对象的时候需要传入相应参数InvocationHandler的实现类

由于字节码动态生成,并不像其他类一样有类加载器加载,需要给这个类指定一个类加载器,一般可以用要代理的接口的类加载器

如果按照上面的代码调用没有返回值的方法调用不会报错而有返回值的方法调用会报空指针异常,

如果返回值类型如果与真正返回的值不匹配的话会抛出java.lang.NullPointerException这里返回值Null而size要求返回值为int。

2.2第二种方式

这种方式是第一种方式的简化

Collection c = (Collection)Proxy.newProxyInstance(Collection.class.getClassLoader(),

           new Class[]{Collection.class},

new InvocationHandler(){

public Object invoke(Object proxy, Method method, Object[] args)

throws Throwable {

return null;

}

});

 


3.InvocationHandler

此类只有一个方法,即:

Object invoke(Object proxy,Method method,Object[] args)throws Throwable

当我们调用一个代理类的一个方法的时候其实是调用的invoke方法

其中有三个参数,分别代表代理类对象,调用方法,方法的参数。invoke方法的返回值为调用时候的返回值。如下

 

 

 

 

*注意只有代理对象自己实现的方法才会去调用invoke方法,这些方法包括接口中的方法以及hashCode(),equals(java.lang.Object),toString()三个重写方法

想看看代理类生成的样子的可以看看http://yy629.iteye.com/blog/681430


文章只是我学习路上的笔记,里面可能还有错误的地方,甚至是比较严重的错误,希望大家能帮忙指正。


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值