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

    代理模式定义

    为某个对象(目标对象)提供一种代理对象以控制对这个对象的访问。在某些情况下在(比如安全性问题),客户端不能直接访问某个对象(目标对象),还是通过代理对象间接访问目标对象,代理对象在客户端和目标对象之间起到中介的作用,而且可以通过代理对象对目标对象的功能进行扩展,代理对象一般对目标对象的非核心业务的扩展,比如权限控制问题,记录日志,事务控制等等。我们日常的生活的代理模式例子有房产中介,明星经纪人,火车票代售点等等,在工作中代理模式有windows快捷键,spring框架aop等等

    使用代理模式的原因

    • 中介隔离作用:在某些情况下,客户端不能直接访问某个对象(目标对象),还是通过代理对象间接访问目标对象,代理对象器中介的作用。
    • 开闭原则,增加功能:代理模式遵循设计模式的开闭原则,目标对象的功能的扩展是通过新增一个代理类,在代理类中进行扩展,而不是在目标类中修改代码,进行扩展。

    代理模式分类

    静态代理:创建一个接口,代理类,目标类实现同一个接口,代理类持有目标对象的一个引用。创建一个飞机类为例子,创建飞机的代理类,计算飞机移动的时间,记录飞机飞行过程的(日志)类图如下:

      

     代码如下:

    1:创建一个接口Movable接口(抽象类也行)

     /**
    * 该接口用来指定代理的方法(需要增加内容的方法),代理对象,被代理对象都该继承该接口 *
    @author Administrator * */ public interface Movable { public void move() throws InterruptedException; }

    2:创建目标类Plane,实现Movable接口

    /**
     * 需要代理的对象飞机类,实现movable接口
     * @author Administrator
     *
     */
    public class Plane implements Movable {
    
        public void move() throws InterruptedException {
            System.out.println("plane luanching...");
            Thread.sleep(new Random().nextInt((int) (1000)));
            
        }
    
    }

    3.1:创建一个时间代理类PlaneTimeProxy,实现movable接口,并持有一个Movable类型的引用。

    /**
     * 飞机类的时间代理对象,用来计算飞机飞行的时间,即是飞机对象move方法调用的时间。
     * @author Administrator
     *
     */
    public class PlaneTimeProxy implements Movable{
        Movable mover;
        public PlaneTimeProxy(Movable mover){
            this.mover=mover;
        }
        public void move() throws InterruptedException {
            // TODO Auto-generated method stub
            long begin=System.currentTimeMillis();
            mover.move();
            long end =System.currentTimeMillis();
            System.out.println("plane time:"+(end-begin));
            
        }
    
    }

     飞机飞行记录代理类PlaneLogProxy(日志)实现movable接口,并持有一个Movable类型的引用。

    /**
     * 飞机飞行过程记录代理类(日志)
     * @author Administrator
     *
     */
    public class PlaneLogProxy implements Movable {
        Movable mover;
        public PlaneLogProxy(Movable mover){
            this.mover=mover;
        }
        public void move() throws InterruptedException {
            System.out.println("plane begin");
            mover.move();
            System.out.println("plane end");
            
        }
    
    }

    4:客户端测试类

    /**
     *静态代理测试类
     */
    public class TestProxy {
        public static void main(String[] args) throws InterruptedException {
            Movable p=new Plane();
            //日志代理类
            PlaneLogProxy plp=new PlaneLogProxy(p);
            plp.move();
            //时间代理类型
            PlaneTimeProxy simplePtp =new PlaneTimeProxy(p);
            simplePtp.move();
            //时间代理类嵌套日志代理类
            PlaneTimeProxy ptp=new PlaneTimeProxy(plp);
            ptp.move();
        }
    }

    静态代理总结:使用静态代理对目标对象功能的扩展,比继承更加灵活,比如上面的例子,如果即需要记录飞机时间,记录飞行的日志,就需要再在新建一个类实现Movable接口,如果用静态代理则可以通过嵌套方式实现。静态代理的不足,1:如果一个目标类被多个类代理,则需要创建多个代理类,而且代理类的逻辑相似,重复代码写了多遍,2:如果在接口增加一个方法,则需要修改代理类跟目标类的逻辑,为了解决上面的问题就产生了动态代理了,动态代理有二种,一种是jdk代理,一种是cglib代理。

    jdk代理:代理对象是由jdk动态生成,不需要我们手动创建代理对象,代理对象是通过Proxy类的newProxyInstance方法创建,我们只需要创建一个类实现InvocationHandler接口,重写invoke方法对目标对象的功能进行增强。还是以上面的l例子的日志代理类为例,接口跟目标类已经有了,就不再在下面展示了,代码如下:

    创建一个类实现InvocationHandler接口,该类持有一个Movable类型的引用,目标对象的功能扩展通过重写invoke方法实现:

    public class MyHandler implements InvocationHandler{
        Movable mover;
        
        public MyHandler(Movable mover) {
            super();
            this.mover = mover;
        }
    
        /**
         *
         * @param proxy  第一个参数指的是jdk生成的代理对象
         * @param method 第二个参数指的是目标对象的目标方法
         * @param args  第三个参数指的是目标对象的目标方法的参数
         * @return  方法返回值为调用目标方法的返回值
         * @throws Throwable
         */
        public Object invoke(Object proxy, Method method, Object[] args)
                throws Throwable {
            System.out.println("plane begin");//新增的逻辑
            Object obj=method.invoke(mover, args);//调用目标对象的目标方法
            System.out.println("plane end");//新增的逻辑
            return obj;
        }
    
    }

    客户端测试类:

    /**
     * jdk动态代理:用反射技术,由jdk动态在内存生成代理对象。
     * @author Administrator
     *
     */
    public class TestJdkProxy {
        public static void main(String[] args) throws InterruptedException {
            Movable p= (Movable) new Plane();
            MyHandler h=new MyHandler(p);
            /**
             *通过Proxy类的newProxyInstance方法创建代理对象,newProxyInstance方法:第一个参数表示目标类的类加载器,
             * 第二个参数指定目标类实现的接口的字节码数组对象,
             * 第三个参数指的是实现目标方法扩展的InvocationHandler实现类。
             */
            Movable mover=(Movable) Proxy.newProxyInstance(Plane.class.getClassLoader(), new Class[]{Movable.class}, h);
            mover.move();
        }
    
    }

    jdk代理总结:jdk代理用反射技术,由jdk动态在内存生成代理对象,将创建代理对象的工作交给jdk,不需要自己创建各种代理类,减少了我们开发任务,当在接口中新增一个方法时,只需要在目标类中新增这个方法的逻辑,jdk代理类会自动新增这个方法,解决静态代理的不足。但是jdk代理也有不足,如果目标对象不实现接口,则无法产生代理对象,所以当目标类没有实现接口,想对目标类功能进行扩展,这时就需要用cglib代理实现。

    cglib代理:cglib代理也叫子类代理,通过在内存构建一个子类,从而实现目标对象功能的扩展,目标对象不需要实现接口,也就是说代理类是目标类的子类。cglig代理对象由Enhancer类来创建,cgliib代理只需要编写一个拦截器类实现MethodInterceptor接口,重写interceptor方法,在intercept方法中对目标对象的功能进行增强操作。还是以上面的列子的日志代理类为例,接口跟目标类已经有了,就不再在下面展示了,代码如下:

    创建一个拦截器类实现MethodInterceptor接口,持有一个Movable类型的引用,通过重写intercept方法来实现目标类功能的增强。

    public class MyInterceptor implements MethodInterceptor{
        Movable mover;
        
        public MyInterceptor(Movable mover) {
            this.mover = mover;
        }
        
        @Override
        public Object intercept(Object proxy, Method arg1, Object[] arg2,
                MethodProxy methodProxy) throws Throwable {
            System.out.println("plane begin");
            //Object obj=methodProxy.invoke(mover, arg2);
            Object obj=methodProxy.invokeSuper(proxy,arg2);
            System.out.println("plane end");
            return obj;
        }
    
    }

    客户端测试类:

    /**
     * cglib代理也叫子类代理,通过在内存构建一个子类,从而实现目标对象功能的扩展,目标对象不需要实现接口(当然实现也可以),本例实现了接口
     * 优点:1:解决了jdk动态代理的目标对象一定要实现接口的缺点。
     * 2:CGLIB 是一个强大的,高性能的代码生成库,它可以在运行期扩展java类。
     * 3:CGLIB 底层是使用小而快的字节码处理框架asm,来转换字节码,生成代理类。
     * @author Administrator
     *
     */
    public class TestCgLibProxy {
        public static void main(String[] args) throws InterruptedException {
            Enhancer enhancer=new Enhancer();//定义一个增强器
            enhancer.setSuperclass(Plane.class);//设置目标对象的父类为代理类的父类
            Movable m= (Movable) new Plane();
            //Plane m=new Plane();
            MyInterceptor callback=new MyInterceptor(m);//创建自定义拦截器对象
            enhancer.setCallback(callback);//设置回调对象即是拦截器
            Plane proxy=(Plane) enhancer.create();//创建代理对象
            proxy.move();
        }
    }

    cglib代理总结:cglib代理解决了jdk代理的目标类必须实现接口的缺点。一般当目标对象(被代理对象)没有实现接口,则用cglib代理,当目标对象实现某个接口时,如果创建的代理对象是单例对象并且目标类不被fianl修饰时,则选用cglib代理,因为cglib创建的态代理对象比JDK创建的动态代理对象的性能更高,但是CGLIB创建代理对象时所花费的时间却比JDK多得多,因为cglib采用继承方法产生代理对象,就产生的代理对象继承目标对象。final修饰的类不能继承的,继续否则就用jdk代理。

     
     
  • 相关阅读:
    P5956[POI2017]Podzielno【数学】
    P6672[清华集训2016]你的生命已如风中残烛【结论】
    P5825排列计数【EGF,NTT】
    P3971[TJOI2014]Alice and Bob【贪心】
    P3244[HNOI2015]落忆枫音【dp】
    jquery 选中单选按钮的值
    jquery ajax 详解
    Common Lisp中的car和cdr
    publishing(android)
    Preparing for Release(发布前的准备)
  • 原文地址:https://www.cnblogs.com/shareAndStudy/p/12820043.html
Copyright © 2011-2022 走看看