设计模式之代理模式

作者简介:大家好,我是smart哥,前中兴通讯、美团架构师,现某互联网公司CTO

联系qq:184480602,加我进群,大家一起学习,一起进步,一起对抗互联网寒冬

学习必须往深处挖,挖的越深,基础越扎实!

阶段1、深入多线程

阶段2、深入多线程设计模式

阶段3、深入juc源码解析

阶段4、深入jdk其余源码解析

阶段5、深入jvm源码解析

码哥源码部分

码哥讲源码-原理源码篇【2024年最新大厂关于线程池使用的场景题】

码哥讲源码【炸雷啦!炸雷啦!黄光头他终于跑路啦!】

码哥讲源码-【jvm课程前置知识及c/c++调试环境搭建】

​​​​​​码哥讲源码-原理源码篇【揭秘join方法的唤醒本质上决定于jvm的底层析构函数】

码哥源码-原理源码篇【Doug Lea为什么要将成员变量赋值给局部变量后再操作?】

码哥讲源码【你水不是你的错,但是你胡说八道就是你不对了!】

码哥讲源码【谁再说Spring不支持多线程事务,你给我抽他!】

终结B站没人能讲清楚红黑树的历史,不服等你来踢馆!

打脸系列【020-3小时讲解MESI协议和volatile之间的关系,那些将x86下的验证结果当作最终结果的水货们请闭嘴】

代理模式

定义

代理模式为另一个对象提供一个替身或占位符以控制对这个对象访问。
代理模式 (英语:Proxy Pattern)是程序设计中的一种设计模式

所谓的代理者是指一个类别可以作为其它东西的接口。代理者可以作任何东西的接口:网络连接、存储器中的大对象、文件或其它昂贵或无法复制的资源。

著名的代理模式例子为引用计数(英语:reference counting)指针对象。

当一个复杂对象的多份副本须存在时,代理模式可以结合享元模式以减少存储器用量。典型作法是创建一个复杂对象及多个代理者,每个代理者会引用到原本的复杂对象。而作用在代理者的运算会转送到原本对象。一旦所有的代理者都不存在时,复杂对象会被移除。

(引用维基百科)

比如要访问远程的机器,由于某些原因,直接访问会给使用者或者系统结构带来很多麻烦,我们可以在访问对象时加上一个对此对象的访问层。比如RMI技术。

类图

实现

1. JDK实现

定义Subject接口
    public interface PersonBean {
        String getName();
        String getGender();
        String getInterest();
        int getHotOrNotRating();
    
        void setName(String name);
        void setGender(String gender);
        void setInterest(String interest);
        void setHotOrNotRating(int rating);
    }
定义Subject实现接口
    public class PersonImpl implements PersonBean {
        String name;
        String gender;
        String interests;
        int rating;
        int ratingCount = 0;
    
        @Override
        public String getName() {
            return name;
        }
    
        @Override
        public String getGender() {
            return gender;
        }
    
        @Override
        public String getInterest() {
            return interests;
        }
    
        @Override
        public int getHotOrNotRating() {
            if (ratingCount == 0) return 0;
            return (rating / ratingCount);
        }
    
        @Override
        public void setName(String name) {
            this.name = name;
        }
    
        @Override
        public void setGender(String gender) {
            this.gender = gender;
    
        }
    
        @Override
        public void setInterest(String interest) {
            this.interests = interest;
    
        }
    
        @Override
        public void setHotOrNotRating(int rating) {
            this.rating += rating;
            ratingCount++;
    
        }
    }
定义InvocationHandler实现类
    /**
     * 步骤一: 创建InvocationHandler类。这个类与Proxy互相配合。遵循单一职责。Proxy负责动态生成代理对象。而InvocationHandler则是实现业务逻辑
     * 个人代理对象: 允许获取和设计自己属性
     */
    public class OwnerInvocationHandler implements InvocationHandler {
        PersonBean personBean ;
        public OwnerInvocationHandler(PersonBean personBean) {
            this.personBean = personBean;
        }
    
        /**
         * 业务上的增加实现。
         *
         * @param proxy
         * @param method
         * @param args
         * @return
         * @throws Throwable
         */
        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            if (method.getName().startsWith("get")) {
                return method.invoke(personBean, args);
            } else if (method.getName().equals("setHotOrNotRating")) {
                throw new IllegalAccessException();
            } else if (method.getName().startsWith("set")) {
                return method.invoke(personBean, args);
            }
            return null;
        }
    }

    /**
     * 步骤一: 创建InvocationHandler类
     */
    public class NonInvocationHandler implements InvocationHandler {
        PersonBean personBean;
    
        public NonInvocationHandler(PersonBean personBean) {
            this.personBean = personBean;
        }
    
        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            if (method.getName().startsWith("get")) {
                return method.invoke(personBean, args);
            } else if (method.getName().equals("setHotOrNotRating")) {
                method.invoke(personBean, args);
            } else if (method.getName().startsWith("set")) {
                throw new IllegalAccessException();
            }
            return null;
        }
    }
Main
    /**
     * 1. 动态代理之所以称为动态: 是因为在运行时才将它的类创建出来。代码开始执行时,还没有proxy类,它是根据需要从你传入的接口集创建的。
     * 2. InvocationHandle不是proxy,它只是一个帮助proxy的类,proxy会把调用转发给它处理。Proxy本身是复用静态的Proxy.newProxyInstance()方法在运行时动态地创建的。
     * 3. 对于newProxyInstance()的接口数据。如果接口不是public,就必须属于同一个package,不同的接口内,不可以有名称和参数完全一样的方法。
     * 4. 动态代理有点像工厂模式,对于不同的需要被代理的对象产生对应的代理对象。
     */
    public class M {
        public static void main(String[] args) {
            PersonBean joe = getJoe();
            PersonBean ownerProxy = getOwnerProxy(joe);
            System.out.println("姓名: " + joe.getName());
            ownerProxy.setInterest("我喜欢篮球");
            try {
                ownerProxy.setHotOrNotRating(10);
            } catch (Exception e) {
                System.out.println("不能自己设定评分哦,需要别人对自己评分才有意义");
            }
            System.out.println("当前Joe评分:" + joe.getHotOrNotRating());
    
            PersonBean nonProxy = getNonProxy(joe);
            System.out.println("姓名: " + nonProxy.getName());
            nonProxy.setHotOrNotRating(10);
            try {
                nonProxy.setInterest("hey");
            } catch (Exception e) {
                System.out.println("当前兴趣爱好不能被其他人设置");
            }
            System.out.println("当前评分: " + nonProxy.getHotOrNotRating());
    
        }
    
        public static PersonBean getTom() {
            PersonBean joe = new PersonImpl();
            joe.setName("Tom");
            joe.setGender("Man");
            joe.setHotOrNotRating(1);
            joe.setInterest("Painting");
            return joe;
        }
    
        public static PersonBean getJoe() {
            PersonBean joe = new PersonImpl();
            joe.setName("Joe");
            joe.setGender("Man");
            joe.setHotOrNotRating(1);
            joe.setInterest("Painting");
            return joe;
        }
    
        public static PersonBean getOwnerProxy(PersonBean personBean) {
            return (PersonBean) Proxy.newProxyInstance(personBean.getClass().getClassLoader(), personBean.getClass().getInterfaces(), new OwnerInvocationHandler(personBean));
        }
    
        public static PersonBean getNonProxy(PersonBean personBean) {
            return (PersonBean) Proxy.newProxyInstance(
                    personBean.getClass().getClassLoader(),
                    personBean.getClass().getInterfaces(),
                    new NonInvocationHandler(personBean));
        }
    
    }

2. CbLib实现

1. 引入Maven
    <dependency>
        <groupId>cglib</groupId>
        <artifactId>cglib</artifactId>
        <version>3.3.0</version>
    </dependency>
2. 创建目标类
    public class Target {
        public void targetMethod(String s1) {
            System.out.println(String.format("当前Classloader%s, 方法参数: %s", this.getClass().getClassLoader(),  s1));
        }
    }
3. 实现方法拦截器
    public class CgLibProxy implements MethodInterceptor {
        @Override
        public Object intercept(Object o, Method method, Object[] args, MethodProxy proxy) throws Throwable {
            beforeMethod(args);
            Object result = proxy.invokeSuper(o, args);
            afterMethod(args);
            return result;
        }
    
        public void beforeMethod(Object[] args) {
            System.out.println("Aop 前置 参数校验");
            if (args.length != 1) {
                throw new IllegalArgumentException("只允许有一个参数");
            }
            args[0] = "参数校验成功: " + args[0];
        }
    
        public void afterMethod(Object[] args) {
            System.out.println("Aop 后置 增强操作!");
        }
    }
4. 创建代理对象
    public class M {
        public static void main(String[] args) {
            Enhancer eh = new Enhancer();
            // 设置被代理的类(目标类)
            eh.setSuperclass(Target.class);
            // 使用回调
            eh.setCallback(new CgLibProxy());
            // 创建 代理
            Object proxy = eh.create();
            Target t = (Target) proxy;
            t.targetMethod("h1");
        }
    }

总结

  1. 代理模式属于结构型模式。

  2. 代理模式基于接口而非实现编程的设计,将原始类对象替换为代理类对象的时候,为了让代码改动较少,代理类和原始类都需要实现相同的接口。但如果原始类并没有定义接口,并且原始类代码并不是我们开发维护的,对于这种外部类的扩展,一般是采用继承方式实现。

  3. 动态代理指不事先为每个原始类编写代理类,而是在运行的时候动态地创建原始类对应的代理类,然后在系统中用代理类替换掉原始类。

  4. 应用场景

    1. 业务系统的非功能性需求开发。如监控、统计、鉴权、限流、事务、幂等、日志。
    2. RPC、缓存。
相关推荐
小白不太白9501 小时前
设计模式之 模板方法模式
java·设计模式·模板方法模式
色空大师2 小时前
23种设计模式
java·开发语言·设计模式
闲人一枚(学习中)2 小时前
设计模式-创建型-建造者模式
java·设计模式·建造者模式
博风2 小时前
设计模式:6、装饰模式(包装器)
设计模式
A_cot2 小时前
理解设计模式与 UML 类图:构建稳健软件架构的基石
microsoft·设计模式·简单工厂模式·工厂方法模式·uml
君败红颜2 小时前
设计模式之创建模式篇
设计模式
闲人一枚(学习中)5 小时前
设计模式-创建型-抽象工厂模式
设计模式·抽象工厂模式
小白不太白9508 小时前
设计模式之 观察者模式
观察者模式·设计模式
小白不太白9509 小时前
设计模式之 责任链模式
python·设计模式·责任链模式
吾与谁归in9 小时前
【C#设计模式(13)——代理模式(Proxy Pattern)】
设计模式·c#·代理模式