zoukankan      html  css  js  c++  java
  • jdk实现动态代理

    介绍

    代理是一种设计模式,提供了对目标对象另外的访问方式,通过代理对象来访问目标对象,这样可以扩展目标对象的功能,对目标对象功能做控制。
    类图如下

    静态代理

    定义一个目标对象和代理对象都需要实现的接口

    /**
     * 可以唱歌的
     */
    public interface Singable {
      /**
       * 唱歌
       */
      void sing();
    }
    
    

    目标对象

    /**
     * 歌手
     */
    public class Singer implements Singable {
      @Override
      public void sing() {
        System.out.println("I am singing...");
      }
    }
    

    代理对象

    /**
     * 歌手经纪人
     */
    public class SingerAgent implements Singable {
    
      private Singable delegate;
    
      public SingerAgent(Singable delegate) {
        this.delegate = delegate;
      }
    
      @Override
      public void sing() {
        System.out.println("before sing...");
        delegate.sing();
        System.out.println("after sing...");
      }
    }
    

    客户端调用

    public class Client {
      public static void main(String[] args) {
        Singer singer = new Singer();
        SingerAgent singerAgent = new SingerAgent(singer);
        singerAgent.sing();
      }
    }
    

    输出结果

    before sing...
    I am singing...
    after sing...
    

    这个例子就相当于我们想找周杰伦唱歌,必须先找周杰伦经纪人,经纪人负责费用等唱歌前以及唱歌后的工作,周杰伦就负责唱歌。
    静态代理的缺点:
    代理和目标对象必须实现相同的接口,接口有变化,目标对象和代理都要维护。为了解决这个缺点,我们可以使用动态代理。

    动态代理

    动态代理其实是可以不实现接口的,但jdk动态代理必须实现,后续的如cglib实现动态代理就不需要了。今天的例子为jdk动态代理。

    /**
     * 可以唱歌的
     */
    public interface Singable {
      /**
       * 唱歌
       */
      void sing();
    }
    
    /**
     * 歌手
     */
    public class Singer implements Singable {
      @Override
      public void sing() {
        System.out.println("I am singing...");
      }
    }
    

    动态代理处理

    public class SingerInvocationHandler implements InvocationHandler {
    
      private Object delegate;
    
      public SingerInvocationHandler(Object delegate) {
        this.delegate = delegate;
      }
    
    
      /**
       * 动态代理调用方法
       *
       * @param proxy  生成的代理对象
       * @param method 代理的方法
       * @param args   方法参数
       * @return
       * @throws Throwable
       */
      @Override
      public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("before sing ");
    // 目标对象方法调用
        Object ret = method.invoke(delegate, args);
        System.out.println("after sing ");
        return ret;
      }
    
      /**
       * 创建代理对象
       * @return
       */
      public Singable newProxyInstance() {
        return (Singable) Proxy.newProxyInstance(
            delegate.getClass().getClassLoader(),
            delegate.getClass().getInterfaces(),
            this);
      }
    }
    

    InvocationHandler 顾名思义为调用处理器,每一个代理对象实例都会有一个调用处理器对象。创建代理对象主要使用的时Proxy的newProxyInstance方法,
    方法参数依次为

    1. 加载代理类的类加载器
    2. 代理类需要实现的接口
    3. 调用处理器
    public class Client {
      public static void main(String[] args) {
       //保存创建的代理类
    System.setProperty("jdk.proxy.ProxyGenerator.saveGeneratedFiles", Boolean.TRUE.toString());
        Singer singer = new Singer();
        SingerInvocationHandler singerInvocationHandler = new SingerInvocationHandler(singer);
        Singable proxy = singerInvocationHandler.newProxyInstance();
        proxy.sing();
      }
    }
    

    输出结果为

    before sing 
    I am singing...
    after sing 
    

    生成的代理类为

    public final class $Proxy0 extends Proxy implements Singable {
      private static Method m1;
      private static Method m2;
      private static Method m3;
      private static Method m0;
    
      public $Proxy0(InvocationHandler var1) throws  {
        super(var1);
      }
    
      public final boolean equals(Object var1) throws  {
        try {
          return (Boolean)super.h.invoke(this, m1, new Object[]{var1});
        } catch (RuntimeException | Error var3) {
          throw var3;
        } catch (Throwable var4) {
          throw new UndeclaredThrowableException(var4);
        }
      }
    
      public final String toString() throws  {
        try {
          return (String)super.h.invoke(this, m2, (Object[])null);
        } catch (RuntimeException | Error var2) {
          throw var2;
        } catch (Throwable var3) {
          throw new UndeclaredThrowableException(var3);
        }
      }
    
      public final void sing() throws  {
        try {
          super.h.invoke(this, m3, (Object[])null);
        } catch (RuntimeException | Error var2) {
          throw var2;
        } catch (Throwable var3) {
          throw new UndeclaredThrowableException(var3);
        }
      }
    
      public final int hashCode() throws  {
        try {
          return (Integer)super.h.invoke(this, m0, (Object[])null);
        } catch (RuntimeException | Error var2) {
          throw var2;
        } catch (Throwable var3) {
          throw new UndeclaredThrowableException(var3);
        }
      }
    
      static {
    // 初始化方法
        try {
          m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object"));
          m2 = Class.forName("java.lang.Object").getMethod("toString");
          m3 = Class.forName("com.imooc.sourcecode.java.dynamicproxy.jdk.test2.Singable").getMethod("sing");
          m0 = Class.forName("java.lang.Object").getMethod("hashCode");
        } catch (NoSuchMethodException var2) {
          throw new NoSuchMethodError(var2.getMessage());
        } catch (ClassNotFoundException var3) {
          throw new NoClassDefFoundError(var3.getMessage());
        }
      }
    }
    

    jdk帮我们创建了4个方法,equals,toString,hashCode3个方法都是Object类的,sing方法是目标接口的。

    总结

    代理模式的本质就是控制对象访问。

  • 相关阅读:
    C#winForm调用WebService的远程接口
    [C#] 走进异步编程的世界
    新手浅谈C#Task异步编程
    C#操作XML方法详解
    C# 实现生产者消费者队列
    c#多线程同步之EventWaitHandle使用
    C# 队列(Queue)和 堆栈(Stack)
    UVA-11925 Generating Permutations (逆向思维)
    UVA-11491 Erasing and Winning (单调队列)
    UVA-12545 Bits Equalizer (贪心)
  • 原文地址:https://www.cnblogs.com/strongmore/p/13437111.html
Copyright © 2011-2022 走看看