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

    静态代理

    1)定义一个接口

    package com.imooc.proxy;
    
    public interface Moveable {
        void move();
    }

    2)被代理类

    package com.imooc.proxy;
    
    import java.util.Random;
    
    public class Car implements Moveable {
    
        @Override
        public void move() {
            //实现开车
            try {
                Thread.sleep(new Random().nextInt(1000));
                System.out.println("汽车行驶中....");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

    3)代理类

    日志代理:

    package com.imooc.proxy;
    
    public class CarLogProxy implements Moveable {
    
        public CarLogProxy(Moveable m) {
            super();
            this.m = m;
        }
    
        private Moveable m;
        
        @Override
        public void move() {
            System.out.println("日志开始....");
            m.move();
            System.out.println("日志结束....");
        }
    
    }

    记时代理

    package com.imooc.proxy;
    
    public class CarTimeProxy implements Moveable {
    
        public CarTimeProxy(Moveable m) {
            super();
            this.m = m;
        }
    
        private Moveable m;
        
        @Override
        public void move() {
            long starttime = System.currentTimeMillis();
            System.out.println("汽车开始行驶....");
            m.move();
            long endtime = System.currentTimeMillis();
            System.out.println("汽车结束行驶....  汽车行驶时间:" 
                    + (endtime - starttime) + "毫秒!");
        }
    
    }

    测试类:

    package com.imooc.proxy;
    
    public class Client {
    
        /**
         * 测试类
         */
        public static void main(String[] args) {
            Car car = new Car();
            CarLogProxy clp = new CarLogProxy(car);
            CarTimeProxy ctp = new CarTimeProxy(clp);
            ctp.move();
        }
    
    }

    结果:

    **聚合方式要优于继承方式

    继承:继承父类想要的功能

    聚合:传入参数,实现想要的父类

    如果为继承方式,如果日志和时间的位置要调换,那么需要新定义一个子类

    而继承方式只需要在测试类中,调换位置就行

    动态代理:

    动态代理不必为特定的对象与方法编写特定的代理对象,使用动态代理,可以使得一个处理折Handler服务于各个对象

    接口被代理类不用改变

    时间代理类:!!继承InvocationHandler的接口

    package com.imooc.proxy;
    
    import java.lang.reflect.InvocationHandler;
    import java.lang.reflect.Method;
    import java.lang.reflect.Proxy;
    
    public class TimeProxyHandler implements InvocationHandler {
    
        private Object target;
        public TimeProxyHandler(Object target) {
            super();//调用父类的构造方法
            this.target = target;
        }
        
        /*
         * 参数:
         * proxy  被代理对象
         * method  被代理对象的方法
         * args 方法的参数
         * 
         * 返回值:
         * Object  方法的返回值
         * */
        @Override
        public Object invoke(Object proxy, Method mothod, Object[] args) throws Throwable {
            System.out.println("运行开始");
            long starttime = System.currentTimeMillis();
            mothod.invoke(target);
            long endtime = System.currentTimeMillis();
            System.out.println("运行结束,运行时间为:"+(endtime-starttime)+"毫秒");
            return null;
        }
        
        //获得代理对象
        public static Object getTimeProxy(Object object){
            InvocationHandler h = new TimeProxyHandler(object);
            Class<?> cls = object.getClass();
            /**
             * loader  类加载器
             * interfaces  实现接口
             * h InvocationHandler
             **/
            return Proxy.newProxyInstance(cls.getClassLoader(),
                                                    cls.getInterfaces(), h);
            
        }
    }

    测试类

    package com.imooc.proxy;
    
    public class Test {
            /**
             * JDK动态代理测试类
             */
            public static void main(String[] args) {
                Moveable m = (Moveable) TimeProxyHandler.getTimeProxy(new Car());
    /
    /返回代理类,代理类是JVM在内存中动态创建的,该类实现传入的接口数组的全部接口(的全部方法).
           m.move(); 
    }
    }

    InvocationHandler角色:

    代理模式:

    代理类处理的逻辑很简单:在调用某个方法前及方法后做一些额外的业务。

    动态代理工作的基本模式就是将自己的方法功能的实现交给 InvocationHandler角色,外界对Proxy角色中的每一个方法的调用,Proxy角色都会交给InvocationHandler来处理,而InvocationHandler则调用具体对象角色的方法。如下图所示:

     

    在这种模式之中:代理Proxy 和RealSubject 应该实现相同的功能,这一点相当重要。(我这里说的功能,可以理解为某个类的public方法)

    在面向对象的编程之中,如果我们想要约定Proxy 和RealSubject可以实现相同的功能,有两种方式:

        a.一个比较直观的方式,就是定义一个功能接口,然后让Proxy 和RealSubject来实现这个接口。(JDK机制)

        1.   获取 RealSubject上的所有接口列表;
        2.   确定要生成的代理类的类名,默认为:com.sun.proxy.$ProxyXXXX ;

        3.   根据需要实现的接口信息,在代码中动态创建 该Proxy类的字节码;

        4 .  将对应的字节码转换为对应的class 对象;

        5.   创建InvocationHandler 实例handler,用来处理Proxy所有方法调用;

        6.   Proxy 的class对象 以创建的handler对象为参数,实例化一个proxy对象

        b.还有比较隐晦的方式,就是通过继承。因为如果Proxy 继承自RealSubject,这样Proxy则拥有了RealSubject的功能,Proxy还可以通过重写RealSubject中的方法,来实现多态。(cglib机制)

    1.   查找A上的所有非final 的public类型的方法定义;

    2.   将这些方法的定义转换成字节码;

    3.   将组成的字节码转换成相应的代理的class对象;

    4.   实现 MethodInterceptor接口,用来处理 对代理类上所有方法的请求(这个接口和JDK动态代理InvocationHandler的功能和角色是一样的)

    CGLib采用了非常底层的字节码技术,其原理是通过字节码技术为一个类创建子类,并在子类中采用方法拦截的技术拦截所有父类方法的调用,顺势织入横切逻辑。

    两者区别:

    java动态代理是利用反射机制生成一个实现代理接口的匿名类,在调用具体方法前调用InvokeHandler来处理。

    而cglib动态代理是利用asm开源包,对代理对象类的class文件加载进来,通过修改其字节码生成子类来处理。

     

  • 相关阅读:
    每日日报2020.12.1
    每日日报2020.11.30
    981. Time Based Key-Value Store
    1146. Snapshot Array
    565. Array Nesting
    79. Word Search
    43. Multiply Strings
    Largest value of the expression
    1014. Best Sightseeing Pair
    562. Longest Line of Consecutive One in Matrix
  • 原文地址:https://www.cnblogs.com/L-a-u-r-a/p/8529245.html
Copyright © 2011-2022 走看看