您的位置:首页 > 编程语言 > Java开发

java动态代理模式Proxy之JDK动态代理机制

2016-09-12 22:09 721 查看
JDK动态代理是利用接口,在JVM运行阶段动态生成Class,没错,它是在运行阶段生成的,是和字节码相关的一种操作。

JDK动态代理可以提供对代理对象的访问,可以在对象接口方法的前后处理一些逻辑,使被代理的对象的关注于自身的功能逻辑。JDK动态代理会实现它所表示的实际对象的接口,因此实现接口是最基本的需要。代理对象隐藏了实际对象,因此用户不知道是同代理做交互还是同对象本身做交互。

JDK动态代理首先要有一个动态类,该类得实现接口InvocationHandler。由于JDK代理是基于接口的,所以代理的对象需要实现接口,然后通过Proxy.newProxyInstance方法为目标对象生成代理对象。

代码示例如下:

/**
* 对象实现的接口
*/
interface Hello {
public void say();
}


/**
* 接口的实现
*/
class HelloImpl implements Hello {

public void say() {
System.out.println("say");
}
}


/**
* 代理类的实现 实现了InvocationHandler
*/
class DynamicProxy implements InvocationHandler {
private Object targetObject;

public DynamicProxy(Object targetObject) {
this.targetObject = targetObject;
}

public void before() {
System.out.println("before-----");
}

public void after() {
System.out.println("after------");
}

@Override
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
before();
method.invoke(targetObject, args);
after();
return null;
}
}


入口main如下

public static void main(String[] args) throws Exception {
Hello helloTarget = new HelloImpl();
DynamicProxy dynamicProxy = new DynamicProxy(helloTarget);
/*
* Proxy.newProxyInstance(ClassLoader classLoader, Class<?>[] interface, InvocationHandler invocationHandler)
* 有三个参数
* 第一个为classLoader,这里使用HelloImpl的类加载器
* 第二个为受代理的对象实现的接口,在这里也就是Hello
* 第三个为InvocationHandler接口的实现对象,在这里也就是dynamicProxy
* 该方法返回一个jvm动态生成的一个代理对象,该对象实现了Hello接口,该对象的class是动态生成的
*/
Hello hello = (Hello)Proxy.newProxyInstance(helloTarget.getClass().getClassLoader(),
helloTarget.getClass().getInterfaces(), dynamicProxy);
hello.say();
System.out.println(hello.getClass().getName());
}


输出如下:

before-----
say
after------
com.my.servlet.$Proxy0


从输出结果和代码可以看出,Proxy.newProxyInstance生成的代理对象实现了Hello接口,同时,该对象的class是一个com.my.servlet.Proxy0类型的。很奇特吧,我们根本没有定义Proxy0这个class。咦,可能有人会疑惑,是不是jdk库里面有个$Proxy0的实现了Hello接口呢。这是不可能的,jdk库哪有这么神,能未卜先知,猜出你定义的是什么接口。那么这过程是如何实现的呢,下面来反编译进去看一下newProxyInstance方法。

下面是反编译后,newProxyInstance方法的实现,我省略了部分代码,删除了安全验证,异常抛出等其它代码,只留下了主要实现部分

public static Object newProxyInstance(ClassLoader paramClassLoader,
Class<?>[] paramArrayOfClass,
InvocationHandler paramInvocationHandler)
throws IllegalArgumentException {

//clone paramArrayOfClass
Class[] arrayOfClass = (Class[]) paramArrayOfClass.clone();
//获取我们给的接口来生成代理class,这里是jvm生成字节码的地方
//可能是由于jdk我用的是1.8,反编译器版本没跟上
//反编译出来的部分变量是三个问号
//所以不进去看内部代码了
//有兴趣的可以进ProxyClassFactory查看Apply方法
//ProxyClassFactory是java.lang.reflect.Proxy的一个内部私有类
Class localClass = getProxyClass0(paramClassLoader, arrayOfClass);
//利用上面生成的代理class来对传入的paramInvocationHandler获取构造器
Constructor localConstructor = localClass
.getConstructor(constructorParams);
//利用构造器和传入的InvocationHandler实现对象,生成最终的Proxy
return localConstructor
.newInstance(new Object[] { paramInvocationHandler });
}


为了更好的认清里面的内容,我把主要实现部分从Proxy移除了出来,写成了方法,便于跟踪。

方法如下

public static Object newProxyInstance(ClassLoader paramClassLoader,
Class<?>[] paramArrayOfClass,
InvocationHandler paramInvocationHandler)
throws Exception {
//里面的内容是经过替换后,实现的主干。
//其功能与上面的部分一致,实现主要是放在这里
//当然了,剪去了缓存异常处理等等一系列代码,才看上去这么一点
Class localClass = Proxy.getProxyClass(paramClassLoader, paramArrayOfClass);
Constructor localConstructor = localClass
.getConstructor(new Class[]{ InvocationHandler.class });
return localConstructor
.newInstance(new Object[] { paramInvocationHandler });
}


main方法将Proxy.newInstance实现替换成自己的newProxyInstance,替换如下

Hello hello = (Hello)newProxyInstance(helloTarget.getClass()
.getClassLoader(), helloTarget.getClass().getInterfaces(),
dynamicProxy);


输出当然一样和刚才一样为

before-----
say
after------`这里写代码片`
com.my.servlet.$Proxy0


好了,下面要咱们看看这几步的内容

首先,我们在main函数里只执行这几行代码。来看看方法里的第一行到底做了什么

public static void main(String[] args) throws Exception {
//这一行设置了后,会生成下面的代理的class文件,否则不生成
System.setProperty("sun.misc.ProxyGenerator.saveGeneratedFiles", "true");
Hello helloTarget = new HelloImpl();
//第一行的代码
Proxy.getProxyClass(helloTarget.getClass().getClassLoader(),
helloTarget.getClass().getInterfaces());
}


好,执行完后,发现在工作目录底下多了个$Proxy0.class文件

现在,我们来对它进行反编译,得到如下的java文件

有点长,不管它,还是直接上代码,才比较清楚

final class $Proxy0 extends Proxy implements Hello {
private static Method m1;
private static Method m2;
private static Method m3;
private static Method m0;

public $Proxy0(InvocationHandler paramInvocationHandler) {
super(paramInvocationHandler);
}

static {
try {
m1 = Class.forName("java.lang.Object").getMethod("equals",
new Class[] { Class.forName("java.lang.Object") });
m2 = Class.forName("java.lang.Object").getMethod("toString",
new Class[0]);
m3 = Class.forName("com.my.servlet.Hello").getMethod("say",
new Class[0]);
m0 = Class.forName("java.lang.Object").getMethod("hashCode",
new Class[0]);
} catch (NoSuchMethodException localNoSuchMethodException) {
throw new NoSuchMethodError(localNoSuchMethodException.getMessage());
} catch (ClassNotFoundException localClassNotFoundException) {
throw new NoClassDefFoundError(
localClassNotFoundException.getMessage());
}
}

public final void say() {
try {
//调用我们实现的InvocationHandler里的invoke方法
this.h.invoke(this, m3, null);
return;
} catch (Error | RuntimeException localError) {
throw localError;
} catch (Throwable localThrowable) {
throw new UndeclaredThrowableException(localThrowable);
}
}
public final boolean equals(Object paramObject) {
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 String toString() {
try {
return (String) this.h.invoke(this, m2, null);
} catch (Error | RuntimeException localError) {
throw localError;
} catch (Throwable localThrowable) {
throw new UndeclaredThrowableException(localThrowable);
}
}

public final int hashCode() {
try {
return ((Integer) this.h.invoke(this, m0, null)).intValue();
} catch (Error | RuntimeException localError) {
throw localError;
} catch (Throwable localThrowable) {
throw new UndeclaredThrowableException(localThrowable);
}
}
}


看到这段代码,发现了吧,该class继承了Proxy,实现了Hello。

里面的say方法如下

public final void say() {
try {
//调用我们实现的InvocationHandler里的invoke方法
this.h.invoke(this, m3, null);
return;
} catch (Error | RuntimeException localError) {
throw localError;
} catch (Throwable localThrowable) {
throw new UndeclaredThrowableException(localThrowable);
}
}


h是继承自Proxy的属性

protected InvocationHandler h;


在构造函数中被初始化

protected Proxy(InvocationHandler paramInvocationHandler) {
Objects.req
ab57
uireNonNull(paramInvocationHandler);
this.h = paramInvocationHandler;
}


这个invocationHandler也就是我们传进去的自己实现的DynamicProxy实例,method是受代理的对象的方法。整个情况瞬间一目了然,JDK动态代理的基本原理过程也就差不多了。一些实现细节方面,就不多说了。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息