你知道JDK动态代理模式为什么基于接口吗?

248 阅读3分钟

    相信大家对于动态代理模式都不陌生,即使业务代码很少涉及,那么Spring中Aop面向切面编程就是代理模式+反射机制的运用这点大家应该都很清楚

    那么在初学时候你是否有这样的疑问呢?

    1、生成的代理类在哪里,长什么样子?

    2、目标类(被代理类)为什么要实现接口?

    3、实现InvocationHandler接口的invoke方法是什么时候执行的呢?

    在开始回答疑问之前我们来看一下基于jdk动态代理的样版式代码。这里有两个要点,一是实现InvocationHandler接口的invoke方法二是通过Proxy.newProxyInstance()方法返回代理对象

package com.my.test.design.proxy;

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

public class MyProxy implements InvocationHandler {

    // 被代理对象
    private Object obj;

    // 返回一个代理对象
    public Object bind (Object obj){
        this.obj = obj;
        // 疑问:这个返回的代理对象在哪里,长什么样子?
        return Proxy.newProxyInstance(obj.getClass().getClassLoader(),
                obj.getClass().getInterfaces(),this);
    }

    // 疑问:这个invoke方法什么时候执行?
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("begin 开始执行方法");
        Object object = method.invoke(obj,args);
        System.out.println("end 执行方法结束");
        return object;
    }
} 

    还记得静态代理么,静态代理要求代理类和目标类(被代理类)都要实现同一个接口,然后在代理类里引入被代理对象,这样我们就可以在访问目标类之前先访问代理类通过代理类再去访问目标类,从而达到控制对目标类的访问

    其实动态代理类也是一样的,只不过创建代理对象的细节jdk帮我们做了。不仅如此,动态代理最大优势在于我事先并不知道要代理哪个类,而是根据你传入对象决定的。

    在main方法里加上这个参数就会在本地生成一个代理对象的class文件$Proxy0.class

System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles", "true");

生成的文件就在你所在项目的com\sun\proxy下面

下面是反编译$Proxy0.class文件结果

疑问1:代理类在哪里,长什么样子?现在已经知道了

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

疑问2:目标类(被代理类)为什么要实现接口?反编译后我们发现代理类$Proxy0已经继承了Proxy类,因为java
是单继承的所以要实现接口
public final class $Proxy0
  extends Proxy
  implements IBook
{
  private static Method m1;
  private static Method m3;
  private static Method m2;
  private static Method m4;
  private static Method m0;
  
  public $Proxy0(InvocationHandler paramInvocationHandler)
  {
    // 这里传入的是MyProxy,MyProxy implements InvocationHandler接口
    super(paramInvocationHandler);
  }
  
  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);
    }
  }
  疑问3:代理类中实现接口的invoke方法是什么时候执行的呢?
  当我们调用saleBook方法时候就会执行到invoke方法
  public final void saleBook()
  {
    try
    {
      // this.h是父类Proxy中InvocationHandler实例,本质还是子类自己的业务类通过构造方法传进去的
      // 而invoke方法就是我们实现的方法,
      // m3 就是后面的 m3 = Class.forName("proxy.IBook").getMethod("saleBook", new Class[0]);
      // 真正被代理对象里面方法
      // 总结一句话就是:这里执行的是我们实现InvocationHandler接口中的invoke方法
      this.h.invoke(this, m3, null);
      return;
    }
    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 void buyBook()
  {
    try
    {
      this.h.invoke(this, m4, null);
      return;
    }
    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);
    }
  }
  
  static
  {
    try
    {
      m1 = Class.forName("java.lang.Object").getMethod("equals", new Class[] { Class.forName("java.lang.Object") });
      m3 = Class.forName("proxy.IBook").getMethod("saleBook", new Class[0]);
      m2 = Class.forName("java.lang.Object").getMethod("toString", new Class[0]);
      m4 = Class.forName("proxy.IBook").getMethod("buyBook", 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());
    }
  }
}

// this.h这个h是父类Proxy中InvocationHandler实例    /**
     * Constructs a new {@code Proxy} instance from a subclass
     * (typically, a dynamic proxy class) with the specified value
     * for its invocation handler.
     *
     * @param  h the invocation handler for this proxy instance
     *
     * @throws NullPointerException if the given invocation handler, {@code h},
     *         is {@code null}.
     */
    protected Proxy(InvocationHandler h) {
        Objects.requireNonNull(h);
        this.h = h;
    }