zoukankan      html  css  js  c++  java
  • 设计模式之动态代理

            在我的前一篇博文静态代理中,使用静态代理模式时,我们需要在程序加载执行前,手动的创建好每个目标对象类的代理类。这样就导致我们如果有很多个需要被代理的类时,就必须手动的去创建好所有的每一个代理类,这样就会生成很多的代理文件,不易于代理的维护和管理。有没有什么办法可以不用生成这么多代理类呢?动态代理

            一 什么是动态代理

            代理类在程序运行时才被创建出来的方式称为动态代理。在这种方式中,我们不需要在程序执行前去生成对应的代理类,只需要在程序执行过程中,根据我们传递的目标对象去动态的创建即可。相比较静态代理,这种方式不需要我们去手动创建大批量的文件,更易于代理对象及方法的统一管理维护,比如我们需要对代理类做方法增强时就不需要去修改每一个代理类。

            二 动态代理实现

      创建两个接口

    1 public interface ICatDao {
    2 
    3     /**
    4      * 描述自己
    5      */
    6     void desc();
    7 
    8 }
    1 public interface IDogDao {
    2 
    3     /**
    4      * 描述自己
    5      */
    6     void desc();
    7 
    8 }

      创建接口的实现类

    1 public class CatDaoImpl implements ICatDao {
    2     @Override
    3     public void desc() {
    4         System.out.println("<<-----------喵喵----------->>");
    5     }
    6 }
    1 public class DogDaoImpl implements IDogDao {
    2     @Override
    3     public void desc() {
    4         System.out.println("<<-----------旺旺----------->>");
    5     }
    6 }

      创建动态代理类实现 InvocationHandler接口(JDK动态代理必须实现这个接口),我们使用提供的目标对象调用getProxy方法来生成代理类,执行目标对象提供的方法时会通过反射执行invoke方法执行

     1 public class DynamicProxy implements InvocationHandler {
     2 
     3     /**
     4      * 目标对象
     5      */
     6     private Object target;
     7 
     8     public DynamicProxy() {
     9     }
    10 
    11     public Object getProxy(Object intf){
    12         this.target = intf;
    13         //args1:类加载器
    14         //args2:目标对象接口方法
    15         //InvocationHandler接口。所有动态代理类的方法调用,都会交由InvocationHandler接口实现类里的invoke()方法去处理
    16         return Proxy.newProxyInstance(this.getClass().getClassLoader(), target.getClass().getInterfaces(), this);
    17     }
    18 
    19     @Override
    20     public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    21         method.invoke(target, args);
    22         return null;
    23     }
    24 
    25 }

      测试,创建main方法生成代理类,执行方法

     1 public static void main(String[] args) {
     2 
     3         ICatDao catDao = new CatDaoImpl();
     4         ICatDao catDaoProxy = (ICatDao) new DynamicProxy().getProxy(catDao);
     5         catDaoProxy.desc();
     6 
     7         IDogDao dagDao = new DogDaoImpl();
     8         IDogDao dagDaoProxy = (IDogDao) new DynamicProxy().getProxy(dagDao);
     9         dagDaoProxy.desc();
    10 
    11     }

      执行结果如下,可以看到动态的生成了每一个目标对象,且可以正常执行

      如果我们需要对做方法增强,我们只需要修改invoke就可以了,如

    1 @Override
    2     public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    3         System.out.println("方法【"+method.getName()+"】要开始执行了");
    4         method.invoke(target, args);
    5         System.out.println("方法【"+method.getName()+"】已经执行结束");
    6         return null;
    7     }

      执行测试方法,结果如下,可以看到对所有的代理类的方法都做了增强

       三 源码分析

      代理对象类生成是调用Proxy类的newProxyInstance方法实现的,我们看看具体的方法

     1 /**
     2      *
     3      * @param loader 定义代理类的类加载器
     4      * @param interfaces 要实现的代理类的接口列表,不能超过 65535
     5      * @param h
     6      * @return
     7      * @throws IllegalArgumentException
     8      */
     9     @CallerSensitive
    10     public static Object newProxyInstance(ClassLoader loader,
    11                                           Class<?>[] interfaces,
    12                                           InvocationHandler h)
    13             throws IllegalArgumentException
    14     {
    15         // InvocationHandler 不能为空
    16         Objects.requireNonNull(h);
    17 
    18         final Class<?>[] intfs = interfaces.clone();
    19         final SecurityManager sm = System.getSecurityManager();
    20         if (sm != null) {
    21             //校验接口是否可以被访问
    22             checkProxyAccess(Reflection.getCallerClass(), loader, intfs);
    23         }
    24 
    25         //生成代理类
    26         Class<?> cl = getProxyClass0(loader, intfs);
    27 
    28         //用指定的InvocationHandler调用其构造函数
    29         try {
    30             if (sm != null) {
    31                 //对代理类进行权限校验
    32                 checkNewProxyPermission(Reflection.getCallerClass(), cl);
    33             }
    34 
    35             //根据参数生成构造器
    36             final Constructor<?> cons = cl.getConstructor(constructorParams);
    37 
    38             final InvocationHandler ih = h;
    39 
    40             if (!Modifier.isPublic(cl.getModifiers())) {
    41                 AccessController.doPrivileged(new PrivilegedAction<Void>() {
    42                     public Void run() {
    43                         cons.setAccessible(true);
    44                         return null;
    45                     }
    46                 });
    47             }
    48 
    49             //使用构造器生成实例
    50             return cons.newInstance(new Object[]{h});
    51 
    52         } catch (IllegalAccessException|InstantiationException e) {
    53             throw new InternalError(e.toString(), e);
    54         } catch (InvocationTargetException e) {
    55             Throwable t = e.getCause();
    56             if (t instanceof RuntimeException) {
    57                 throw (RuntimeException) t;
    58             } else {
    59                 throw new InternalError(t.toString(), t);
    60             }
    61         } catch (NoSuchMethodException e) {
    62             throw new InternalError(e.toString(), e);
    63         }
    64     }
    1 private static Class<?> getProxyClass0(ClassLoader loader,
    2                                            Class<?>... interfaces) {
    3         if (interfaces.length > 65535) {
    4             throw new IllegalArgumentException("interface limit exceeded");
    5         }
    6 
    7         //如果实现给定接口的给定加载器定义的代理类存在,那么它将返回缓存的副本; 否则,它将通过ProxyClassFactory创建代理类
    8         return proxyClassCache.get(loader, interfaces);
    9     }
    由于个人能力有限,难免有错误之处,敬请读者指正,不胜感激。
  • 相关阅读:
    Ajax三
    Ajax二
    【Verilog】组合逻辑写法
    【电路】LVDS 差分接口
    【C】数据类型定义
    【Flash】nv-ddr2接口Flash的ODT
    【vivado】clocking wizard 时钟配置
    【Linux】linux学习资料
    【Linux】ubuntu系统安装及软件依赖库
    【vivado】PL通过axi_hp接口控制PS的DDR
  • 原文地址:https://www.cnblogs.com/love-wzy/p/10175151.html
Copyright © 2011-2022 走看看