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

版权声明:本文为博主原创文章,遵循 CC 4.0 by-sa 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/zqz_zqz/article/details/78869000

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

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

  1. 一个接口类;
  2. 一个实现接口的业务类;
  3. 一个生成动态代理类,并通过动态代理类来执行业务方法的测试类;

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

  • 接口类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 ,就能输出执行结果,大家先回答一个问题,以下两种情况,控制台会打印什么内容?

  1. 业务方法正常执行;
  2. 业务方法抛出异常;

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

假设你已经知道答案了,然后该如何入手呢?切入点就在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方法来实现最终的代理逻辑;

简单吧?貌似也不是很简单,但是也没有我们想象中的那么神奇,完!

展开阅读全文

没有更多推荐了,返回首页