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

一篇文章彻底搞懂java动态代理的实现

2017-12-22 00:17 896 查看
网上有太多文章介绍动态代理是什么,这里就不介绍了,本文目的是让大家弄懂动态代理是如何做到这些神奇的功能的。

先来一个小demo,通过这个demo来讲解,动态代理需要三个类:

一个接口类;

一个实现接口的业务类;

一个生成动态代理类,并通过动态代理类来执行业务方法的测试类;

下面我们就一一实现它们。

接口类ITaskService

package com.zqz.jdkproxy;

public interface ITaskService {
public void doTask();
}


实现ITaskService 接口的业务类TaskServiceImpl

package com.zqz.jdkproxy;

public class TaskServiceImpl implements ITaskService{

public String doTask(){
System.out.println("do task");
return "do task";
}

}


实现动态代理

package com.zqz.jdkproxy;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

public class TestMain {

public static void main(String[] args)  {

/**
* 通过Proxy.newProxyInstance()方法为我们的业务类生成动态代理类实例proxyInstance对象
* 它需要三个参数
* 当前的Classloader,用来加载动态生成代理类;动态代理类要实现的业务接口;InvocationHandler执行代理操作,并调用真正的业务类来执行业务方法;
*/
ITaskService proxyInstance = (ITaskService) Proxy.newProxyInstance(
TestMain.class.getClassLoader(),  //装载生成的动态代理类的classloader对象
new Class[]{ITaskService.class},  //生成的动态代理类需要实现的业务接口
new InvocationHandler(){          //InvocationHandler,调用代理操作,并执行真正的业务方法;
//被代理的目标对象
ITaskService taskService = new TaskServiceImpl();
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
try{
//业务方法执行前打印
System.out.println("before");
//调用业务方法
Object obj = method.invoke(taskService, args);
//业务方法执行后打印
System.out.println("after");
return obj;
} catch (Throwable e){
//业务方法抛出异常打印
System.out.println("exception");
throw e;
} finally{
//业务方法执行完成打印(无论是否发生异常)
System.out.println("finally");
}
}
}
);
//使用代理对象执行代理方法
proxyInstance.doTask();
}
}


三个类完成了,通过运行TestMain ,就能输出执行结果,大家先回答一个问题,以下两种情况,控制台会打印什么内容?

业务方法正常执行;

业务方法抛出异常;

如果你知道上面的答案,那么我们一起往下探索,为什么会有这样的输出结果(如果不知道,说明你还不清楚动态代理是什么,开篇说了,本文对此不做讲解,请自行度娘、谷歌,然后运行代码自己寻找答案);

假设你已经知道答案了,然后该如何入手呢?切入点就在Proxy.newProxyInstance()生成的动态代理类实例proxyInstance 上,如果我们拿到了它的源码,一切真相就浮出水面了,如何去拿呢?不卖关子,直接上代码:

byte[] bytes = ProxyGenerator.generateProxyClass("TaskService$proxy", new Class[]{ITaskService.class});
FileOutputStream fos = new FileOutputStream(new File("c://TaskService$proxy.class"));
fos.write(bytes);
fos.flush();
fos.close();
Object obj = new Object();


这段代码就是获取动态代理生成的代理类,大家肯定看出来了,关键点就在ProxyGenerator.generateProxyClass()这个方法,为什么是这个方法?如果我们去跟踪Proxy.newProxyInstance()源码就会发现代理类的字节码最终是由这个方法构造出来的,它的执行过程非常简单,如下:

Proxy类有一个静态成员变量proxyClassCache,它就是代理类的缓存,首先根据当前Classloader和业务类的接口类调用缓存的get方法,查找缓存里是否存在代理类的Class对象(这个缓存是为了提高执行效率,避免每次调用都生成一个新的代理类),有就直接返回,如果不存在,则会调用Proxy内部类ProxyClassFactory的apply方法来构建并载入这个类,构建类就是调用了ProxyGenerator.generateProxyClass()方法来构建代理类的字节码,然后执行defineClass0()方法定义类(不清楚这个方法作用的童鞋,建议去查看下Classloader载入类的过程,当然本文不理解这个地方是没有影响,自己看着办);

我们很容就拿到了它的字节码数组,并把它生成class文件保存到了我们的硬盘上c://TaskService$proxy.class,然后通过反编译工具就获取到了它的源码:

import com.zqz.jdkproxy.ITaskService;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.lang.reflect.UndeclaredThrowableException;

public final class TaskService$proxy extends Proxy
implements ITaskService
{
private static Method m1;
private static Method m2;
private static Method m3;
private static Method m0;

public TaskService$proxy(InvocationHandler paramInvocationHandler)
throws
{
super(paramInvocationHandler);
}

public final boolean equals(Object paramObject)
throws
{
try
{
return ((Boolean)this.h.invoke(this, m1, new Object[] { paramObject })).booleanValue();
}
catch (RuntimeException localRuntimeException)
{
throw localRuntimeException;
}
catch (Throwable localThrowable)
{
}
throw new UndeclaredThrowableException(localThrowable);
}

public final String toString()
throws
{
try
{
return (String)this.h.invoke(this, m2, null);
}
catch (RuntimeException localRuntimeException)
{
throw localRuntimeException;
}
catch (Throwable localThrowable)
{
}
throw new UndeclaredThrowableException(localThrowable);
}

public final void doTask()
throws
{
try
{
this.h.invoke(this, m3, null);
return;
}
catch (RuntimeException localRuntimeException)
{
throw localRuntimeException;
}
catch (Throwable localThrowable)
{
}
throw new UndeclaredThrowableException(localThrowable);
}

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

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.zqz.jdkproxy.ITaskService").getMethod("doTask", new Class[0]);
m0 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]);
return;
}
catch (NoSuchMethodException localNoSuchMethodException)
{
throw new NoSuchMethodError(localNoSuchMethodException.getMessage());
}
catch (ClassNotFoundException localClassNotFoundException)
{
}
throw new NoClassDefFoundError(localClassNotFoundException.getMessage());
}
}


本文重点来了,我们一步一步的解析这个类

public final class TaskService$proxy extends Proxy implements ITaskService


这个代理类是final类,继承自Proxy类,并且实现了ITaskService,我提两个问题,带着问题去找答案:

1. 为什么要继承Proxy类?

2. 为什么要实现ITaskService接口?

我相信第二个问题,大家肯定都知道答案,我就不费口舌了,我们只去找第一个问题的答案,接着往下看:

private static Method m1;
private static Method m2;
private static Method m3;
private static Method m0;


四个Method成员变量,这又是什么鬼?答案在代码最后的static块中:

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.zqz.jdkproxy.ITaskService").getMethod("doTask", new Class[0]);
m0 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]);
return;
}
catch (NoSuchMethodException localNoSuchMethodException)
{
throw new NoSuchMethodError(localNoSuchMethodException.getMessage());
}
catch (ClassNotFoundException localClassNotFoundException)
{
}
throw new NoClassDefFoundError(localClassNotFoundException.getMessage());
}


M1就是Object类的equals方法;

M2就是Object类的toString方法Object类的toString方法;

M3就是我们业务接口类的doTask方法;

M4就是Object类的hashCode方法;

这里又抛出一个问题,为什么要获取这四个方法呢?

M3大家肯定是没有疑问,业务方法就是我们要代理的方法;

那么equals,toString,hashCode为什么也要代理呢?其实我们想一想这三个方法的作用就知道了,如果不对这三个方法做代理,当调用这三个方法的时候,返回的就是新生成的代理对象的toString,equals,hashCode的方法,而不是我们原来业务对象的方法,执行结果肯定是不一致的,拿hashCode方法来说,动态生成的代理类与我们原来业务类是两个不同的类,所以hashCode肯定也是不一样的,这样就和我们预期结果不一样了,所以这三个方法也是需要代理的;

这里还有一个问题不知道大家有没有注意,即使我们需要代理这四个方法,为什么要专门把它们赋给成员变量呢?原因就在于执行效率,如果熟悉反射的话,就会知道getMethod方法要做各种检查、查找操作,比直接调用方法要慢很多,所以在初始化的时候获取到它们,并赋给成员变量,起到一个缓存作用;

剩下的就是业务接口实现的方法了,他们实现代码基本是一模一样,我们只拿实现的业务接口的方法来分析:

public final void doTask()
throws
{
try
{
this.h.invoke(this, m3, null);
return;
}
catch (RuntimeException localRuntimeException)
{
throw localRuntimeException;
}
catch (Throwable localThrowable)
{
}
throw new UndeclaredThrowableException(localThrowable);
}


关键代码在this.h这个对象上,它又是何方神圣,如果我们去动态代理类的父类Proxy类中一找就会发现,原来它就是Proxy中的一个成员变量:

protected InvocationHandler h;


还记得我们在TestMain 方法里面创建的那个InvocationHandler实例对象吗? 这个h就是我们传入的InvocationHandler对象的实例,在接口实现方法中,就是通过它调用我们自己实现的invoke方法来实现代理逻辑。

所以流程就是,生成动态代理对象继承了Proxy类,Proxy类里面有一个成员变量持有我们自己创建的InvocationHandler对象,动态代理类在实现了业务接口的方法中,调用我们在InvocationHandler对象中实现的invoke方法来实现最终的代理逻辑;

简单吧?貌似也不是很简单,但是也没有我们想象中的那么神奇,完!
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  java 动态代理