Java 怎样实现代理模式,有什么优缺点

一、介绍

代理模式是一种常见的设计模式,它可以为其他对象提供一种代理以控制对这个对象的访问。代理对象具有与被代理对象相同的接口,客户端无需知道代理对象和被代理对象的区别。代理模式可以应用于各种不同的场景,例如远程代理、虚拟代理、保护代理等。本文将介绍 Java 中代理模式的实现方式和优缺点,并提供相应的代码示例。

在这里插入图片描述

二、Java 中代理模式的实现方式

Java 中实现代理模式有两种方式:静态代理和动态代理。

静态代理

静态代理是指在代码编译期间就已经确定代理类和被代理类的关系,代理类和被代理类的接口是相同的。静态代理需要手动编写代理类,因此代码量较大,但是在运行时代理对象的性能较高。下面是一个简单的静态代理代码示例:

首先定义一个接口 Subject:

public interface Subject {
    void request();
}

然后定义一个被代理类 RealSubject:

public class RealSubject implements Subject {
    @Override
    public void request() {
        System.out.println("RealSubject is doing something.");
    }
}

最后定义一个代理类 Proxy:

public class Proxy implements Subject {
    private RealSubject realSubject;

    public Proxy(RealSubject realSubject) {
        this.realSubject = realSubject;
    }

    @Override
    public void request() {
        System.out.println("Before the request.");
        realSubject.request();
        System.out.println("After the request.");
    }
}

在客户端使用时,可以直接使用代理对象调用方法:

Subject subject = new Proxy(new RealSubject());
subject.request();

输出结果为:

Before the request.
RealSubject is doing something.
After the request.

动态代理

动态代理是指在运行时动态生成代理类,不需要手动编写代理类。Java 中提供了两种动态代理的实现方式:基于接口的动态代理(JDK 动态代理)和基于类的动态代理(CGLIB 动态代理)。

(1)基于接口的动态代理

基于接口的动态代理是指代理类实现被代理类的接口,使用 Java 提供的 Proxy 类生成代理对象。在使用基于接口的动态代理时,被代理类必须实现一个接口,代理类会实现该接口并将方法的调用转发给被代理类。下面是一个简单的基于接口的动态代理代码示例:

首先定义一个接口 Subject:

public interface Subject {
    void request();
}

然后定义一个被代理类 RealSubject:

public class RealSubject implements Subject {
    @Override
    public void request() {
        System.out.println("RealSubject is doing something.");
    }
}

最后定义一个 InvocationHandler 类:

public class MyInvocationHandler implements InvocationHandler {
    private Object target;

    public MyInvocationHandler(Object target) {
        this.target = target;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("Before the request.");
        Object result = method.invoke(target, args);
        System.out.println("After the request.");
        return result;
    }
}

在客户端使用时,可以通过 Proxy.newProxyInstance 方法生成代理对象:

Subject subject = (Subject) Proxy.newProxyInstance(
        Subject.class.getClassLoader(),
        new Class[] { Subject.class },
        new MyInvocationHandler(new RealSubject())
);
subject.request();

输出结果为:

Before the request.
RealSubject is doing something.
After the request.

(2)基于类的动态代理

基于类的动态代理是指代理类继承被代理类,使用 CGLIB 库生成代理对象。在使用基于类的动态代理时,被代理类不需要实现接口,代理类会继承被代理类并重写其方法,将方法的调用转发给被代理类。下面是一个简单的基于类的动态代理代码示例:

首先定义一个被代理类 RealSubject:

public class RealSubject {
    public void request() {
        System.out.println("RealSubject is doing something.");
    }
}

然后定义一个 MethodInterceptor 类:

public class MyMethodInterceptor implements MethodInterceptor {
    @Override
    public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
        System.out.println("Before the request.");
        Object result = proxy.invokeSuper(obj, args);
        System.out.println("After the request.");
        return result;
    }
}

在客户端使用时,可以通过 Enhancer.create 方法生成代理对象:

Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(RealSubject.class);
enhancer.setCallback(new MyMethodInterceptor());
RealSubject subject = (RealSubject) enhancer.create();
subject.request();

输出结果为:

Before the request.
RealSubject is doing something.
After the request.

三、Java 中代理模式的优缺点

代理模式在 Java 中的应用非常广泛,其优缺点如下:

优点

(1)代理模式可以隐藏被代理对象的实现细节,使得客户端无需直接访问被代理对象,从而提高了系统的安全性。

(2)代理模式可以为被代理对象提供额外的功能,例如远程访问、缓存等,在不改变原有代码的情况下扩展了系统的功能。

(3)代理模式可以提高系统的性能,例如基于类的动态代理在运行时生成代理类,减少了编译时的性能开销。

缺点

(1)代理模式会增加系统的复杂度,需要编写额外的代码来实现代理对象。

(2)代理模式可能会降低系统的性能,例如基于接口的动态代理在运行时需要使用反射来调用被代理对象的方法,会带来一定的性能开销。

四、总结

本文介绍了 Java 中代理模式的实现方式和优缺点,并提供了相应的代码示例。在实际开发中,我们可以根据具体的需求选择适合的代理模式实现方式。静态代理适用于代理对象数量较少、代理类与被代理类的关系固定的情况,动态代理适用于代理对象数量较多、代理类与被代理类的关系不固定的情况。同时,我们也需要权衡代理模式的优缺点,选择最合适的方案来提高系统的可维护性和性能。

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

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

相关文章

SpringBoot整合Mybatis-plus实现多级评论

在本文中,我们将介绍如何使用SpringBoot整合Mybatis-plus实现多级评论功能。同时,本文还将提供数据库的设计和详细的后端代码,前端界面使用Vue2。 数据库设计 本文的多级评论功能将采用MySQL数据库实现,下面是数据库的设计&…

vcruntime140.dll无法继续执行代码?vcruntime140.dll如何修复?只需要3步即可

vcruntime140.dll是用于Microsoft Visual C Redistributable(可再发行组件)的一部分,它是一个动态链接库文件,包含了该软件包提供的运行库。在许多应用程序和游戏中,vcruntime140.dll文件经常被使用。如果该文件缺失或…

spark 数据的加载和保存(Parquet、JSON、CSV、MySql)

spark数据的加载和保存 SparkSQL 默认读取和保存的文件格式为 parquet 1.加载数据 spark.read.load 是加载数据的通用方法 scala> spark.read. csv format jdbc json load option options orc parquet schema table text textFile 如果读取不同格式的数据,可以…

后端要一次性返回我10万条数据

问题描述 面试官:后端一次性返回10万条数据给你,你如何处理?我:歪嘴一笑,what the f**k! 问题考察点 看似无厘头的问题,实际上考查候选人知识的广度和深度,虽然在工作中这种情况很少遇到... …

情景剧本杀闯关系统

情景剧本杀闯关软件的开发需求通常包括以下几个方面: 剧本设计:开发者需要根据用户需求和市场调研,设计不同主题和难度等级的剧本内容,以及游戏过程中的任务、角色和道具等。 游戏引擎开发:为了实现游戏过程中…

如何在 DigitalOcean 中部署 ONLYOFFICE 文档

现在您可使用通过 DigitalOcean 市场提供的一键式应用在 DigitalOcean 云架构中轻松部署 Docker 版本的 ONLYOFFICE 文档。 一键式应用是一个包含所有必要预配置组件的镜像,可用于便捷地在运行有 Ubuntu OS 的 DigitalOcean 服务器上部署 ONLYOFFICE: D…

Azure DevOps Server 2022.0.1升级手册

Contents 1. 概述2. 操作方法 2.1 安装操作系统2.2 安装数据库2.4 还原数据2.3 安装和配置Azure DevOps Server 1. 概述 Azure DevOps Server 是微软公司经过20多年的持续开发,逐渐将需求管理、敏捷实践、源代码管理、持续集成等功能集成一体,实现应用软…

B-Tree (多路查找树)分析-20230503

B-Tree (多路查找树)学习-20230503 前言 B-树是一类多路查询树,它主要用于文件系统和某些数据库的索引,如果采用二叉平衡树访问文件里面的数据,最坏情况下,磁头可能需要进行O(h)次对磁盘的读写,其中h为树的高度&…

微服务不是本地部署的最佳选择,不妨试试模块化单体

微服务仅适用于成熟产品 关于从头开始使用微服务,马丁・福勒(Martin Fowler)总结道: 1. 几乎所有成功的微服务都是从一个过于庞大而不得不拆分的单体应用开始的。 2. 几乎所有从头开始以微服务构建的系统,最后都会因…

Java 反射机制

目录 一、反射机制概述 二、理解并获取Class实例 三、反射的用法 1. 通过反射创建运行时类的对象 2. 通过反射获取运行时类的属性结构 3. 通过反射获取运行时类的方法结构 4. 通过反射获取运行时类的构造器结构 5. 通过反射获取运行时类的父类 6. 通过反射获取运行时类…

DDD系列:三、Repository模式

为什么需要Repository? ​ Anemic Domain Model(贫血领域模型)特征: 有大量的XxxDO对象:这里DO虽然有时候代表了Domain Object,但实际上仅仅是数据库表结构的映射,里面没有包含(或…

Midjourney之logo设计(建议收藏)

目录 宠物诊所的logo设计 常见的Logo类型 图形logo: 字母LOGO APP LOGO 进阶技巧 设置艺术家风格 去掉不需要的元素 ChatGPT Midjourney设计logo 聊天(国产):文心一言通义千问 绘图(国产) UI设计 ChatGP…

【谷粒商城之服务认证OAuth2.0】

本笔记内容为尚硅谷谷粒商城服务认证OAuth2.0部分 目录 一、OAuth 2.0 二、微博登录测试 1、微博登陆准备工作 2、获取微博Access Token 3、登录测试 1.添加HttpUtils工具类 2.controller 3.service 4.vo 总结 一、OAuth 2.0 OAuth: OAuth(开…

Java多线程深入探讨

1. 线程与进程2. 创建和管理线程2.1. 继承Thread类2.2. 实现Runnable接口2.3 利用Callable、FutureTask接口实现。2.4 Thread的常用方法 3. 线程同步3.1. synchronized关键字3.1.1同步代码块:3.1.2 同步方法: 3.2. Lock接口 4. 线程间通信5. 线程池5.1 使…

【Linux】管道

目录 一、前言 二、管道 1、匿名管道 1.1、基本原理 1.2、代码实现 1.3、管道的特点 1.4、基于管道的简单设计 2、命名管道 2.1、匿名管道与命名管道的区别 2.2、代码实现命名管道通信 一、前言 为了满足各种需求,进程之间是需要通信的。进程间通信的主要目…

python函数的递归调用

引入 函数既可以嵌套定义也可以嵌套调用。嵌套定义指的是在定义一个函数时在该函数内部定义另一个函数;嵌套调用指的是在调用一个函数的过程中函数内部有调用另一个函数。而函数的递归调用指的是在调用一个函数的过程中又直接或者间接的调用该函数本身。 函数递归…

PySpark基础入门(3):RDD持久化

RDD的持久化 RDD 的数据是过程数据,因此需要持久化存储; RDD之间进行相互迭代的计算,新的RDD的生成代表着旧的RDD的消失;这样的特性可以最大化地利用资源,老旧地RDD可以及时地从内存中清理,从而给后续地计…

aop切面调用失效问题排查

应用里有较多的地方访问外部供应商接口,由于公网网络不稳定或者外部接口不稳定(重启,发版,ip切换)的原因,经常报502或者504错误。为了解决HTTP调用的500报错,选择使用spring的retryable注解进行…

Pyinstaller将python文件打包成exe程序——封装LoFTR开源匹配代码

Pyinstaller将python文件打包成exe程序——封装LoFTR开源匹配代码 1.LoFTR代码下载及环境搭建 源码下载:https://github.com/bodhisatan/LoFTR-Stitch 环境搭建:按照github项目中的readme文档进行搭建即可,几乎没有遇到问题,代码…

【Unity入门】22.动态创建实例

【Unity入门】动态创建实例 大家好,我是Lampard~~ 欢迎来到Unity入门系列博客,所学知识来自B站阿发老师~感谢 (一)脚本实例化预制体对象 (1)Instantiate克隆创建对象 昨天我们学习了预制体这个概念&#…