【再探】设计模式—代理模式

 代理是指授权代理人在一定范围内代表其向第三方进行处理有关事务。

1 代理模式

需求:1)将业务代码与非业务代码分离,在不改变代码结构的基础上,为其添加新的功能。2)为系统中的某些操作做同一处理,例如进行鉴权、监控、日志、统计等。

1.1 代理模式介绍

增加一个代理对象,在客户端和目标对象之间起到中介作用,去掉客户不能看到的内容和服务,或者添加客户想要的额外服务。

图 代理模式UML

public class StaticProxy {

    public static void main(String[] args) {
        Subject request = new SimpleRequest();
        Subject proxy = new LogProxy(request);
        proxy.request("applet","123456");
    }

    private interface Subject {
        void request(String source,String token);
    }

    private static class LogProxy implements Subject {

        private final Subject subject;

        private LogProxy(Subject subject) {
            this.subject = subject;
        }

        @Override
        public void request(String source, String token) {
            System.out.println("记录访问日志,source=" + source + ",token=" + token);
            subject.request(source,token);
        }
    }

    private static class SimpleRequest implements Subject {
        @Override
        public void request(String source, String token) {
            System.out.println("接收请求:" + source + "," + token);
        }
    }

}

代理模式

侧重控制对目标对象的访问,及扩展不同于目标对象纬度的功能。

装饰模式

侧重于增强目标对象本身的功能,是继承方案的一个代替。

表 代理模式与装饰模式对比

1.1.1 动态代理

静态代理模式,需要为每个目标对象创建一个代理类,会造成系统中类的数量增多,而且当这些代理类提供的功能相同时,将会造成代码的冗余。动态代理是指在运行时动态创建代理对象。

JDK

JDK自带的一种动态代理方法,要求被代理类必须要实现接口。

原理:动态生成实现目标接口的代理类,调用代理方法时,通过反射的形式来调用目标对象方法。

CGLIB

是CGLIB提供的一种动态代理方法。被代理类可为普通类及接口。

原理:动态生成继承目标类的代理类,通过fastclass 的策略来找到目标方法。

与JDK方式对比,其动态生成的类会比较多,但执行效率会比反射更快。

表 Java中两种动态代理

JDK 方式:

public class JdkDynamicProxy {

    private interface Subject {
        void request(String source,String token);
    }

    private static class RealRequest implements Subject{

        @Override
        public void request(String source, String token) {
            System.out.println("请求网络");
        }
    }

    private static class RequestIntercept implements InvocationHandler {

        private final Subject subject;

        private RequestIntercept(Subject subject) {
            this.subject = subject;
        }

        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            System.out.println("请求参数:" + Arrays.asList(args));
            Object result = method.invoke(subject, args);
            System.out.println("记录访问日志");
            return result;
        }
    }

    public static void main(String[] args) {
        Subject realSubject = new RealRequest();
        Subject subject = (Subject) Proxy.newProxyInstance(JdkDynamicProxy.class.getClassLoader(), new Class[]{Subject.class}, new RequestIntercept(realSubject));
        subject.request("小程序","1344");
    }

}

CGLIB方式

public class CglibDynamicProxy {

    private static class RealRequest {

        public RealRequest() {
        }

        public void request(String source, String token) {
            System.out.println("请求成功,source=" + source + ",token=" + token);
        }
    }

    private static class RequestIntercept implements MethodInterceptor {
        @Override
        public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
            System.out.println("拦截请求:" + Arrays.asList(objects));
            Object result = methodProxy.invokeSuper(o, objects);
            System.out.println("记录请求日志");
            return result;
        }
    }


    public static void main(String[] args) {
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(RealRequest.class);
        enhancer.setCallback(new RequestIntercept());
        RealRequest realRequest = (RealRequest) enhancer.create();
        realRequest.request("小程序","1344");
    }
}

1.1.2 远程代理

使得客户端可以访问远程机,将网络细节隐藏起来,客户端可完全认为被代理的远程对象是局域的而非远程的。

远程代理对象承担了大部份网络通信工作,并负责对远程业务方法的调用。

RMI,Remote Method Invocation,远程方法调用,是JDK 实现的一种远程代理。

表 RMI 内部实现步骤

客户端通过一个桩(Stub),相当于代理类对象,与远程主机上的业务对象进行通信。而远程主机端有个Skeleton(骨架)对象来负责与Stub对象通信。

远程机部署代码:

public interface RemoteService extends Remote {

    String service(String requestInfo) throws RemoteException;

}

public class RemoteServiceImpl extends UnicastRemoteObject implements RemoteService{

    public RemoteServiceImpl() throws RemoteException{
    }

    @Override
    public String service(String requestInfo) throws RemoteException {
        System.out.println("请求:" + requestInfo);
        return "RemoteService提供远程服务:" + requestInfo;
    }
}

public class JNDIPublisher {

    public static void main(String[] args) throws RemoteException, MalformedURLException {
        RemoteService remoteService = new RemoteServiceImpl();
        int port = 8080;
        String serviceName = "rmi://localhost:" + port + "/remoteService";
        LocateRegistry.createRegistry(port);
        Naming.rebind(serviceName,remoteService);
    }

}

客户端代码:

public class RmiLocal {

    public static void main(String[] args) throws MalformedURLException, NotBoundException, RemoteException, NoSuchMethodException, InvocationTargetException, IllegalAccessException {
        String serviceName = "rmi://localhost:" + 8080 + "/remoteService";
        reflectRemote(serviceName);
        interfaceRemote(serviceName);
    }

    // 这种方式仍然需要导入远程服务提供的接口,RemoteService
    private static void reflectRemote(String serviceName) throws MalformedURLException, NotBoundException, RemoteException, NoSuchMethodException, InvocationTargetException, IllegalAccessException {
        Remote lookup = Naming.lookup(serviceName);
        Method method = lookup.getClass().getMethod("service", String.class);
        Object result = method.invoke(lookup, "反射方式");
        System.out.println("反射方式,远程调用结果:" + result);
    }

    private static void interfaceRemote(String serviceName) throws MalformedURLException, NotBoundException, RemoteException{
        RemoteService remoteService = (RemoteService)Naming.lookup(serviceName);
        String result = remoteService.service("接口方式");
        System.out.println("接口方式,远程调用结果:" + result);
    }

}

1.1.3 虚拟代理

主要目标是延迟对象的创建或者复杂计算,直到它们实际需要被创建或计算。这可以提高性能,降低资源消耗。

public class VirtualAgent {

    private interface Image {
        void display();
    }

    private static class RealImage implements Image {

        private String path;

        public RealImage(String path) {
            System.out.println("创建一个图片需要许多资源,耗费时间较长");
            this.path = path;
        }

        @Override
        public void display() {
            System.out.println("展示图片:" + path);
        }
    }

    private static class ProxyImage implements Image {

        private volatile Image image;
        private String path;

        public ProxyImage(String path) {
            System.out.println("虚拟对象,需要资源及时间都很少,用于延迟真实对象的创建与访问");
            this.path = path;
        }

        @Override
        public void display() {
            if (image == null) {
                synchronized (ProxyImage.class) {
                    if (image == null) image = new RealImage(path);
                }
            }
            image.display();
        }
    }

    public static void main(String[] args) {
        Image[] images = new Image[10];
        // 在网页加载过程,先加载这些图片,为了防止陷入卡顿,不直接创建图片,而是先创建虚拟对象
        System.out.println("网页加载中...");
        for (int i = 0; i < images.length; i++) {
            images[i] = new ProxyImage(i + ".png");
        }
        System.out.println("网页加载完成--------");
        // 当网页加载完成后,可以依次加载真实图片了
        for (Image image : images) {
            image.display();
        }
    }

}

1.1.4 缓存代理

为某些目标操作的结果提供临时的存储空间,让后续相同的操作及相同的参数可以直接共享这些结果,而不必再执行计算,以此来提高系统性能。

public class CacheProxy {

    private static class CacheMethodInterceptor implements MethodInterceptor {

        private final static Map<MethodKey,Object> resultMap = new HashMap<>();

        @Override
        public Object intercept(Object o, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
            MethodKey methodKey = MethodKey.getKey(method, args);
            Object result = resultMap.get(methodKey);
            if (result != null) {
                return result;
            } else {
                Object result2 = methodProxy.invokeSuper(o, args);
                resultMap.put(methodKey,result2);
                return result2;
            }
        }
    }

    // 根据method 及 参数来生成key
    private static class MethodKey {

        private static final Map<String,MethodKey> methodKeyMap = new HashMap<>();

        private String code;

        private MethodKey(String code) { this.code = code;}

        public static MethodKey getKey(Method method, Object[] objects) {
            String hasCode = "method:" + method.hashCode();
            if (objects != null) hasCode += "args:" + Arrays.hashCode(objects);
            MethodKey methodKey = methodKeyMap.get(hasCode);
            if (methodKey == null) {
                synchronized (MethodKey.class) {
                    methodKey = new MethodKey(hasCode);
                    methodKeyMap.put(hasCode,methodKey);
                }
            }
            return methodKey;
        }

        @Override
        public String toString() {
            return code;
        }
    }

    private static class ComplexCompute {

        public ComplexCompute() {
        }

        long compute(long num) {
            System.out.println("执行复杂计算,参数" + num);
            return num * 2345;
        }
    }

    public static void main(String[] args) {
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(ComplexCompute.class);
        enhancer.setCallback(new CacheMethodInterceptor());
        ComplexCompute compute = (ComplexCompute) enhancer.create();
        System.out.println(compute.compute(23));
        System.out.println("----------");
        System.out.println(compute.compute(123));
        System.out.println("----------");
        System.out.println(compute.compute(23));
    }

}

1.2 优缺点

优点:

  1. 能动态扩展对象的功能,而不需要修改原有代码,符合开闭原则。
  2. 能对目标对象的方法访问进行控制与保护。
  3. 将耗费系统资源及时间较多的对象延迟到真正需要使用时再创建,能提高系统性能及系统对客户的友好度。
  4. 能调用远程对象方法,而不需要考虑复杂的实现细节。
  5. 能对运行结果进行缓存,提高系统运行效率。

缺点:

  1. 增加了代理对象,可能会造成请求的处理速度变慢。
  2. 增加了类的数量,让系统变得更复杂。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/641053.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Dilworth 定理

这是一个关于偏序集的定理&#xff0c;事实上它也可以扩展到图论&#xff0c;dp等中&#xff0c;是一个很有意思的东西 偏序集 偏序集是由集合 S S S以及其上的一个偏序关系 R R R定义的&#xff0c;记为 ( S , R ) (S,R) (S,R) 偏序关系&#xff1a; 对于一个二元关系 R ⊂…

Python筑基之旅-MySQL数据库(四)

目录 一、数据表操作 1、新增记录 1-1、用mysql-connector-python库 1-2、用PyMySQL库 1-3、用PeeWee库 1-4、用SQLAlchemy库 2、删除记录 2-1、用mysql-connector-python库 2-2、用PyMySQL库 2-3、用PeeWee库 2-4、用SQLAlchemy库 3、修改记录 3-1、用mysql-conn…

力扣HOT100 - 21. 合并两个有序链表

解题思路&#xff1a; class Solution {public ListNode mergeTwoLists(ListNode list1, ListNode list2) {ListNode dum new ListNode(0), cur dum;while (list1 ! null && list2 ! null) {if (list1.val < list2.val) {cur.next list1;list1 list1.next;} els…

力扣刷题---3146. 两个字符串的排列差

题目描述 给你两个字符串 s 和 t&#xff0c;每个字符串中的字符都不重复&#xff0c;且 t 是 s 的一个排列。 排列差 定义为 s 和 t 中每个字符在两个字符串中位置的绝对差值之和。 返回 s 和 t 之间的 排列差 。 示例 1&#xff1a; 输入&#xff1a;s “abc”, t “b…

四万字长文详解——node.js使用移动云,EOS对象存储

目录 前言 安装及安装前的操作 前置条件 如何创建认证信息 使用npm安装SDK开发包 安装开发包命令 初始化操作 存储桶 查看结果命令 查看桶列表 查看结果命令 删除桶 查看结果命令 创建桶 获取桶列表 判断桶是否存在 查询桶所属地域 查询桶的访问权限 管理桶的…

基于springboot+vue+Mysql的校园台球厅人员与设备管理系统

开发语言&#xff1a;Java框架&#xff1a;springbootJDK版本&#xff1a;JDK1.8服务器&#xff1a;tomcat7数据库&#xff1a;mysql 5.7&#xff08;一定要5.7版本&#xff09;数据库工具&#xff1a;Navicat11开发软件&#xff1a;eclipse/myeclipse/ideaMaven包&#xff1a;…

【C语言】冒泡排序详解

前言 排序&#xff0c;就是将一组数据按特定的规则调换位置&#xff0c;使这组数据具有某种顺序关系&#xff0c;一般就是递增或递减。 在接触C语言不久&#xff0c;我们就会遇到其中一种有名的排序算法——“冒泡排序”&#xff0c;不知道你是否已经掌握了&#xff0c;如果还…

2024最新 Jenkins + Docker 实战教程(五)- 配置Gitee Webhooks实现自动构建部署

&#x1f604; 19年之后由于某些原因断更了三年&#xff0c;23年重新扬帆起航&#xff0c;推出更多优质博文&#xff0c;希望大家多多支持&#xff5e; &#x1f337; 古之立大事者&#xff0c;不惟有超世之才&#xff0c;亦必有坚忍不拔之志 &#x1f390; 个人CSND主页——Mi…

等保建设:打造MySQL数据库审计系统

1、建设目标 在等级保护三级->应用安全->安全审计中强制需要有审计平台(满足对操作系统、数据库、网络设备的审计&#xff0c;在条件不允许的情况下&#xff0c;至少要使用数据库审计) 数据库审计服务符合等级保护三级标准&#xff0c;帮助您满足合规性要求&#xff0c;…

什么是组态?什么是工业控制中的组态软件?

随着工业4.0和智能制造的发展&#xff0c;工控软件的应用越来越广泛&#xff0c;它们在提高生产效率、降低能耗和减少人力成本等方面发挥着越来越重要的作用。 什么是工控软件&#xff1f; 工控软件是指用于工业控制系统的软件&#xff0c;主要应用于各种生产过程控制、自动化…

Java中流的概念细分

按流的方向分类&#xff1a; 输入流&#xff1a;数据流向是数据源到程序&#xff08;以InputStream、Reader结尾的流&#xff09;。 输出流&#xff1a;数据流向是程序到目的地&#xff08;以OutputStream、Writer结尾的流&#xff09;。 按处理的数据单元分类&#xff1a; 字…

在winnas中使用docker desktop遇到的问题及解决方法记录

最近在尝试从群晖转向winnas&#xff0c;一些简单的服务依然计划使用docker来部署。群晖的docker简单易用且稳定&#xff0c;在win上使用docker desktop过程中遇到了不少问题&#xff0c;在此记录一下以供后来人参考。 一、安装docker desktop后启动时遇到无法启动docker引擎 …

构建数字未来:探索Web3在物联网中的新视角

引言 随着Web3时代的来临&#xff0c;物联网技术正迎来一场新的变革。在这个数字化时代&#xff0c;Web3所带来的技术创新将为物联网的发展开辟新的视角。本文将深入探讨Web3在物联网领域的应用&#xff0c;揭示其在构建数字未来中的重要性和影响。 Web3与物联网的融合 区块链…

运用HTML、CSS设计Web网页——“西式甜品网”图例及代码

目录 一、效果展示图 二、设计分析 1.整体效果分析 2.头部header模块效果分析 3.导航及banner模块效果分析 4.分类classify模块效果分析 5.产品展示show模块效果分析 6.版权banquan模块效果分析 三、HTML、CSS代码分模块展示 1. 头部header模块代码 2.导航及bann…

QQ个性网空间日志网站模板源码

QQ个性网空间日志网站模板源码自带后台登录设置&#xff0c;适用于博客、文章、资讯、其他类网站内容使用。模板自带eyoucms内核&#xff0c;原创设计、手工书写DIVCSS&#xff0c;完美兼容IE7、Firefox、Chrome、360浏览器等;主流浏览器;结构容易优化;多终端均可正常预览。由于…

保安维稳,四信以科技构筑高速公路安全智慧防线

近日&#xff0c;广东梅大高速发生严重塌方事故&#xff0c;造成了严重的人员伤亡和财产损失。这一事件在公众心中敲响了安全的警钟&#xff0c;再次引起了公众对于交通设施运营安全性的重点关注。 国务院安委会办公室和国家防灾减灾救灾委员会办公室等主管机构先后印发紧急通知…

FuTalk设计周刊-Vol.053

#AI漫谈 热点捕手 1.Midjourney推出新功能Room 用户可在聊天室中一起创作图像 Midjourney最近推出了一个有趣的新功能——Room&#xff0c;为用户提供了一个协作和社交平台&#xff0c;用户可以一起创建和分享图像&#xff0c;并参与实时聊天。Room促进了用户之间的互动和合作…

Mujava 工具的简单使用

首先下载openjava.jar和mujava.jar&#xff0c;以及自己手写一个mujava.config指向存放mujava的目录&#xff0c;并将这些文件放在mujava目录下。此时&#xff0c;基本的mujava环境就搭建好了。 分别创建src&#xff08;存放源码文件&#xff09;、classes&#xff08;存放源码…

excel poi的titleRows 和 headRows含义

titleRows 这个参数的意思是&#xff1a;excel标题占多少行&#xff0c;而不是第几行headRows 这个参数的意思是&#xff1a;excel表头占几行&#xff0c;而不是第几行&#xff08;多行的意思是合并的行数&#xff09; 比如有一个excel如下&#xff0c;1-2行是标题&#xff0c…

webstorm新建vue项目相关问题

前言 这个迭代后端需求偏少&#xff0c;前端code的键盘都起火星子了。来了4个外包支持&#xff0c;1个后端3个前端&#xff0c;还是不够用啊。刚好趁这个机会稍微学习下vue&#xff0c;其实之前环境也配置过了&#xff0c;所以这里就不分享环境配置了&#xff0c;主要分享下新建…