Java的代理模式
java有三种代理模式
静态代理
jdk动态代理
cglib实现动态代理
代理模式的定义: 为其他对象提供一种代理以控制对这个对象的访问。在某些情况下,一个对象不适合或者不能直接引用另一个对象,而代理对象可以在客户端和目标对象之间起到中介的作用。
java的三种代理模式优缺点
静态代理 | jdk动态代理 | cglib实现动态代理 | |
优 点 | 简单易懂:静态代理模式的实现相对简单,易于理解和使用。代理类在编译时就已经存在,开发人员可以直接通过编码方式创建和配置代理对象。 可以在代理类中添加额外的逻辑:通过静态代理,我们可以在代理类中添加额外的逻辑,如安全检查、日志记录、性能监控等,而无需修改被代理类的代码。这种方式使得代理类具有更高的灵活性和扩展性。 控制访问权限:静态代理可以控制对被代理对象的访问权限。代理类可以添加一些条件或限制,以决定是否调用被代理类的方法,从而实现对被代理对象的权限控制。 低耦合:通过静态代理,被代理类和代理类是相互独立的,彼此并无直接的依赖关系。这种低耦合的设计能够降低系统的复杂性,提高代码的可维护性和可测试性。 可以对多个对象进行代理:静态代理模式可以对多个对象进行代理,这样我们可以在不改变原始对象的情况下,通过代理对象来进行统一的管理和控制。 | 简化代理模式实现:相比于静态代理,JDK动态代理通过反射机制实现,无需手动编写大量的代理类代码。只需要编写一个代理处理器即可,大大简化了代理模式的实现过程。 可以代理任意接口:JDK动态代理是基于接口的代理,可以代理任意实现了接口的类。这意味着无论被代理类的具体实现如何,只要实现了接口,就可以使用JDK动态代理进行代理操作。 代码结构清晰:使用JDK动态代理,代理类和被代理类之间的关系更加清晰。代理类实现了InvocationHandler接口,可以在方法调用前后添加自定义逻辑,而被代理类则专注于核心业务逻辑的实现。这样有利于代码的维护和扩展。 可以动态改变代理行为:由于代理类是在运行时动态生成的,可以在代理类中灵活地修改和调整代理行为。可以根据实际需要,在不修改被代理类代码的情况下,通过修改代理处理器的逻辑,实现不同的代理行为。 高性能:相比于CGlib等其他动态代理实现,JDK动态代理在性能上具有较好的表现。JDK动态代理使用Java原生的反射机制,在执行方法调用时会有一些性能损耗,但仍然比一些其他动态代理实现方式更高效。 | 无需接口:CGLib可以为非接口类生成代理,这意味着我们可以代理那些没有实现任何接口的类。相比于JDK动态代理需要目标类实现接口的限制,CGLib的使用更加灵活。 高性能:CGLib采用了底层的字节码操作技术,通过生成目标类的子类,并重写其中的方法来实现代理。相比于JDK动态代理的反射调用,CGLib的代理调用更快速,性能更高效。 简化代理:CGLib可以在运行时生成代理类,避免了编写很多重复的代理类代码的麻烦。它通过继承来实现代理,这样我们就可以专注于业务逻辑的实现,而无需关心代理类的创建和管理。 支持回调过滤:CGLib提供了MethodInterceptor接口,可以在代理类的方法调用前后插入自定义的逻辑。这允许我们在目标方法执行前后进行一些额外的处理,如日志记录、权限校验、缓存等。 可扩展性强:CGLib是一个功能强大的库,除了可以实现动态代理外,还可以用于AOP编程、实现Bean的动态注入等。它提供了丰富的API和扩展点,可以根据实际需求进行灵活的扩展和定制。 |
缺 点 | 代码复杂度增加:使用静态代理会引入额外的代理类,从而增加代码的复杂性。每个被代理类都需要对应一个代理类,随着被代理类的增加,代理类的数量也会增加,导致代码变得冗长且难以维护。 频繁更新代理类:如果被代理类的接口发生变化,代理类也需要相应地进行修改。这意味着在使用静态代理时,每次接口发生变动,需要手动更新相关的代理类,增加了维护成本和风险。 静态代理不支持动态代理:静态代理是在编译时期生成的代理类,对于动态生成的对象,无法进行代理。如果需要对一批动态生成的对象进行代理,静态代理就显得无能为力。 单一职责原则破坏:静态代理中代理类与被代理类紧密耦合,代理类不仅要实现接口方法的委托,还要添加额外的逻辑。这样可能导致代理类承担了过多的责任,违反了单一职责原则。 动态扩展困难:静态代理在代理类定义时就已经确定了被代理对象,如果需要代理新的类或者对象,就要重新编写对应的代理类。这种静态的特性使得动态扩展变得困难,难以应对频繁变化的需求。 | 只能代理接口:JDK动态代理只能代理实现了接口的类,对于没有实现接口的类,无法进行代理。这一限制使得某些类无法使用JDK动态代理,需要考虑其他代理实现方式。 性能有一定损耗:JDK动态代理使用反射机制,在方法调用时会涉及到反射操作,这会引入一定的性能损耗。相比起静态代理,JDK动态代理的性能稍差一些。对于对性能要求较高的场景,可能需要考虑其他更高效的代理实现方式。 无法代理私有方法:JDK动态代理只能代理公开的方法,无法代理私有方法。这是由于代理机制是基于接口生成的动态代理类,无法直接访问类的私有成员。 需要实现InvocationHandler接口:使用JDK动态代理,需要编写一个实现InvocationHandler接口的代理处理器类。虽然这使得动态代理实现更加灵活,但同时也增加了开发的复杂性。 无法直接修改被代理类:JDK动态代理是通过代理接口生成代理类,而不是直接修改被代理类的字节码。这一特性使得在不修改被代理类的情况下无法对其进行修改或添加新的行为。 | 类加载器限制:CGLib使用了底层的字节码操作技术来生成代理类,这需要在运行时动态创建和加载类。某些情况下,类加载器限制可能会导致无法使用CGLib动态代理,例如在一些受限的环境下或者使用特定的类加载器。 需要依赖第三方库:CGLib是一个第三方库,不是Java的标准库。使用CGLib时需要将其引入项目中并进行相应的配置,增加了项目的依赖。 Proxy无法代理final方法:CGLib的动态代理无法代理final方法,因为final方法无法被子类重写。如果目标类中存在final方法,CGLib将无法为其生成代理,这在一些特定场景下可能会有限制。 难以调试:使用CGLib生成的代理类是动态生成的字节码,对于开发者来说可读性较差。对于调试代理类的问题或者理解代理逻辑,可能需要额外的工具和技巧。 内存占用较高:由于CGLib是通过生成代理类的子类来实现的,每次代理都会创建一个新的类,并加载到JVM中。如果频繁地创建代理对象,可能会产生大量的代理类,增加内存占用。 |
静态代理
先创建一个父类或者接口,被代理对象和代理对象都应该继承父类或者实现接口。
创建接口Movie.java
public interface Movie {void play();
}
创建实现类RealMovie.java
public class RealMovie implements Movie {@Overridepublic void play() {System.out.println("播放金刚大战哥斯拉");}
}
创建代理类ProxyMovie.java
public class ProxyMovie implements Movie {Movie movie;public ProxyMovie(Movie movie) {this.movie = movie;}@Overridepublic void play() {System.out.println("电影还没开始,买点爆米花");movie.play();System.out.println("电影结束了,买个哥斯拉模型");}
}
创建测试类Test.java
public class Test {public static void main(String[] args) {RealMovie movie = new RealMovie();ProxyMovie proxyMovie = new ProxyMovie(movie);proxyMovie.play();}
}
总结:在不改变RealMovie的前提下,使用ProxyMovie对其进行了增强。但是由于目标对象和代理对象都实现了同一个接口,一旦这个接口添加或者删除方法,那么代理对象和目标对象都要进行相应的操作,耦合度太高。
静态代理的应用:在使用实现Runnable接口实现多线程的时候,将Runnable接口的实现类的对象作为Thread的构造函数的参数
jdk动态代理
为了解决静态代理的代理类必需实现接口的所有方法的问题,动态代理诞生了,java的动态代理有如下的特点:
1、代理对象不需要跟目标对象实现同一接口或继承同一个父类(解决了静态代理的痛点);
2、代理对象的生成利用jdk提供的api,在JVM内存中动态的构建代理对象。
创建接口Movie.java
public interface Movie {void play();
}
创建实现类RealMovie.java
public class RealMovie implements Movie{@Overridepublic void play() {System.out.println("播放侏罗纪世界");}
}
创建代理类ProxyMovie.java
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;public class ProxyMovie implements InvocationHandler {private Object movie;public ProxyMovie(Object movie) {this.movie = movie;}@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {System.out.println("电影还没开始,买瓶可乐");method.invoke(movie, args);System.out.println("电影还结束,买一只恐龙模型");return null;}
}
创建测试类Test.java
public class Test {public static void main(String[] args) {RealMovie realMovie = new RealMovie();InvocationHandler proxyMovie = new ProxyMovie(realMovie);// 创建代理对象Movie movie = (Movie)Proxy.newProxyInstance(realMovie.getClass().getClassLoader(), realMovie.getClass().getInterfaces(), proxyMovie);System.out.println(movie.getClass().getSimpleName());movie.play();}
}
总结:在这里,代理类没有实现Movie接口,而是实现了InvocationHandler接口,然后重写了invoke方法,与Movie接口的耦合度降低。
cglib实现动态代理
在使用jdk动态代理时,被代理对象是实现了一个接口的,假如实际中被代理对象没有实现接口该怎么办呢,cglib动态代理便应运而生,原理是创建一个目标对象的子类对象,然后进行增强操作,所以被代理对象的类不能是final类型的(无法被继承,也就没有子类),被调用的方法也不能是final和static类型的(不会执行代理功能)。
在pom.xml文件中添加cglib依赖
<dependency><groupId>cglib</groupId><artifactId>cglib</artifactId><version>3.3.0</version>
</dependency>
创建CglibService.java
public class CglibService {public void movie() {System.out.println("播放侏罗纪公园");}
}
创建CglibServiceProxy.java类
import org.springframework.cglib.proxy.Enhancer;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;import java.lang.reflect.Method;public class CglibServiceProxy implements MethodInterceptor {private Object object;public CglibServiceProxy(Object object) {this.object = object;}public Object getProxyInstance() {//创建代理类Enhancer enhancer = new Enhancer();//继承要被代理的类enhancer.setSuperclass(object.getClass());//设置回调函数enhancer.setCallback(this);return enhancer.create();}@Overridepublic Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {System.out.println("电影即将开始,大家有序进入影厅");Object obj = method.invoke(object);System.out.println("电影结束,大家离开电影院");return obj;}
}
创建CglibProxyTest.java
public class CglibProxyTest {public static void main(String[] args) {CglibService service = new CglibService();CglibServiceProxy proxy = new CglibServiceProxy(service);CglibService proxyInstance = (CglibService)proxy.getProxyInstance();proxyInstance.movie();}
}
总结:由以上可以看出,使用cglib代理可以代理一些没有父类的目标对象,所以以后如果要代理没有实现接口的类,可以选择使用cglib代理。
相关文章:

Java的代理模式
java有三种代理模式 静态代理 jdk动态代理 cglib实现动态代理 代理模式的定义: 为其他对象提供一种代理以控制对这个对象的访问。在某些情况下,一个对象不适合或者不能直接引用另一个对象,而代理对象可以在客户端和目标对象之间起到中介的…...
FilterAttributeOnClassMethod
目录 1 BadMethodFilterAttribute 2 FilterAttributeOnClassMethod 2.1 OnMethodExecuted 2.2 OnMethodExecutedAsync 2.3 OnMethodExecuting BadMethodFilterAttribute using System; using System.Threading.Tasks; namespace Flatwhite.Core.Tests.Attributes …...
springboot + (mysql/pgsql) + jpa 多数据源(不同类数据源)
配置文件: spring:datasource:primary:jdbc-url: jdbc:mysql://host:3306/数据库?useUnicodetrue&characterEncodingUTF-8&autoReconnecttrue&failOverReadOnlyfalse&serverTimezoneAsia/Shanghai&zeroDateTimeBehaviorconvertToNullusername…...
【Golang】Golang进阶系列教程--Go 语言 context 都能做什么?
文章目录 前言核心是 Context 接口:包含四个方法:遵循规则WithCancelWithDeadlineWithTimeoutWithValue 前言 很多 Go 项目的源码,在读的过程中会发现一个很常见的参数 ctx,而且基本都是作为函数的第一个参数。 为什么要这么写呢…...

画图干货!14种uml图类型及示例
1. 什么是 UML UML 是统一建模语言的缩写。UML 图是基于 UML(统一建模语言)的图表,目的是直观地表示系统及其主要参与者、角色、动作、工件或类,以便更好地理解、更改、维护或记录信息关于系统。简而言之,UML 是一种…...

计算机视觉实验:人脸识别系统设计
实验内容 设计计算机视觉目标识别系统,与实际应用有关(建议:最终展示形式为带界面可运行的系统),以下内容选择其中一个做。 1. 人脸识别系统设计 (1) 人脸识别系统设计(必做):根据…...

振弦采集仪完整链条的岩土工程隧道安全监测
振弦采集仪完整链条的岩土工程隧道安全监测 隧道工程是一种特殊的地下工程,其建设过程及运行期间,都受到各种内外力的作用,如水压、地震、地质变形、交通荷载等,这些因素都会对隧道的安全性产生影响。因此,对隧道的安…...

NLP实战9:Transformer实战-单词预测
目录 一、定义模型 二、加载数据集 三、初始化实例 四、训练模型 五、评估模型 🍨 本文为[🔗365天深度学习训练营]内部限免文章(版权归 *K同学啊* 所有) 🍖 作者:[K同学啊] 模型结构图: &a…...
使用Vue.js和Rust构建高性能的物联网应用
物联网(IoT)应用是现代技术的重要组成部分,它们可以在各种场景中(例如智能家居,工业自动化等)提供无缝的自动化解决方案。在这篇文章中,我们将探讨如何使用Vue.js和Rust构建高性能的物联网应用。 1. 为什么选择Vue.js…...

idea调节文字大小、日志颜色、git改动信息
idea调节菜单栏文字大小: 调节代码文字大小: 按住ctrl滚动滑轮可以调节代码文字大小: 单击文件即可在主窗口上打开显示: idea在控制台对不同级别的日志打印不同颜色 : “grep console”插件 点击某一行的时候&#x…...
避免大龄程序员边缘化:如何在技术行业中保持竞争力
目录 导语持续学习和进修维护专业形象寻找适合自己的领域构建个人品牌和网络拥抱变化和创新实例结语: 导语 导语:随着科技的不断发展,技术行业的竞争日益激烈。对于那些年龄稍长的程序员来说,如何保持竞争力并避免边缘化成为了一…...

Jenkins工具系列 —— 启动 Jenkins 服务报错
错误显示 apt-get 安装 Jenkins 后,自动启动 Jenkins 服务报错。 排查原因 直接运行jenkins命令 发现具体报错log:Failed to start Jetty或Failed to bind to 0.0.0.0/0.0.0.0:8080或Address already in use 说明:这里提示的是8080端口号…...
华为数通HCIA-实验环境ensp简介
ensp 路由器:AR系列、NE系列; 模拟器中使用AR2220; 交换机:S系列、CE系列; 模拟器中使用S5700; 线缆:copper——以太网链路; serial——串行链路,在模拟器中用于模…...
SK5代理与IP代理:网络安全中的爬虫利器
一、什么是IP代理与SK5代理? IP代理: IP代理是一种允许用户通过代理服务器进行网络连接的技术。用户请求经由代理服务器中转,从而实现隐藏真实IP地址,保护用户隐私,并在一定程度上突破IP访问限制。常见的IP代理有HTTP…...

实战:Prometheus+Grafana监控Linux服务器及Springboot项目
文章目录 前言知识积累什么是Prometheus什么是Grafana怎样完成数据采集和监控 环境搭建docker与docker-compose安装docker-compose编写 监控配置grafana配置prometheus数据源grafana配置dashboardLinux Host Metrics监控Spring Boot 监控 写在最后 前言 相信大家都知道一个项目…...

[用go实现解释器]笔记1-词法分析
本文是《用go实现解释器》的读书笔记 https://malred-blogmalred.github.io/2023/06/03/ji-suan-ji-li-lun-ji-shu-ji/shi-ti/go-compile/yong-go-yu-yan-shi-xian-jie-shi-qi/go-compiler-1/#toc-heading-6http://个人博客该笔记地址 github.com/malred/malanghttp:/…...
在 spark-sql / spark-shell / hive / beeline 中粘贴 sql、程序脚本时的常见错误
一个很小的问题,简单记录一下。有时候我们会粘贴一段已经成功运行过的SQL或程序脚本,但是在spark-sql / spark-shell / hive / beeline 中执行时可能会报这样的错误: hive> CREATE EXTERNAL TABLE IF NOT EXISTS ORDERS(> Display all…...

关于视频汇聚融合EasyCVR平台多视频播放协议的概述
视频监控综合管理平台EasyCVR具备视频融合能力,平台基于云边端一体化架构,具有强大的数据接入、处理及分发能力,平台既具备传统安防视频监控的能力与服务,也支持AI智能检测技术的接入,可应用在多行业领域的智能化监管场…...

三星书画联展:三位艺术家开启国风艺术之旅
7月22日,由广州白云区文联、白云区工商联主办的“三星书画联展”,在源美术馆正式开展。本次书画展展出的艺术种类丰富,油画、国画、彩墨画、书法等作品异彩纷呈。广东省政协原副主席、农工党省委书画院名誉院长马光瑜,意大利艺术研…...

在腾讯云服务器OpenCLoudOS系统中安装nginx(有图详解)
1. 创建安装目录 2. 下载、安装、编译 进入安装目录: cd /app/soft/nginx/ 下载: wget https://nginx.org/download/nginx-1.21.6.tar.gz 解压: tar -zxvf nginx-1.21.6.tar.gz 安装插件: yum -y install pcre-devel 安装…...
HTML 语义化
目录 HTML 语义化HTML5 新特性HTML 语义化的好处语义化标签的使用场景最佳实践 HTML 语义化 HTML5 新特性 标准答案: 语义化标签: <header>:页头<nav>:导航<main>:主要内容<article>&#x…...

云启出海,智联未来|阿里云网络「企业出海」系列客户沙龙上海站圆满落地
借阿里云中企出海大会的东风,以**「云启出海,智联未来|打造安全可靠的出海云网络引擎」为主题的阿里云企业出海客户沙龙云网络&安全专场于5.28日下午在上海顺利举办,现场吸引了来自携程、小红书、米哈游、哔哩哔哩、波克城市、…...

【网络安全产品大调研系列】2. 体验漏洞扫描
前言 2023 年漏洞扫描服务市场规模预计为 3.06(十亿美元)。漏洞扫描服务市场行业预计将从 2024 年的 3.48(十亿美元)增长到 2032 年的 9.54(十亿美元)。预测期内漏洞扫描服务市场 CAGR(增长率&…...

STM32标准库-DMA直接存储器存取
文章目录 一、DMA1.1简介1.2存储器映像1.3DMA框图1.4DMA基本结构1.5DMA请求1.6数据宽度与对齐1.7数据转运DMA1.8ADC扫描模式DMA 二、数据转运DMA2.1接线图2.2代码2.3相关API 一、DMA 1.1简介 DMA(Direct Memory Access)直接存储器存取 DMA可以提供外设…...

(二)原型模式
原型的功能是将一个已经存在的对象作为源目标,其余对象都是通过这个源目标创建。发挥复制的作用就是原型模式的核心思想。 一、源型模式的定义 原型模式是指第二次创建对象可以通过复制已经存在的原型对象来实现,忽略对象创建过程中的其它细节。 📌 核心特点: 避免重复初…...

【SQL学习笔记1】增删改查+多表连接全解析(内附SQL免费在线练习工具)
可以使用Sqliteviz这个网站免费编写sql语句,它能够让用户直接在浏览器内练习SQL的语法,不需要安装任何软件。 链接如下: sqliteviz 注意: 在转写SQL语法时,关键字之间有一个特定的顺序,这个顺序会影响到…...
WEB3全栈开发——面试专业技能点P2智能合约开发(Solidity)
一、Solidity合约开发 下面是 Solidity 合约开发 的概念、代码示例及讲解,适合用作学习或写简历项目背景说明。 🧠 一、概念简介:Solidity 合约开发 Solidity 是一种专门为 以太坊(Ethereum)平台编写智能合约的高级编…...

JUC笔记(上)-复习 涉及死锁 volatile synchronized CAS 原子操作
一、上下文切换 即使单核CPU也可以进行多线程执行代码,CPU会给每个线程分配CPU时间片来实现这个机制。时间片非常短,所以CPU会不断地切换线程执行,从而让我们感觉多个线程是同时执行的。时间片一般是十几毫秒(ms)。通过时间片分配算法执行。…...

人机融合智能 | “人智交互”跨学科新领域
本文系统地提出基于“以人为中心AI(HCAI)”理念的人-人工智能交互(人智交互)这一跨学科新领域及框架,定义人智交互领域的理念、基本理论和关键问题、方法、开发流程和参与团队等,阐述提出人智交互新领域的意义。然后,提出人智交互研究的三种新范式取向以及它们的意义。最后,总结…...

MFC 抛体运动模拟:常见问题解决与界面美化
在 MFC 中开发抛体运动模拟程序时,我们常遇到 轨迹残留、无效刷新、视觉单调、物理逻辑瑕疵 等问题。本文将针对这些痛点,详细解析原因并提供解决方案,同时兼顾界面美化,让模拟效果更专业、更高效。 问题一:历史轨迹与小球残影残留 现象 小球运动后,历史位置的 “残影”…...