JAVA 基于反射的动态代理

xiaoxiao2021-02-28  125

设计模式中有一种模式叫做代理模式,主要用来处理当该类因为某些原因不方便访问,但又要调用访问他的方法时,这时可以使用代理模式.设计一个代理类,通过代理类访问真实类.

代理分为静态代理和动态代理.

静态代理是程序在编译期代理类就已经确定自己要代理什么对象了,比如,有一位歌星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 stub method.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方法被调用.

至于为什么代理处理类里的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

转载请注明原文地址: https://www.6miu.com/read-58218.html

最新回复(0)