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

    一、概念

      1、定义

      为其他对象提供一种代理以控制对这个对象的访问,在某些情况下,一个对象不适合或者不能直接引用另一个对象,而代理对象可以在客户端和目标对象之间起到中介的作用。

      2、优点

    • 真实的角色就是实现实际的业务逻辑,不用关心其他非本职责的事物。
    • 代理对象可以在客户端和目标对象之间起到中介作用、
    • 高扩展性

    二、代理模式组成

      在代理模式中,通常涉及到下面4中角色:

      ISubject:该接口是对被访问者或被访问资源的抽象

      SubjectImpl:被访问者的具体实现类

      SubjectProxy:被访问者的代理实现类,SubjectProxy持有SubjectImpl的实例

      Client:代表访问者的角色,Client将会访问ISubject类型的对象或资源,Client必须通过ISubject资源的访问代理类SubjectProxy进行

    三、代理模式实现方式

      1、静态代理

      SubjectImpl和SubjectProxy都实现了相同的接口,而SubjectProxy内部持有SubjectImpl的引用,当Client通过request()请求服务的时候,SubjectProxy将请求转发给SubjectImpl。在将请求转发给被代理对象SubjectImpl之前或者之后,都可以根据情况插入其他处理逻辑,比如在转发之前记录方法执行开始时间,在转发之后记录结束时间。示例代码如下:

    /*
     * ISubject角色
     */
    public interface Aithmetic
    {
         public int add(int i,int j);
         public int sub(int i,int j);
    }
    /**
     * ISubjectImpl角色
     */
    public class AithmeticImpl implements Aithmetic
    {
        public int add(int i, int j)
        {
            int result=i+j;
            return result;
        }
        public int sub(int i, int j)
        {    
            int result=i-j;
            return result;
        }
    }
    /*
     * SubjectProxy角色
     */
    public class AithmeticProxy implements Aithmetic
    {
        private Aithmetic aithmetic;
        public AithmeticProxy(Aithmetic aithmetic)
        {
            this.aithmetic=aithmetic;
        }
        public int add(int i, int j)
        {
            //加入自己的逻辑
            System.out.println("自定义逻辑开始");
            int sum=aithmetic.add(i, j);
            return sum;
        }
        public int sub(int i, int j)
        {
            System.out.println("自定义逻辑开始");
            int sub=aithmetic.sub(i, j);
            return sub;
        }
    }
    /*
     * Client角色
     */
    public class AithmeticProxyTest
    {
        @Test
        public void test()
        {
            Aithmetic aithmetic=new AithmeticImpl();
            AithmeticProxy aithmeticProxy=new AithmeticProxy(aithmetic);
            int sum=aithmeticProxy.add(2, 3);
            System.out.println(sum);
        }
    }

      

      静态代理的缺点:针对不一样的目标对象类型,我们要为其单独实现一个代理对象,而实际上,这些代理对象所要添加的横切逻辑是一样的。

      2、动态代理

      我们利用动态代理机制,可以为指定的接口在系统运行期间动态的生成代理对象。

      动态代理机制的实现主要由一个类和一个接口组成,即Java.lang.reflect.Proxy和java.lang.reflect.InvocationHandler接口,下面给出示例为两种不同的类型提供代理对象。

      假设现在有两个接口,都有request方法,我们想在request方法执行之前加入自己的处理逻辑。两个接口如下:

    public interface Aithmetic
    {
         public int add(int i,int j);
         public int sub(int i,int j);
         public void request();
    }
    public interface IRequestable
    {
         public void request();
    }
    public class AithmeticImpl implements Aithmetic
    {
        public int add(int i, int j)
        {
            int result=i+j;
            return result;
        }
        public int sub(int i, int j)
        {    
            int result=i-j;
            return result;
        }
        public void request()
        {
            System.out.println("执行AithmeticImpl的request函数");
        }  
    }
    public class IRequestableImpl implements IRequestable
    {
    
        public void request()
        {
            System.out.println("执行IRequestableImpl的request函数");
        }
    }

      为了达到目的,我们需要实现一个InvocationHandler:

    public class MyInvocationHandler implements InvocationHandler
    {
    
        private Object target;
        public MyInvocationHandler(Object target)
        {
            this.target=target;
        }
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable
        {
            if(method.getName().equals("request"))
            {
                System.out.println("加入自己的逻辑");
            }
            return method.invoke(target, args);
        }
    }

      然后就可以使用Proxy类,根据MyInvocationHandler的逻辑,为Aithmetic和IRequestable两种类型生成相应的代理对象实例:

    public class MyInvocationHandlerTest
    {
        @Test
        public void test()
        {
            Aithmetic aithmetic=(Aithmetic) Proxy.newProxyInstance(MyInvocationHandlerTest.class.getClassLoader(), new Class[]{Aithmetic.class}, new MyInvocationHandler(new AithmeticImpl()));
            aithmetic.request();
            System.out.println(aithmetic.add(2,3));
            
            IRequestable iRequestable=(IRequestable) Proxy.newProxyInstance(MyInvocationHandlerTest.class.getClassLoader(), new Class[]{IRequestable.class}, new MyInvocationHandler(new IRequestableImpl()));
            iRequestable.request();
        }
    }

      执行结果为:

      

      动态代理的缺点:动态代理的只能对实现了相应的接口的类使用,如果某个类没有实现任何接口,就无法使用 动态代理机制为其生成相应的动态代理对象。

      3、动态字节码生成

      原理:我们可以对目标对象进行继承扩展,为其生成相应的子类,而子类可以通过覆写来扩展父类的行为,只要将横切逻辑的实现放在子类中,然后让系统使用扩展后的目标对象的子类,就可以达到与代理模式相同的效果了。我们借助于CGLIB这样的动态字节码生成库,在系统运行期间为目标对象生成相应的扩展子类。

      注:需要用到以下三个包

      

      下面是示例代码:

      首先编写需要代理的目标对象:

    public class TargetObject
    {
        public int add(int i, int j)
        {
            int result = i + j;
            return result;
        }
    }

      创建cglib代理的工厂类:

    public class CglibProxy implements MethodInterceptor  
    {
         //设置需要创建子类的类
        private Object targetObject;
        public Object createProxyInstance(Object target)
        {
            this.targetObject = target;
            Enhancer enhancer = new Enhancer();
            enhancer.setSuperclass(this.targetObject.getClass());
            //设置回调函数
            enhancer.setCallback(this);
            //通过字节码技术动态创建子类实例
            return enhancer.create();     
        }
        public Object intercept(Object obj, Method method, Object[] args, MethodProxy methodProxy) throws Throwable
        {
            /*obj:目标类实例
             *method:目标类法方法的反射对象
             *args:方法的动态入参
             *methodProxy:代理类实例 
            */
            System.out.println("加入自己的逻辑");        
            //通过代理类调用父类中的方法
            Object result = methodProxy.invoke(targetObject, args);        
            System.out.println("操作结束");
            return result;
        }
        
    }

      测试:

    public class CglibProxyTest
    {
        @Test
        public void test()
        {
             CglibProxy cglibProxy=new CglibProxy();
             TargetObject t=(TargetObject) cglibProxy.createProxyInstance(new TargetObject());
             System.out.println(t.add(2, 3));
        }
    }

      执行结果:

      

      注:CGLIB是针对类实现代理,主要是对指定的类生成一个子类,覆盖其中的方法。因为是继承,所以该类或方法不要声明成final。

  • 相关阅读:
    1-27 awk 基本使用
    计算机网络(一)带宽
    CRC检错技术原理
    Wireshark漫谈(一)
    SQL字符串拼接
    MySQL学习笔记(二)
    MySQL学习笔记(一)
    Java 反射机制(二)
    Java 反射机制(一)
    Windows使用MySQL数据库管理系统中文乱码问题
  • 原文地址:https://www.cnblogs.com/xujian2014/p/5616828.html
Copyright © 2011-2022 走看看