zoukankan      html  css  js  c++  java
  • 反射与代理设计模式

    一.代理设计模式
      代理设计模式是在程序开发之中使用最多的设计模式,代理设计模式的核心是有真实业务实现类与代理业务实现类,并且代理类要完成比真实业务更多的处理操作.
    --传统代理设计模式的弊端:所有的代理模式如果按照要求来讲,必须是基于接口的设计,也就是说需要首先定义出核心接口的组成.模拟一个消息发送的代理操作结构:

     1 package 反射.反射与代理设计模式;
     2 
     3 /**
     4  * @author : S K Y
     5  * @version :0.0.1
     6  */
     7 interface IMessage { //传统代理设计必须有接口
     8     public void send();     //业务方法
     9 
    10 }
    11 
    12 class MessageReal implements IMessage {
    13     @Override
    14     public void send() {
    15         System.out.println("发送消息.....");
    16     }
    17 }
    18 
    19 class MessageProxy implements IMessage {     //代理类
    20     private IMessage message;       //代理的核心对象,一定是业务接口示例
    21 
    22     public MessageProxy(IMessage message) {
    23         this.message = message;
    24     }
    25 
    26     public boolean connect() {
    27         System.out.println("[消息代理]进行消息发送通道的连接.");
    28         return true;
    29     }
    30 
    31     @Override
    32     public void send() {
    33         if (this.connect()) {
    34             this.message.send();        //消息的发送处理
    35             this.close();
    36         }
    37     }
    38 
    39     public void close() {
    40         System.out.println("[消息代理]关闭消息通道");
    41     }
    42 }
    43 
    44 public class JavaTestDemo {
    45     public static void main(String[] args) {
    46         IMessage msg = new MessageProxy(new MessageReal())
    47         msg.send();
    48     }
    49 }

    --运行结果

    [消息代理]进行消息发送通道的连接.
    发送消息.....
    [消息代理]关闭消息通道
    
    Process finished with exit code 0

    --以上的操作代码是最为标准的代理设计,但是如果要进一步的思考会发现客户端的接口与具体子类产生了耦合问题,所以这样的操作从实际的开发来将最好再引入工厂设计模式进行代理对象的获取.
    --以上的代理设计模式为静态代理设计,这种静态代理模式设计的特点在于:一个代理类只为一个接口服务,如果现在准备出了3000个业务接口,则按照此种做法就意味着需要编写3000个代理类,并且这3000个代理类操作形式类似.所以现在需要解决的问题在于,如何让一个代理类满足所有的代理服务要求,即使用动态代理设计模式.

    二.动态代理设计模式
      通过静态代理设计模式的缺陷可以发现最好的做法是为所有功能一致的业务操作接口提供有统一的代理处理操作,而这可以通过动态代理机制来实现,但是在动态代理机制里面需要考虑到如下几点问题:
    --1.不管是动态代理类还是静态代理类,都一定要接受真实业务实现子类对象;
    --2.由于动态代理类不再与某一个具体的接口进行捆绑,所以应该可以动态获取类的接口信息:所有的真实业务对象都可以通过反射获取接口;
    --在进行动态代理实现的操作之中,首先需要关注的一个就是InvocationHandler接口,这个接口中规定了代理方法的执行

     1 class DynamicProxyP implements InvocationHandler {
     2     /**
     3      * 代理方法调用,代理主题里面执行的方法最终都是此方法
     4      * @param proxy 要代理的对象
     5      * @param method 要执行的接口方法名称
     6      * @param args 传递的参数
     7      * @return 某一个方法的返回值
     8      * @throws Throwable    方法调用时产生的错误,继续向上抛出
     9      */
    10     @Override
    11     public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    12         return null;
    13     }
    14 }

    --在进行动态代理设计的时候,对于动态对象的创建是由JVM底层完成的,此时主要依靠的是java.lang.reflect.Proxy程序类,在这个程序类之中只提供了一个核心的方法:public static Object newProxyInstance(ClassLoader loader,Class<?>[]interfaces,InvocationHandler h)throws IllegalArgumentException:
      ClassLoader loader:  获取当前真实主体类的ClassLoader;
      Class<?>[]interfaces:  代理是围绕接口进行的,一定要获取真实主体类的接口信息
      InvocationHandler h:  代理处理的方法

     1 package 反射.反射与代理设计模式;
     2 
     3 import java.lang.reflect.InvocationHandler;
     4 import java.lang.reflect.Method;
     5 import java.lang.reflect.Proxy;
     6 
     7 /**
     8  * @author : S K Y
     9  * @version :0.0.1
    10  */
    11 interface IMessage { //传统代理设计必须有接口
    12     public void send();     //业务方法
    13 
    14 }
    15 
    16 class MessageReal implements IMessage {
    17     @Override
    18     public void send() {
    19         System.out.println("发送消息.....");
    20     }
    21 }
    22 
    23 class DynamicProxy implements InvocationHandler {
    24     private Object target;      //保存真实业务对象
    25 
    26     /**
    27      * 进行真实业务对象与代理代理业务对象之间的绑定处理
    28      *
    29      * @param target 真实业务对象
    30      * @return Proxy生成的代理业务对象
    31      */
    32     public Object bind(Object target) {
    33         this.target = target;
    34         return Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), this);
    35     }
    36 
    37     public boolean connect() {
    38         System.out.println("[消息代理]进行消息发送通道的连接");
    39         return true;
    40     }
    41 
    42     public void close() {
    43         System.out.println("[消息代理]关闭消息通道");
    44     }
    45 
    46     /**
    47      * 代理方法调用,代理主题里面执行的方法最终都是此方法
    48      *
    49      * @param proxy  要代理的对象
    50      * @param method 要执行的接口方法名称
    51      * @param args   传递的参数
    52      * @return 某一个方法的返回值
    53      * @throws Throwable 方法调用时产生的错误,继续向上抛出
    54      */
    55     @Override
    56     public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    57         System.out.println("[执行方法] " + method);
    58         Object returnData = null;
    59         if (this.connect()) {
    60             returnData = method.invoke(this.target, args);
    61             this.close();
    62         }
    63         return returnData;
    64     }
    65 
    66 }
    67 
    68 public class JavaTestDemo {
    69     public static void main(String[] args) {
    70         IMessage message = (IMessage) new DynamicProxy().bind(new MessageReal());
    71         message.send();
    72     }
    73 }

    --运行结果

    [执行方法] public abstract void 反射.反射与代理设计模式.IMessage.send()
    [消息代理]进行消息发送通道的连接
    发送消息.....
    [消息代理]关闭消息通道
    
    Process finished with exit code 0

    --如果认真观察系统中提供的Proxy.newProxyInstance()方法,会发现该方法会使用大量的底层机制来实现代理对象的动态创建,所有的代理类是符合所有相关功能需求操作的功能类,他不再代表具体的接口,这样在处理的时候就必须依赖与类加载器与接口进行代理对象的伪造.

    三.CGLIB实现代理设计模式
      从java的官方来讲,已经明确的要求了如果想要实现代理设计模式,那么一定是基于接口的应用,因此使用Proxy类来创建代理对象时都需要传递该对象所有的接口信息.但是这时有一部分的开发者认为不应该强迫性的基于接口来实现代理设计.所以一些开发者就开发出了CGLIB的开发包利用这个开发包就可以实现基于类的代理设计模式.
      1.CGLIB是一个第三方的程序包,需要单独进行配置 下载地址:https://mvnrepository.com/artifact/cglib/cglib-nodep/3.2.6
      2.编写一个程序类,该类不实现任何接口.
      3.利用CGLIB编写代理类,但是这个代理类需要做一个明确,此时相当于使用了类的形式实现了代理设计的处理,所以该代理设计需要通过CGLIB来生成代理对象.定义一个代理类:

     1 class Message {
     2     public void send() {
     3         System.out.println("发送消息....");
     4     }
     5 }
     6 
     7 class CGLIBProxy implements MethodInterceptor {  //拦截器
     8     private Object target;      //保存真实代理对象
     9 
    10     public CGLIBProxy(Object target) {
    11         this.target = target;
    12     }
    13 
    14     public boolean connect() {
    15         System.out.println("[消息代理]进行消息发送通道的连接");
    16         return true;
    17     }
    18 
    19     public void close() {
    20         System.out.println("[消息代理]关闭消息通道");
    21     }
    22 
    23     @Override
    24     public Object intercept(Object proxy, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
    25         Object returnData = null;
    26         if (this.connect()) {
    27             method.invoke(this.target, objects);
    28             this.close();
    29         }
    30         return returnData;
    31     }
    32 }

    --此时如果要创建代理类对象,则就必须进行一系列的CGLIB处理.

    public class CGLIBDemo {
        public static void main(String[] args) {
            Message realObject = new Message();     //真实主题对象
            Enhancer enhancer = new Enhancer();     //负责代理操作的程序类
            enhancer.setSuperclass(realObject.getClass());      //假定一个父类
            enhancer.setCallback(new CGLIBProxy(realObject));       //设置代理类
            Message proxyObject = (Message) enhancer.create();//常见代理对象
            proxyObject.send();
        }
    }

    --运行结果

    [消息代理]进行消息发送通道的连接
    发送消息....
    [消息代理]关闭消息通道
    
    Process finished with exit code 0

    --在进行代理设计模式定义的时候除了可以使用接口之外,也可以不受接口的限制而实现基于类的代理设计,但是如果从正常的设计角度来讲,强烈建议基于接口的设计会比较合理.

  • 相关阅读:
    ASC1 C New Year Bonus Grant
    CodeForces 180C Letter
    Codeforces 71C Round Table Knights
    GCPC2014 J Not a subsequence
    treeview自动从表中添加标题和列值做目录的方法2
    delphi学习treeview中从表列名和数据添加为目录并双击自动选中
    对一个表中所有列数据模糊查询adoquery
    单击dbgrid列标题排序 升降序
    从右键菜单里粘贴复制的工资到数据库里
    如何从右键弹出菜单中清空删除数据加清空前提问确定
  • 原文地址:https://www.cnblogs.com/skykuqi/p/11443517.html
Copyright © 2011-2022 走看看