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方法是目标接口的。

    总结

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

  • 相关阅读:
    IOS Charles(代理服务器软件,可以用来拦截网络请求)
    Javascript中addEventListener和attachEvent的区别
    MVC中实现Area几种方法
    Entity Framework Code First 中使用 Fluent API 笔记。
    自定义JsonResult解决 序列化类型 System.Data.Entity.DynamicProxies 的对象时检测到循环引用
    序列化类型 System.Data.Entity.DynamicProxies 的对象时检测到循环引用
    An entity object cannot be referenced by multiple instances of IEntityChangeTracker 的解决方案
    Code First :使用Entity. Framework编程(8) ----转发 收藏
    Code First :使用Entity. Framework编程(6) ----转发 收藏
    Code First :使用Entity. Framework编程(5) ----转发 收藏
  • 原文地址:https://www.cnblogs.com/strongmore/p/13437111.html
Copyright © 2011-2022 走看看