• 设计模式-代理模式(Proxy Pattern)


    一、概念

    为目标对象提供一种代理,以控制对其的访问。代理对象相当于中介,可以去掉功能服务或者增加添加额外功能服务。
    举例
    今天我想去服装店A买件衣服,一般都是直接去A的实体店进行购买,但是今天下雨了不想出门,于是我就打开淘宝在A的官方旗舰店B把想要的衣服买了,那么B就是相当于A的一个代理。B又提供了其它一些服务,比如:七天无理由退换货、送货上门等(添加额外功能)。B又没有现场试穿等服务(去掉功能服务)。

    1.1 作用

    代理可以在不改变目标类原有代码的基础上拓展其功能。例如:对一些方法进行性能测试,添加操作日志等。AOP便是使用动态代理来实现拦截切入功能。

    二、分类

    2.1 静态代理

    2.1.1 特点:

    需要定义父类或者接口,代理对象和被代理对象需要同时继承父类或者实现该接口。

    2.1.2 缺点:

    每进行一个代理都要建立一个代理类,随着代理类增多会造成类膨胀。

    2.1.3 实现:

    有两种实现方式,继承与聚合,由于聚合的方式优于继承,这里只演示聚合的实现。
    创建接口:目标类和代理类同时实现该接口。

    public interface Moveable {
        void move();
    }
    

    目标对象

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

    代理对象

    public class CarProxy implements Moveable {
        private Car car;
    
        public CarProxy(Car car) {
            this.car = car;
        }
        @Override
        public void move() {
            long startTime = System.currentTimeMillis();
            System.out.println("汽车开始行驶......");
            car.move();
            long endTime = System.currentTimeMillis();
            System.out.println("汽车结束行驶......  汽车行驶时间:"+(endTime-startTime)+"毫秒!");
    
        }
    }
    

    测试

    public class ProxyTest {
        public static void main(String[] args) {
            Car car = new Car();
            Moveable proxy = new CarProxy(car);
            proxy.move();
        }
    }
    

    测试结果

    汽车开始行驶......
    汽车行驶中......
    汽车结束行驶......  汽车行驶时间:724毫秒!
    

    2.2 jdk动态代理

    2.2.1 特点:

    1. 代理目标必须实现一个或者多个接口。
    2. 代理对象的生成不需要实现接口。
    3. 代理对象通过JAVA的API动态生成。

    2.2.2 缺点:

    没有实现接口的类不能进行动态代理。

    2.2.3 实现:

    Java动态代理类位于java.lang.reflect包下,主要涉及以下两个类
    (1) public interface InvocationHandler:该接口定义了一个抽象方法:

    /**
     * @param proxy 代理类
     * @param method 被代理的方法
     * @param args 被代理方法的参数
     */
    public Object invoke(Object proxy, Method method, Object[] args)throws Throwable;
    

    该抽象方法在代理类中动态实现。
    (2)public class Proxy:该类为动态代理类,该类产生代理类的方法为:

     /**
         * @param loader 目标对象的类加载器
         * @param interfaces 目标对象的接口类型
         * @param h 事件处理函数,实现对目标对象的操作。
         */
        public static Object newProxyInstance(
                ClassLoader loader,
                Class<?>[] interfaces,
                InvocationHandler h) throws IllegalArgumentException
    

    创建接口

    public interface Moveable {
        void move();
    }
    

    目标对象

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

    代理工厂类

    public class TimeProxyFactory {
        //代理的目标对象
        private Object target;
    
        public TimeProxyFactory(Object target) {
            this.target = target;
        }
    
        //获取目标对象
        public Object getInstance() {
            return Proxy.newProxyInstance(
                    target.getClass().getClassLoader(),
                    target.getClass().getInterfaces(),
                    new InvocationHandler() {
                        @Override
                        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                            long startTime = System.currentTimeMillis();
                            System.out.println("汽车开始行驶......");
                            Object obj = method.invoke(target, args);
                            long endTime = System.currentTimeMillis();
                            System.out.println("汽车结束行驶......  汽车行驶时间:"+(endTime-startTime)+"毫秒!");
                            return obj;
                        }
                    });
        }
    }
    

    测试

    public class ProxyTest {
        public static void main(String[] args) {
            Moveable move = new Car();
            TimeProxyFactory factory = new TimeProxyFactory(move);
            Moveable proxy =(Moveable) factory.getInstance();
            proxy.move();
        }
    }
    

    测试结果

    汽车开始行驶......
    汽车行驶中......
    汽车结束行驶......  汽车行驶时间:123毫秒!
    

    2.3 CGLIB动态代理

    Cglib代理,也叫作子类代理,它是在内存中构建一个子类对象从而实现对目标对象功能的扩展.

    2.3.1 特点:

    1. Cglib是一个强大的高性能的代码生成包,它可以在运行期扩展java类与实现java接口.它广泛的被许多AOP的框架使用,例如Spring AOP和synaop,为他们提供方法的interception(拦截)
    2. Cglib包的底层是通过使用一个小而块的字节码处理框架ASM来转换字节码并生成新的类.不鼓励直接使用ASM,因为它要求你必须对JVM内部结构包括class文件的格式和指令集都很熟悉.
    3. 因为它是使用继承方式,所以不能对final修饰的类进行代理。
    4. 产生的代理类其实就是目标类的子类。

    2.3.2 实现:

    目标对象

    public class Train {
        public void move() {
            System.out.println("火车行驶中......");
        }
    }
    

    代理类

    public class CGLibProxy implements MethodInterceptor {
        //创建代理对象
        public Object getProxyInstance(Class c) {
            //Enhancer可以为接口或者非接口类型创建一个子类作为代理类,可以拦截所有方法
            Enhancer enhancer = new Enhancer();
            //设置创建子类的类,也就是说为每个代理类创建子类
            enhancer.setSuperclass(c);
            //设置回掉函数(因为MethodInterceptor继承了Callback类,默认执行intercept方法)
            enhancer.setCallback(this);
            //创建子类实例,返回去
            return enhancer.create();
        }
    
        /**
         * 拦截所有目标类方法的调用
         * @param o       目标类的实例
         * @param method  目标方法的反射对象
         * @param objects 目标方法的参数
         * @param proxy   代理类的实例
         */
        @Override
        public Object intercept(Object o, Method method, Object[] objects, MethodProxy proxy) throws Throwable {
    
            long startTime = System.currentTimeMillis();
            System.out.println("汽车开始行驶......");
            //代理类调用父类的方法,来实现代理类的方法
            Object returnObject = proxy.invokeSuper(o, objects);
            long endTime = System.currentTimeMillis();
            System.out.println("汽车结束行驶......  汽车行驶时间:"+(endTime-startTime)+"毫秒!");
            return returnObject;
        }
    }
    

    测试

    public class ProxyTest {
        public static void main(String[] args) {
            Train train = (Train) new CGLibProxy().getProxyInstance(Train.class);
            train.move();
        }
    }
    

    测试结果

    汽车开始行驶......
    火车行驶中......
    汽车结束行驶......  汽车行驶时间:297毫秒!
    

    三、小结

    代理方式特点缺点
    静态代理需要定义父类或者接口,代理对象和被代理对象需要同时继承父类或者实现该接口,一次代理一个类随着代理类增多,出现大量重复代码,难维护,造成类膨胀
    jdk动态代理目标类需要实现至少一个接口,代理对象通过JAVA的API动态生成,可以代理一个借口的多个实现只能够代理实现了接口的目标类
    cglib动态代理代理类要实现MethodInterceptor接口,通过Enhancer创建目标类的子类为代理对象,所有也是通过继承关系创建代理类的,然后通过实现intercept(Object o, Method method, Object[] objects, MethodProxy proxy)方法对所有的方法进行拦截,添加增强处理,注意该方法中要通过代理类的invokeSuper调用父类的方法不能代理final修饰的类
    只有把命运掌握在自己手中,从今天起开始努力,即使暂时看不到希望,也要相信自己。因为比你牛几倍的人,依然在努力。
  • 相关阅读:
    纷享销客公司产品能力学习笔记
    有质量的两道面试题
    java项目启动时执行指定方法
    css银行卡号样式
    swiper6使用鼠标滚轮失效退回swiper4即可
    vue-swiper Demo
    vue点击下载图片
    跨域请求发送数据在body里,java后台接收
    跨域,跨服务session获取不到,前后台不会传输Cookie,sessionId不一致
    Windows下 redis命令及配置
  • 原文地址:https://www.cnblogs.com/freesky168/p/14358232.html
Copyright © 2020-2023  润新知