大话设计模式:动态代理模式

/ 大话设计模式 / 1 条评论 / 469人围观

由于静态代理带来扩展性差,可维护性差等缺点,所以就有了动态代理模式。

下面介绍一下JDK的动态代理:

动态代理介绍

1. Proxy类:

provides static methods for creating dynamic proxy classes and instances, and it is also the superclass of all dynamic proxy classes created by those methods.

它是所有动态代理类的父类,同时提供一个静态方法来创建代理类的实例,方法名叫:newProxyInstance

2. InvocationHandler接口:

Processes a method invocation on a proxy instance and returns the result. This method will be invoked on an invocation handler when a method is invoked on a proxy instance that it is associated with.

其中只有一个方法invoke,大致是:在代理类的实例中调用真实方法,并返回结果。

有三个参数:

  1. proxy:代理对象的实例
  2. method: 通过它可以调用真实方法
  3. args:传过来的参数

动态代理的简单应用

  1. 创建实现InvocationHandler接口的代理类:
/**
 * description 实现InvocationHandler接口的动态代理类
 *
 * @author 70KG
 * @date 2018/8/2
 */
public class ProxyClassBeans implements InvocationHandler {

    /**
     * 被代理的对象
     **/
    private Object tar;

    /**
     * 利用反射来调用目标方法
     **/
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        doBefore();
        Object invoke = method.invoke(tar, args);
        doAfter();
        return invoke;
    }

    /** 绑定该类实现的所有接口,取得代理类 **/
    public Object getProxyInstance() {
        return Proxy.newProxyInstance(tar.getClass().getClassLoader(), tar.getClass().getInterfaces(), this);
    }

    // ===================动态代理类中的增强方法========================

    public void doBefore() {
        System.out.println("在国外寻找优质的货源。");
    }

    public void doAfter() {
        System.out.println("精包装安全邮寄回国内。");
    }

    // ===========================get/set=============================


    public Object getTar() {
        return tar;
    }

    public void setTar(Object tar) {
        this.tar = tar;
    }
}
  1. 接口
/**
 * description
 *
 * @author 70KG
 * @date 2018/8/1
 */
public interface IBusinessA {

    /**
     * Description: 卖包的业务接口
     * Author:70KG
     * Param [brand] 品牌
     * Return void
     * Date 2018/8/1 9:46
     */
    void saleBag(String brand);

}
/**
 * description
 *
 * @author 70KG
 * @date 2018/8/1
 */
public interface IBusinessB {

    /**
     * Description: 卖手表的业务接口
     * Author:70KG
     * Param [brand] 品牌
     * Return void
     * Date 2018/8/1 9:48
     */
    void saleWatch(String brand);

}
  1. 实现规定接口的被代理类
/**
 * description
 *
 * @author 70KG
 * @date 2018/8/1
 */
public class CompanyA implements IBusinessA {

    @Override
    public void saleBag(String brand) {
        System.out.println("从国外A公司买到一款" + brand + "牌的包。");
    }

}
/**
 * description
 *
 * @author 70KG
 * @date 2018/8/1
 */
public class CompanyB implements IBusinessB {

    @Override
    public void saleWatch(String brand) {
        System.out.println("从国外B公司买到一款" + brand + "牌的手表。");
    }

}
  1. 测试方法
/**
 * description
 *
 * @author 70KG
 * @date 2018/8/2
 */
public class MainTest {

    public static void main(String[] args) {
        ProxyClassBeans proxyClassBeans = new ProxyClassBeans();

        IBusinessA companyA = new CompanyA();
        proxyClassBeans.setTar(companyA);
        IBusinessA instance1 = (IBusinessA) proxyClassBeans.getProxyInstance();
        instance1.saleBag("Gucci");

        System.out.println("=================================");

        IBusinessB companyB = new CompanyB();
        proxyClassBeans.setTar(companyB);
        IBusinessB instance2 = (IBusinessB) proxyClassBeans.getProxyInstance();
        instance2.saleWatch("Tissot");
    }

}
  1. 运行结果
在国外寻找优质的货源。
从国外A公司买到一款Gucci牌的包。
精包装安全邮寄回国内。
=================================
在国外寻找优质的货源。
从国外B公司买到一款Tissot牌的手表。
精包装安全邮寄回国内。
  1. JDK的动态代理只能代理接口,不能代理具体的类,通过JDK生成的代理类:public final class $Proxy0 extends Proxy implements YourInterface {},Java只支持单继承,所以不能代理具体类了。

相比静态代理,当业务扩展或者参数变动的时候,我们不需要改动代理类,只需要生成相应的代理实例,并传入接口需要的参数就好了,这样扩展性和维护性大大的提高了。


动态代理源码的简单分析

debug跟进生成的代理类 请输入图片描述

这个代理类的名字很特殊,跟一下源码 请输入图片描述

跟进 Class<?> cl = getProxyClass0(loader, intfs); 请输入图片描述

跟进get方法 请输入图片描述

跟进apply方法 请输入图片描述

请输入图片描述

动态代理代理类提取

动态代理生成的代理类是存在于内存中的,通过上图中byte[] proxyClassFile = ProxyGenerator.generateProxyClass(proxyName, interfaces, 17);这段代码从内存中生成代理类的字节码文件,然后通过反射来创建的代理类的对象,来实现对目标方法的增强,下面就将这个内存中的东西写到磁盘中。

1. 创建IO测试方法:

    public static void getProxyClass(String proxyName, Class<?>[] interfaces, int accessFlags) {

        File file = new File("D:/", "$Proxy0.class");  //创建文件对象
        try {
            if (!file.exists()) {               //如果文件不存在则新建文件
                file.createNewFile();

            }
            FileOutputStream output = new FileOutputStream(file);

            byte[] proxyClassFile = ProxyGenerator.generateProxyClass(proxyName, interfaces, 17);

            output.write(proxyClassFile);                //将数组的信息写入文件中

            output.flush();
            output.close();
            System.out.println("输出成功:路径为:" + file.getPath());
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

2. 创建测试方法

args1:是导出来的代理类文件名字

args2:接口class的数组

args3:标志

    public static void main(String[] args) {
        Class<?>[] interfaces = {IBusinessA.class};
        IOTest.getProxyClass("$Proxy0",interfaces,1);
    }

3. 测试结果

下面就是反编译过来的代理类的源码,可见它已经继承了Proxy类,所以不能够再代理其他的类了,只能够代理接口,这就是JDK的动态代理。

import com.nmys.story.proxy.dynamicproxy.IBusinessA;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.lang.reflect.UndeclaredThrowableException;

public final class $Proxy0
  extends Proxy
  implements IBusinessA
{
  private static Method m1;
  private static Method m3;
  private static Method m2;
  private static Method m0;
  
  public $Proxy0(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 (Error|RuntimeException localError)
    {
      throw localError;
    }
    catch (Throwable localThrowable)
    {
      throw new UndeclaredThrowableException(localThrowable);
    }
  }
  
  public final void saleBag(String paramString)
    throws 
  {
    try
    {
      // h为-------> InvocationHandler 
      this.h.invoke(this, m3, new Object[] { paramString });
      return;
    }
    catch (Error|RuntimeException localError)
    {
      throw localError;
    }
    catch (Throwable localThrowable)
    {
      throw new UndeclaredThrowableException(localThrowable);
    }
  }
  
  public final String toString()
    throws 
  {
    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()
    throws 
  {
    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("com.nmys.story.proxy.dynamicproxy.IBusinessA").getMethod("saleBag", new Class[] { Class.forName("java.lang.String") });
      m2 = Class.forName("java.lang.Object").getMethod("toString", 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());
    }
  }
}