设计模式中有一种模式叫做代理模式,主要用来处理当该类因为某些原因不方便访问,但又要调用访问他的方法时,这时可以使用代理模式.设计一个代理类,通过代理类访问真实类.
代理分为静态代理和动态代理.
静态代理是程序在编译期代理类就已经确定自己要代理什么对象了,比如,有一位歌星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会被调用就更有意思了:
首先,解释一下代理类生成代码:
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