JAVA动态代理详解

定义

代理类在程序运行时创建的代理方式被成为动态代理。 也就是说,代理类并不需要在Java代码中定义,而是在运行时动态生成的。相比于静态代理, 动态代理的优势在于可以很方便的对代理类的函数进行统一的处理,而不用修改每个代理类的函数。
这么说比较抽象,我们以上文的静态代理为例,在执行doSomething方法前,打印真实类名称。试想,如果Subject接口有10个方法,每个方法执行前都要打印真实类名,那么我们要写一堆重复代码。通过使用动态代理,我们可以做一个“统一指示”,从而对所有代理类的方法进行统一处理,而不用逐一修改每个方法。下面我们来具体介绍下如何使用动态代理方式实现我们的需求。

示例

接口类

public interface Subject {
    public void doSometing();
}


委托类

public class RealSubject implements Subject{
    @Override
    public void doSometing() {
        System.out.println("I'm workding"); 
    }
}


动态代理类
在使用动态代理时,我们需要定义一个位于代理类与委托类之间的中介类,也可以叫动态代理类,这个类被要求实现InvocationHandler接口:

public class DynamicProxy implements InvocationHandler{
    //被代理的实例
    private Object obj;

    public DynamicProxy(Object _object){
        this.obj = _object;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println(obj.getClass().getName());
        Object result = method.invoke(obj, args);
        return result;
    }
}

InvocationHandler接口是JDK提供的动态代理接口,对被代理类的方法进行代理。这个接口的定义如下:

public interface InvocationHandler {   
    Object invoke(Object proxy, Method method, Object[] args); 
}

当我们调用代理类对象的方法时,这个“调用”会转送到invoke方法中,参数method标识了我们具体调用的是代理类的哪个方法,args为这个方法的参数。。通过上述代码,就实现了,在执行委托类的所有方法前,打印委托类名。
4. 场景类
看看如何通过动态代理类实现动态代理:

     public class Client {
        public static void main(String args[]){
        //我们要代理的真实对象
        Subject subject = new RealSubject();
        //创建动态代理类实例
        InvocationHandler  handler = new DynamicProxy(subject);
        //获取类加载器
        ClassLoader cl = subject.getClass().getClassLoader();
        //动态产生一个代理类
        Subject proxy = (Subject) Proxy.newProxyInstance(cl, subject.getClass().getInterfaces(), handler);
        //通过代理类,执行doSomething方法;
        proxy.doSometing();
    }
}


执行结果如下:

model.proxy.RealSubject
I'm workding

总结

总结一下,一个典型的动态代理可分为以下四个步骤:

  1. 创建接口类
  2. 创建委托类
  3. 通过实现InvocationHandler接口创建自己的中介类
  4. 通过场景类,动态生成代理类

如果只是想用动态代理,看到这里就够了。但如果想知道为什么通过proxy对象,就能够执行中介类的invoke方法,以及生成的proxy对象是什么样的,可以继续往下看。

源码分析(JDK7)

下面代码,生成了一个RealSubject的代理类

//动态产生一个代理类
Subject proxy = (Subject) Proxy.newProxyInstance(cl, subject.getClass().getInterfaces(), handler);

我们可以猜测上面代码做了以下事情:

  • 根据传入的第二个参数interfaces动态生成一个类,它实现interfaces中的接口,该例中即Subject接口的doSometing方法。假设动态生成的类为$Proxy0。

  • 通过传入的第一个参数classloder将刚生成的$Proxy0类加载到jvm中。

  • 利用第三个参数,调用$Proxy0的$Proxy0(InvocationHandler)构造函数 创建$Proxy0的对象

那么它到底是如何实现的呢?我们可以通过源码一窥究竟。

查看Proxy类的newProxyInstance方法,主要业务逻辑如下

//生成代理类class,并加载到jvm中
Class<?> cl = getProxyClass0(loader, interfaces);
//获取代理类参数为InvocationHandler的构造函数
final Constructor<?> cons = cl.getConstructor(constructorParams);
//生成代理类,并返回
return newInstance(cons, ih);
getProxyClass0方法
   private static Class<?> getProxyClass0(ClassLoader loader,
                                           Class<?>... interfaces) {
        if (interfaces.length > 65535) {
            throw new IllegalArgumentException("interface limit exceeded");
        }

        // If the proxy class defined by the given loader implementing
        // the given interfaces exists, this will simply return the cached copy;
        // otherwise, it will create the proxy class via the ProxyClassFactory
        return proxyClassCache.get(loader, interfaces);
    }

在我们的Proxy类中有个属性proxyClassCache,这是一个WeakCache类型的静态变量。它指示了我们的类加载器和代理类之间的映射。所以proxyClassCache的get方法用于根据类加载器来获取Proxy类,如果已经存在则直接从cache中返回,如果没有则创建一个映射并更新cache表。

我们看一下创建一个Proxy类的流程
V value = supplier.get();
Factory是一个Supplier的实现类,get方法如下:
value = Objects.requireNonNull(valueFactory.apply(key, parameter));
调用ProxyClassFactory的apply方法,生成proxy类的核心代码如下:

//Generate the specified proxy class.
byte[] proxyClassFile = ProxyGenerator.generateProxyClass(proxyName, interfaces);

至此我们就获取到了委托类对应的代理类型,接着我们通过该类的getConstructor方法获取该代理类的构造器,并传入InvocationHandler.class作为参数,生成代理类。

查看动态生成的代理类

通过上面的简单分析,我们知道了代理类的创建流程。但创建出来的代理类到底是什么样子的呢?
我们可以通过ProxyGenerator.generateProxyClass创建一个看看:

 public static void main(String[] args) throws IOException {
        //注意是RealSubject
        byte[] classFile = ProxyGenerator.generateProxyClass("TestProxyGen", RealSubject.class.getInterfaces());
        File file = new File("D:/aaa/TestProxyGen.class");
        FileOutputStream fos = new FileOutputStream(file); 
        fos.write(classFile); 
        fos.flush(); 
        fos.close(); 
      }

通过反编译工具查看生成的class文件内容如下:

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.lang.reflect.UndeclaredThrowableException;
import model.proxy.Subject;

public final class TestProxyGen extends Proxy
  implements Subject
{
  private static Method m1;
  private static Method m0;
  private static Method m3;
  private static Method m2;

  public TestProxyGen(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 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);
  }

  public final void doSometing()
    throws 
  {
    try
    {
      this.h.invoke(this, m3, null);
      return;
    }
    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);
  }

  static
  {
    try
    {
      m1 = Class.forName("java.lang.Object").getMethod("equals", new Class[] { Class.forName("java.lang.Object") });
      m0 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]);
      m3 = Class.forName("model.proxy.Subject").getMethod("doSometing", new Class[0]);
      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());
  }
}

这个类实现了doSometing方法。通过这里,就能理解为什么调用代理类,会执行中介类的invoke方法了:

 public final void doSometing()
    throws 
  {
    try
    {
      this.h.invoke(this, m3, null);
      return;
    }
    catch (RuntimeException localRuntimeException)
    {
      throw localRuntimeException;
    }
    catch (Throwable localThrowable)
    {
    }
    throw new UndeclaredThrowableException(localThrowable);
  }
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值