基础笔记-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
产生这个结果的原因
有可能成员属性不是公有的,因为不是公有的话就不能获取
而暴力获取之后获得16个java.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();
上面代码注意点:
l 由于动态生成的类只有一个有参数的构造方法,所以创建其对象的时候需要传入相应参数InvocationHandler的实现类
l 由于字节码动态生成,并不像其他类一样有类加载器加载,需要给这个类指定一个类加载器,一般可以用要代理的接口的类加载器
l 如果按照上面的代码调用没有返回值的方法调用不会报错而有返回值的方法调用会报空指针异常,
如果返回值类型如果与真正返回的值不匹配的话会抛出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
文章只是我学习路上的笔记,里面可能还有错误的地方,甚至是比较严重的错误,希望大家能帮忙指正。
基础笔记-JDK动态代理
最新推荐文章于 2024-10-13 23:27:44 发布