JAVA 基于反射的动态代理
来源:互联网 发布:c语言疯狂讲义 pdf 编辑:程序博客网 时间:2024/05/21 07:14
设计模式中有一种模式叫做代理模式,主要用来处理当该类因为某些原因不方便访问,但又要调用访问他的方法时,这时可以使用代理模式.设计一个代理类,通过代理类访问真实类.
代理分为静态代理和动态代理.
静态代理是程序在编译期代理类就已经确定自己要代理什么对象了,比如,有一位歌星A(真实类),有一位经纪人a(代理类),这个经纪人身上贴了个标签:我只是A的经纪人,就管A的事,其他歌星的事别找我,找我也不管用.某大公司商演,上次找的经纪人a,这次想请歌星B来演出,于是,他们还要再去联系歌星B的经纪人b.
静态代理扩展起来是比较麻烦的,添加其他真实类就要添加相应代理类.
而动态代理就是有某位经纪人在演艺圈混的风生水起,同时担任多个人的经纪人,某大公司商演再想换人,都是找这一个经纪人,演艺圈又出了个很火的新人(新的真实类),这个经纪人再把他也代理了.扩展起来相对简单.
静态代理和动态代理的优势分别就是,静态代理容易理解及编写代码,但扩展麻烦.动态代理扩展简单,但不易理解.
原始的代理模式,为了透明访问,代理类和真实类要实现相同的接口,而JAVA中基于反射的动态代理也是这样.
一般我们使用基于反射的动态代理可以这样写:
public class ProxyHandler implements InvocationHandler{private Object target;/***** * 返回要代理的类 * @param target * @return */ public Object createProxy(Object target){ this.target = target; //返回代理类实例 return Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), this); }public Object invoke(Object proxy, Method method, Object[] args)throws Throwable {// TODO Auto-generated method stubmethod.invoke(target, args);return null;}}
ProxyHandler实现InvocationHandler接口
注意:ProxyHandler并不是我们需要的代理类,他只是个代理辅助类或称为调用处理类(里面有个重要的invoke方法),调用ProxyHandler的createProxy()方法获得的类才是真正的代理类.但这个真正代理类不是我们自己编写代码,而是使用反射技术返回的,他也利用反射技术实现了真实类的接口.
关于接口InvocationHandler,API文档中是这样说的:
Each proxy instance has an associated invocation handler. When a method is invoked on a proxy instance, the method invocation is encoded and dispatched to the
invoke
method of its invocation handler.
至于为什么代理处理类里的invoke会被调用就更有意思了:
首先,解释一下代理类生成代码:
return Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), this);
我们使用代理类,希望像使用真实类一样,所以代理类也应该实现真实类实现的接口,而同时,当我们调用代理类方法时,会回来调用代理处理类中的invoke()方法,这是为什么在new新的代理类时传这两个参数:target.getClass().getInterfaces()(表示真实类的所有接口), this(代理处理类的引用).
同时我们有理由猜测,我们生成的代理类中的方法实现会通过我们传进去的this引用(或代理处理类引用)来调用他自己的invoke方法.
接下来有意思的来了,我们读源码看代理类生成过程有这样一句:
/* * Generate the specified proxy class. */ byte[] proxyClassFile = ProxyGenerator.generateProxyClass( proxyName, interfaces, accessFlags);
上边一句代码表示根据传进来的参数生成了一份字节码.其实我们接着详细读下去会发现,系统将生成的字节码存储在了硬盘上,如下:
Path path; if (i > 0) { Path dir = Paths.get(name.substring(0, i).replace('.', File.separatorChar)); Files.createDirectories(dir); path = dir.resolve(name.substring(i+1, name.length()) + ".class"); } else { path = Paths.get(name + ".class"); } Files.write(path, classFile);
这不就和我们调用我们自己写的类一样了吗?不同的是代理类是系统帮我们生成的,我们用上面代码其实可以自己生成想要的代理类字节码,如下,生成了一份String的代理类字节码:
public class TestProxy {public static void main(String[] args) throws Exception {byte[] byteArray = ProxyGenerator.generateProxyClass("$Proxy0", String.class.getInterfaces());FileOutputStream fos = new FileOutputStream(new File("C:\\Users\\Administrator\\Desktop\\out.class"));fos.write(byteArray);fos.flush();fos.close();}}
桌面上生成了一个out.class文件,这不就是我们java文件编译后的字节码文件嘛!反编译一把,这样的:
import java.io.Serializable;import java.lang.reflect.InvocationHandler;import java.lang.reflect.Method;import java.lang.reflect.Proxy;import java.lang.reflect.UndeclaredThrowableException;public final class $Proxy0 extends Proxy implements Serializable, Comparable, CharSequence{ private static Method m5; private static Method m3; private static Method m1; private static Method m4; private static Method m0; private static Method m6; private static Method m2; public $Proxy0(InvocationHandler paramInvocationHandler) throws { super(paramInvocationHandler); } public final int length() throws { try { return ((Integer)this.h.invoke(this, m5, null)).intValue(); } catch (Error|RuntimeException localError) { throw localError; } catch (Throwable localThrowable) { throw new UndeclaredThrowableException(localThrowable); } } public final int compareTo(Object paramObject) throws { try { return ((Integer)this.h.invoke(this, m3, new Object[] { paramObject })).intValue(); } catch (Error|RuntimeException localError) { throw localError; } catch (Throwable localThrowable) { throw new UndeclaredThrowableException(localThrowable); } } public final boolean equals(Object paramObject) throws { try { return ((Boolean)this.h.invoke(this, m1, new Object[] { paramObject })).booleanValue(); } catch (Error|RuntimeException localError) { throw localError; } catch (Throwable localThrowable) { throw new UndeclaredThrowableException(localThrowable); } } public final char charAt(int paramInt) throws { try { return ((Character)this.h.invoke(this, m4, new Object[] { Integer.valueOf(paramInt) })).charValue(); } catch (Error|RuntimeException localError) { throw localError; } catch (Throwable localThrowable) { throw new UndeclaredThrowableException(localThrowable); } } public final int hashCode() throws { try { return ((Integer)this.h.invoke(this, m0, null)).intValue(); } catch (Error|RuntimeException localError) { throw localError; } catch (Throwable localThrowable) { throw new UndeclaredThrowableException(localThrowable); } } public final CharSequence subSequence(int paramInt1, int paramInt2) throws { try { return (CharSequence)this.h.invoke(this, m6, new Object[] { Integer.valueOf(paramInt1), Integer.valueOf(paramInt2) }); } catch (Error|RuntimeException localError) { throw localError; } catch (Throwable localThrowable) { throw new UndeclaredThrowableException(localThrowable); } } public final String toString() throws { try { return (String)this.h.invoke(this, m2, null); } catch (Error|RuntimeException localError) { throw localError; } catch (Throwable localThrowable) { throw new UndeclaredThrowableException(localThrowable); } } static { try { m5 = Class.forName("java.lang.CharSequence").getMethod("length", new Class[0]); m3 = Class.forName("java.lang.Comparable").getMethod("compareTo", new Class[] { Class.forName("java.lang.Object") }); m1 = Class.forName("java.lang.Object").getMethod("equals", new Class[] { Class.forName("java.lang.Object") }); m4 = Class.forName("java.lang.CharSequence").getMethod("charAt", new Class[] { Integer.TYPE }); m0 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]); m6 = Class.forName("java.lang.CharSequence").getMethod("subSequence", new Class[] { Integer.TYPE, Integer.TYPE }); m2 = Class.forName("java.lang.Object").getMethod("toString", new Class[0]); return; } catch (NoSuchMethodException localNoSuchMethodException) { throw new NoSuchMethodError(localNoSuchMethodException.getMessage()); } catch (ClassNotFoundException localClassNotFoundException) { throw new NoClassDefFoundError(localClassNotFoundException.getMessage()); } }}
一看就明白了,代理类在所有的方法里又通过传进来的代理处理类的引用调用了invoke方法.
总结整个过程:
通过Proxy静态方法获得代理类,强转一下代理类成高层次接口类型,因为代理类也实现了真实类的接口,可以调用和真实类相同的方法,但代理类中方法的实现却和真实类不同,代理类方法又调用了代理类处理类的invoke方法,invoke方法使用反射技术又调用了真实类的方法,从而实现代理的整个过程.
在invoke中随手打印了一下proxy没想到还引出了StackOverflowError,查了一下baidu,又看了一下out.class的源码发现问题应该是这样引起的:
打印proxy,就要调用proxy对象的toString方法,而proxy中的方法都回调了invoke方法,而就是在invoke方法中进行的打印操作,形成了递归...
这其中还有不少过程值得细细琢磨,以上只是个人粗浅见解,如有错误,欢迎指正!
end
- JAVA 基于反射的动态代理
- java反射之基于JDK的动态代理的乐子
- Java 反射-动态代理
- java动态代理、反射
- java的动态代理和反射
- JAVA的反射机制与动态代理
- JAVA的反射机制与动态代理
- JAVA的反射机制和动态代理
- java的反射实现动态代理类
- JAVA的反射机制和动态代理
- java反射和动态代理的理解
- 反射的动态代理
- 杂谈下java的反射(反射,动态代理)
- Java反射与动态代理
- Java反射和动态代理
- java、反射和动态代理
- 07 Java反射/动态代理
- java反射与动态代理
- 软件测试之App测试-硬件环境测试
- LeetCode-83. Remove Duplicates from Sorted List (Java)
- iOS开发-Runtime详解(简书)
- JAVA网络编程复习
- R语言-股票数据库(3)-股票日K线信息-前复权-Wind
- JAVA 基于反射的动态代理
- Android中String类型的颜色值转换为int类型
- 向网站添加音频的最简单方法
- 回文字符串
- Zigbee 8051环境 开发笔记- 1 环境配置
- 将一个数转换成小于它的任意进制(java版)
- C++中int,string等常见类型的转换
- nodejs入门(04)-事件循环
- 对Android 系统的理解