zoukankan      html  css  js  c++  java
  • 代理模式

      在开发过程中,尤其现在注解开发过程中代理其实运用的非常广泛,它可以让我们可以更关注业务,其他的比如日志,事务,以及aop这些公共的东西交给代理类去做。本文就来介绍代理设计模式。

    一、代理模式概念

      代理模式的定义:为其他对象提供一种代理以控制对这个对象的访问。在某些情况下,一个对象不适合或者不能直接引用另一个对象,而代理对象可以在客户端和目标对象之间起到中介的作用。(来自百度百科的介绍)
      我觉得,代理就是让我们可以单纯的去关注业务,而把公共的方法交给代理类去处理,如果业务发生变化,而像一些公共的方法是不需要发生变化,我们只需要去修改业务方法就可以。

    二、代理模式的优缺点

    • 优点
      (1) .责任清晰,真实的角色就是实现实际的业务逻辑,不用关心其他非本职责的事务,通过后期的代理完成一件完成事务,附带的结果就是编程简洁清晰。
      (2) .代理对象可以在客户端和目标对象之间起到中介的作用,这样起到了中介的作用和保护了目标对象的作用。
      (3) .高扩展性
    • 缺点
      (1) .静态代理,如果代理对象多的话,则会增加代理类,加重了开发人员的工作量。
      (2) .静态代理由于需要和目标对象实现相同的接口,一旦更改了接口,那么目标对象和代理对象都要同时做出调整,不方便管理。

    三、代理模式的组成

    • 抽象角色:通过接口或抽象类声明真实角色实现的业务方法。
    • 代理角色:实现抽象角色,是真实角色的代理,通过真实角色的业务逻辑方法来实现抽象方法,并可以附加自己的操作。
    • 真实角色:实现抽象角色,定义真实角色所要实现的业务逻辑,供代理角色调用。

    四、静态代理

    下面写一个静态代理的例子:

    • 首先需要一个抽象角色(用于定义真实角色的动作)
    /**
     * 
     * @escription:此接口为代理模式的"抽象角色", 其中定义了真实角色与代理角色共同需要做的事情。
     * 此接口也可以定义为抽象类。
     * @author: Herrt灬凌夜
     * @date: 2018年11月24日 下午12:24:24
     */
    public interface AbstractRole {
        /**
         * 
         * @Title: businessMethod   
         * @Description: 此方法为真实角色和代理角色需要实现的业务方法
         * @param:       
         * @return: void      
         * @throws
         */
        public void businessMethod();
    }
    • 然后是真实角色(来实现抽象角色的动作)
    /**
     * @escription:(真实角色)
     * @author: Herrt灬凌夜
     * @date: 2018年11月24日 下午12:47:07 
     */
    public class RealRole implements AbstractRole {
        /**   
         * <p>Title: businessMethod</p>   
         * <p>Description: 真实角色所以执行的方法,只需要注重业务</p>      
         * @see proxy.AbstractRole#businessMethod()   
         */  
        public void businessMethod() {
            System.out.println("业务方法!");
        }
    }
    • 接下来是代理角色(来代理真实角色的动作,并且加上自己的一些动作)
    /**
     * @escription:(代理角色)
     * @author: Herrt灬凌夜
     * @date: 2018年11月24日 下午12:46:35 
     */
    public class ProxyRole implements AbstractRole{
        
        private RealRole realRole;
        /**
         * 
         * @Title:ProxyRole   
         * @Description:通过构造器初始化真实角色 
         * @param:@param realRole  
         * @throws
         */
        public ProxyRole(RealRole realRole) {
            super();
            this.realRole = realRole;
        }
    
        /**   
         * <p>Title: businessMethod</p>   
         * <p>Description: 代理角色执行的方法,在这里处理非业务,并且公共的东西,比如日志,事务等等。</p>      
         * @see proxy.AbstractRole#businessMethod()   
         */  
        public void businessMethod() {
            prior();
            realRole.businessMethod();
            after();
        }
    
        /**
         * 
         * @Title: prior   
         * @Description: 在businessMethod方法执行之前执行
         * @param:       
         * @return: void      
         * @throws
         */
        private void prior() {
            System.out.println("在真实角色执行方法之前执行");
        }
        
        /**
         * 
         * @Title: after   
         * @Description: 在businessMethod方法执行之后执行
         * @param:       
         * @return: void      
         * @throws
         */
        private void after() {
            System.out.println("在真实角色执行方法之后执行");
        }
    }
    • 最后是使用者
    /**
     * @escription:使用者,调用真实角色的方法
     * @author: Herrt灬凌夜
     * @date: 2018年11月24日 下午12:48:16 
     */
    public class Client {
        public static void main(String[] args) {
            RealRole realRole = new RealRole();
            ProxyRole proxyRole = new ProxyRole(realRole);
            proxyRole.businessMethod();
        }
    }

      执行结果如下:

    五、基于接口动态代理(jdk动态代理)

      在java的java.lang.reflect包下提供了一个Proxy类和一个InvocationHandler接口,通过这个类和这个接口可以生成JDK动态代理类和动态代理对象。

    • 首先还是创建抽象角色和真实角色,此处使用的还是上例中的AbstractRole和RealRole
    • 创建动态代理类
     1 /**
     2  * @escription:log的动态代理类
     3  * @author: Herrt灬凌夜
     4  * @date: 2018年11月24日 下午2:44:26 
     5  */
     6 public class LogDynamicProxy implements InvocationHandler{
     7 
     8     private Object target;
     9 
    10     /**   
    11      * @Title:LogDynamicProxy   
    12      * @Description: 将代理对象通过构造器传入
    13      * @param:@param target  
    14      * @throws   
    15      */  
    16     public LogDynamicProxy(Object target) {
    17         super();
    18         this.target = target;
    19     }
    20 
    21 
    22     /**
    23      * 获取代理类
    24      * @return the target
    25      */
    26     public Object getTarget() {
    27         return Proxy.newProxyInstance(this.getClass().getClassLoader(), target.getClass().getInterfaces(), this);
    28     }
    29 
    30     /**   
    31      * <p>Title: invoke</p>   
    32      * <p>Description: </p>   
    33      * @param proxy : 代理类
    34      * @param method : 代理类的 调用处理程序的方法对象
    35      * @param args : 参数
    36      * @return
    37      * @throws Throwable   
    38      * @see java.lang.reflect.InvocationHandler#invoke(java.lang.Object, java.lang.reflect.Method, java.lang.Object[])   
    39      */  
    40     public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    41         log(method.getName());
    42         return method.invoke(target, args);
    43     }
    44 
    45     private void log (String method) {
    46         System.out.println("执行" + method + "方法");
    47     }
    48 
    49 }
    • 创建使用者
     1 /**
     2  * @escription:使用者,调用真实角色的方法
     3  * @author: Herrt灬凌夜
     4  * @date: 2018年11月24日 下午12:48:16 
     5  */
     6 public class Client {
     7 
     8     public static void main(String[] args) {
     9         RealRole realRole = new RealRole();
    10         LogDynamicProxy logProxy = new LogDynamicProxy(realRole);
    11         AbstractRole abstractRole = (AbstractRole)logProxy.getTarget();
    12         abstractRole.businessMethod();
    13     }
    14 }

      下面对上述代码进行简单分析,首相我们在创建LogDynamicProxy时,调用其构造器,将真实角色传入在LogDynamicProxy的get方法中调用了Proxy类的newProxyInstance方法,这个方法是用于动态创建代理类的,其中有三个参数,第一个参数是代理类的类加载器ClassLoader,第二个是真实角色的接口数组,第三参数是代理类。我们简单看下其源码,就可以很容易的看到使用反射去创建代理类。

     1 @CallerSensitive
     2 public static Object newProxyInstance(ClassLoader loader,
     3                                           Class<?>[] interfaces,
     4                                           InvocationHandler h)throws IllegalArgumentException
     5     {
     6         //判断代理类是否为null
     7         Objects.requireNonNull(h);
     8         //克隆接口
     9         final Class<?>[] intfs = interfaces.clone();
    10         //权限检查
    11         final SecurityManager sm = System.getSecurityManager();
    12         if (sm != null) {
    13             checkProxyAccess(Reflection.getCallerClass(), loader, intfs);
    14         }
    15 
    16         /*
    17          * Look up or generate the designated proxy class.
    18          * 生成代理类,如果有直接从缓存中取,如果没有则生成
    19          */
    20         Class<?> cl = getProxyClass0(loader, intfs);
    21 
    22         /*
    23          * Invoke its constructor with the designated invocation handler.
    24          */
    25         try {
    26             if (sm != null) {
    27                 checkNewProxyPermission(Reflection.getCallerClass(), cl);
    28             }
    29             //动态获取构造
    30             final Constructor<?> cons = cl.getConstructor(constructorParams);
    31             final InvocationHandler ih = h;
    32             if (!Modifier.isPublic(cl.getModifiers())) {
    33                 AccessController.doPrivileged(new PrivilegedAction<Void>() {
    34                     public Void run() {
    35                         cons.setAccessible(true);
    36                         return null;
    37                     }
    38                 });
    39             }
    40             //返回代理对象
    41             return cons.newInstance(new Object[]{h});
    42         } catch (IllegalAccessException|InstantiationException e) {
    43             throw new InternalError(e.toString(), e);
    44         } catch (InvocationTargetException e) {
    45             Throwable t = e.getCause();
    46             if (t instanceof RuntimeException) {
    47                 throw (RuntimeException) t;
    48             } else {
    49                 throw new InternalError(t.toString(), t);
    50             }
    51         } catch (NoSuchMethodException e) {
    52             throw new InternalError(e.toString(), e);
    53         }
    54     }

      最后使用代理类去调用真实角色的方法,在调用时其实是调用的代理类的invoke方法,而这个invoke方法是重写了InvocationHandler接口的方法,这个方法有3个参数,第一个为代理类,第二个则为方法,第三为参数,在这个方法中我们动态的调用真实角色的方法,并且在其中加入一些log。
      所以说每个动态代理类,都可以去代理一类的业务,这样可以减轻程序员的代码量,而且可以更好的管理代理类。

    六、基于类动态代理(cglib动态代理)

      上面的jdk动态代理是实现接口的方式去实现动态代理,而接下来说的则是继承类去实现动态代理。
      要想使用cglib的动态代理,首先,我们需要去导一个cglib-nodep的jar包,下面是maven的坐标

    <!-- https://mvnrepository.com/artifact/cglib/cglib-nodep -->
    <dependency>
        <groupId>cglib</groupId>
        <artifactId>cglib-nodep</artifactId>
        <version>2.1_3</version>
    </dependency> 
    • 首先需要创建真实角色,这个还是使用上例中的RealRole类
    • 创建cglib代理类
     1 /**
     2  * @escription:cglig代理类
     3  * @author: Herrt灬凌夜
     4  * @date: 2018年11月25日 下午12:29:14 
     5  */
     6 public class LogCglibProxy  implements MethodInterceptor {
     7 
     8     /**   
     9      * <p>Title: intercept</p>   
    10      * <p>Description: 在Client中调用通过动态代理获取到的真实对象调用方法时,其实调用的是此方法</p>   
    11      * @param obj
    12      * @param method
    13      * @param args
    14      * @param proxy
    15      * @return
    16      * @throws Throwable   
    17      * @see net.sf.cglib.proxy.MethodInterceptor#intercept(java.lang.Object, java.lang.reflect.Method, java.lang.Object[], net.sf.cglib.proxy.MethodProxy)   
    18      */  
    19     public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
    20         log(method.getName());
    21         return proxy.invokeSuper(obj, args); //动态调用真实角色的方法
    22     }
    23 
    24     /**
    25      * @Title: getInstance   
    26      * @Description: 动态获取代理对象
    27      * @param: @param target 真实对象
    28      * @return 代理对象
    29      * @throws
    30      */
    31     public Object getInstance (Object target) {
    32         Enhancer enhancer = new Enhancer(); //创建加强器,用来创建动态代理类
    33         enhancer.setSuperclass(target.getClass()); //给加强器指定父类,即真实角色
    34         enhancer.setCallback(this); //设置回调,调用上面的 intercept 方法
    35         return enhancer.create();//动态创建代理类,并返回
    36     }
    37 
    38     private void log (String method) {
    39         System.out.println("执行" + method + "方法");
    40     }
    41
    • 创建使用者
     1 /**
     2  * @escription:使用者,调用真实角色的方法
     3  * @author: Herrt灬凌夜
     4  * @date: 2018年11月24日 下午12:48:16 
     5  */
     6 public class Client {
     7 
     8     public static void main(String[] args) {
     9         RealRole realRole = new RealRole();
    10         LogCglibProxy cglibProxy = new LogCglibProxy();
    11         RealRole cglibRealRole = (RealRole)cglibProxy.getInstance(realRole);
    12         cglibRealRole.businessMethod();
    13     }
    14

    对上面代码进行简单分析,首先创建真实角色,代理类,调用其getInstance方法,动态创建代理类,其中代码的含义已经在代码中备注,所以在此不再赘述。而在使用代理类调用真实角色方法时,其实在调用intercept方法,这个方法是重写了MethodInterceptor 接口的方法,可以在其中加入log的一些方法。

    -------------------- END ---------------------

     

    
    

     

    最后附上作者的微信公众号地址和博客地址 

     

    
    

     

    公众号:wuyouxin_gzh

     

    
    

     

     

    
    

     

     

     

    
    

     

    Herrt灬凌夜:https://www.cnblogs.com/wuyx/

  • 相关阅读:
    sql server中的左连接与右连接的简便写法
    SQL中CONVERT()转化函数的用法 字符串转日期
    Asp.net MVC 中Controller返回值类型ActionResult
    一探前端开发中的JS调试技巧
    String trim 坑 对于ascii码为160的去不掉
    SQL小练习
    Java运行时异常和非运行时异常
    java 子类不能继承父类的static方法
    Java中的类加载器
    搞懂head 和 tail 命令
  • 原文地址:https://www.cnblogs.com/wuyx/p/10012221.html
Copyright © 2011-2022 走看看