• 结构型设计模式


    代理模式介绍

    代理模式提供了对目标对象额外的访问方式,即通过代理对象访问目标对象,这样可以在不修改原目标对象的前提下,提供额外的功能操作,扩展目标对象的功能。

    代理模式分为三类:

    • 静态代理
    • 动态代理
    • Cglib 代理

    静态代理(不推荐)

    介绍

    要求目标对象和代理对象实现同一个接口,调用的时候调用代理对象的方法,从而达到增强的效果

    优点:

    可以在不修改目标对象的前提下,增强目标对象方法的功能(所有代理模式都可以实现,因此不推荐使用此方法)

    缺点:

    ① 冗余。目标对象和代理对象实现同一个接口,会产生过多的代理类。

    ② 不易维护。当接口方法增加,目标对象与代理对象都要进行修改。

    代码实现

    场景:厂家生产了商品,但是没有足够的精力、人力去销售,这时候就需要一个代理商帮他售卖,但是代理商需要从中抽取 20% 的利润。

    公共接口

    public interface IProducer {
        void sale(float money);
    }
    

    被代理对象

    public class Producer implements IProducer {
        @Override
        public void sale(float money) {
            System.out.println("卖出产品,厂家获得" + money + "元");
        }
    }
    

    代理对象

    public class ProxyProducer implements IProducer{
    
        private IProducer producer;
    
        public ProxyProducer(IProducer producer) {
            this.producer = producer;
        }
    
        @Override
        public void sale(float money) {
            producer.sale(money * 0.8f);
        }
    }
    

    测试类

    public class Client {
        @Test
        public void test(){
            IProducer producer = new Producer();
            ProxyProducer proxyProducer = new ProxyProducer(producer);
            proxyProducer.sale(1000f);
        }
    }
    

    运行结果

    卖出产品,厂家获得800.0元
    

    动态代理

    介绍

    动态代理也称:JDK 代理、接口代理,需要目标对象实现接口,否则不能用动态代理,利用 JDK 的 API(java.lang.reflect.Proxy),动态地在内存中构建代理对象

    静态代理和动态代理的区别:

    • 静态代理在编译时就已经实现,编译完后的代理类是一个实际的 class 文件
    • 动态代理实在运行时动态生成的,编译后没有实际的 class 文件,而是在运行时动态的生成类字节码,并加载到 JVM 中

    代码实现

    以静态代理的情景为例,我们只需要修改代理对象的代码,代理对象不需要实现公共接口了。

    public class ProxyProducer {
        /**
         * 维护一个目标对象
         */
        private Object target;
    
        public ProxyProducer(Object target) {
            this.target = target;
        }
    
        public Object getProxyInstance() {
            return Proxy.newProxyInstance(
                    target.getClass().getClassLoader(),
                    target.getClass().getInterfaces(),
                    new InvocationHandler() {
                        /**
                         * 执行被代理对象的任何接口方法都会经过这里
                         * @param proxy 代理对象的引用
                         * @param method 当前执行的方法
                         * @param args 当前执行方法的参数
                         * @return 和被代理对象具有相同的返回值
                         * @throws Throwable
                         */
                        @Override
                        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                            //代理过程中执行一些方法
                            float money = (float) args[0] * 0.8f;
                            //反射机制调用目标对象的方法
                            Object invoke = method.invoke(target, money);
                            return invoke;
                        }
                    });
        }
    }
    

    Cglib 代理

    介绍

    Cglib 代理也叫子类代理,目标对象不需要实现任何接口,它是在内存中构建一个子类对象从而实现对目标对象功能的扩展。

    Cglib 是一个强大的高性能的代码生成包,它可以在运行期间扩展 Java 类与实现 Java 接口,它广泛地被许多 AOP 的框架使用,例如 Spring AOP,用于实现方法拦截。

    Cglib 包底层实通过使用字节码处理框架 ASM 来转换字节码并生成新的类。

    在 AOP 编程中选择哪种代理模式?

    • 目标对象需要实现接口,用 JDK 代理
    • 目标对象不需要实现接口,用 Cglib 代理

    代码实现

    使用之前需要导入相关 jar 包,可去 maven 仓库下载

    被代理对象,无需实现接口

    public class Producer {
        public void sale(float money) {
            System.out.println("卖出产品,厂家获得" + money + "元");
        }
    }
    

    代理对象

    public class ProxyProducer implements MethodInterceptor {
        /**
         * 维护一个目标对象
         */
        private Object target;
    
        public ProxyProducer(Object target) {
            this.target = target;
        }
    
        /**
         * 为目标对象生成代理对象
         */
        public Object getProxyInstance(){
            //创建一个工具类
            Enhancer enhancer = new Enhancer();
            //设置父类
            enhancer.setSuperclass(target.getClass());
            //设置回调函数
            enhancer.setCallback(this);
            //创建子类对象(代理对象)
            return enhancer.create();
        }
    
        /**
         * 会拦截被代理对象的所有方法
         * @param obj 增强对象
         * @param method 被代理对象的方法
         * @param args 被代理对象方法的参数
         * @param methodProxy 代理对象
         */
        @Override
        public Object intercept(Object obj, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
            System.out.println("obj:" + obj.getClass());
            Object returnValue = null;
            float money = (float) args[0] * 0.8f;
            if("sale".equals(method.getName())){
                returnValue = method.invoke(target, money);
            }
            return returnValue;
        }
    }
    

    测试类

    public class Client {
        @Test
        public void test() {
            Producer producer = new Producer();
            Producer proxyInstance = (Producer) new ProxyProducer(producer).getProxyInstance();
            proxyInstance.sale(1000f);
        }
    }
    
  • 相关阅读:
    linux vim编辑
    jQuery Ajax 操作函数
    导出excel表功能
    jquery 分页控件功能
    数据绑定后细节处理
    删除数据库日志文件的操作语句
    SqlHelper.cs
    c#中$.ajax的使用
    SpringBoot 获取微信小程序openid
    Arduino IDE 开发 ESP-01S/ESP-01物联网实战检测温度湿度上传MQTT服务器
  • 原文地址:https://www.cnblogs.com/songjilong/p/12713689.html
Copyright © 2020-2023  润新知