Java的代理模式

news2024/9/28 11:20:00

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 {
    @Override
    public void play() {
        System.out.println("播放金刚大战哥斯拉");
    }
}

创建代理类ProxyMovie.java

public class ProxyMovie implements Movie {
    Movie movie;

    public ProxyMovie(Movie movie) {
        this.movie = movie;
    }

    @Override
    public 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{
    @Override
    public 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;
    }

    @Override
    public 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();
    }

    @Override
    public 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代理。

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

打包出现ProjectBuildingException异常原因之一

一次正常打包操作突然出现 原因是同事不小心在集合模块里面添加了重复的模块引入

Github 上 爆火,标星 103K的 Spring Security 手册及源码笔记,YYDS

Spring Security 是一个基于 Spring AOP 和 Servlet 过滤器的安全框架&#xff0c;它提供了安全性方面的解决方案 Spring Security 作为非常强大的框架&#xff0c;作为程序员是非常热爱的&#xff0c;我这里整理了四份 Spring Security 手写笔记及实战手册原文档见文末 目录…

FANUC机器人实现2个RO输出信号互锁关联(互补)的具体方法

FANUC机器人实现2个RO输出信号互锁关联(互补)的具体方法 一般情况下,为了方便用户控制工装夹具上的电磁阀等控制工具,FANUC机器人出厂时给我们提供了8个RO输出信号,如下图所示,这8个RO信号可以各自单独使用。 那么,如果为了安全控制,需要将2个RO信号成对的进行安全互锁…

【C语言进阶篇】回调函数都学了吧!那么用冒泡排序实现qsort函数你会嘛?

&#x1f3ac; 鸽芷咕&#xff1a;个人主页 &#x1f525; 个人专栏:《C语言初阶篇》 《C语言进阶篇》 ⛺️生活的理想&#xff0c;就是为了理想的生活! 文章目录 &#x1f4cb; 前言&#x1f4ac; qsort 和 冒泡排序的区别&#x1f4d1; qsort 的特点&#x1f4d1; 冒泡排序 …

干货 | 常见电路板GND与外壳GND之间接一个电阻一个电容,为什么?

干货 | 常见电路板GND与外壳GND之间接一个电阻一个电容&#xff0c;为什么&#xff1f; 外壳是金属的&#xff0c;中间是一个螺丝孔&#xff0c;也就是跟大地连接起来了。这里通过一个1M的电阻跟一个0.1uF的电容并联&#xff0c;跟电路板的地连接在一起&#xff0c;这样有什么好…

Michael.W基于Foundry精读Openzeppelin第14期——SafeMath.sol

Michael.W基于Foundry精读Openzeppelin第14期——SafeMath.sol 0. 版本0.1 SafeMath.sol 1. 目标合约2. 代码精读2.1 tryAdd(uint256 a, uint256 b) && trySub(uint256 a, uint256 b) && tryMul(uint256 a, uint256 b) && tryDiv(uint256 a, uint256 b…

CSS鼠标样式(cursor)

CSS cursor 属性值 属性值示意图描述auto默认值&#xff0c;由浏览器根据当前上下文确定要显示的光标样式default 默认光标&#xff0c;不考虑上下文&#xff0c;通常是一个箭头none不显示光标initial将此属性设置为其默认值inherit从父元素基础 cursor 属性的值context-menu…

JVM理论(七)性能监控与调优

概述 性能优化的步骤 性能监控&#xff1a;就是通过以非强行或入侵方式收集或查看应用程序运行状态,包括如下问题 GC频繁CPU过载过高OOM内存泄漏死锁程序响应时间较长性能分析&#xff1a;通常在系统测试环境或者开发环境进行分析 通过查看程序日志以及GC日志,或者运用命令行工…

simulink与遗传算法结合求解TSP问题

前言&#xff1a;刚开始入门学习simulink&#xff0c;了解了基本的模块功能后想尝试从自己熟悉的领域入手&#xff0c;自己出题使用simulink搭建模型。选择的是TSP问题的遗传算法&#xff0c;考虑如何用simulink建模思想来实现一个简单TSP问题的遗传算法。 TSP问题描述 一个配…

注解和反射04--类加载

类加载 Java内存分析了解类的加载过程类的加载与ClassLoader的理解什么时候会发生类的初始化 类加载器类加载器的作用 Java内存分析 了解类的加载过程 当程序主动使用某个类是&#xff0c;如果该类害未被加载到内存中&#xff0c;啧系统会通过下面三个步骤来对该类进行初始化 …

数字信号处理中的基本运算——加法运算

1. 一位全加器 2. 二进制加法原理 两个N位二进制补码相加&#xff0c;为防止溢出时导致计算结果错误&#xff0c;可将这两个加数先进行符号位扩展&#xff0c;变为N1位二进制数&#xff0c;然后相加&#xff0c;结果亦取N1位&#xff0c;可保证运算结果正确。 根据多位加法器…

互联网医院申办|线上问诊系统|互联网医院系统功能

随着互联网的快速发展&#xff0c;各行各业都在积极探索如何将互联网与自身服务相结合&#xff0c;实现数字化转型。互联网医院建设分院内与院外建设&#xff0c;院内建设是业务流程的优化过程&#xff0c;是系统改造的过程&#xff0c;是医院精细化运营的一部分&#xff0c;也…

基于Java+SpringBoot+vue前后端分离美容院管理系统设计实现

博主介绍&#xff1a;✌全网粉丝30W,csdn特邀作者、博客专家、CSDN新星计划导师、Java领域优质创作者,博客之星、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域和毕业项目实战✌ &#x1f345;文末获取源码联系&#x1f345; &#x1f447;&#x1f3fb; 精彩专…

跑步适合戴什么样的耳机、最好的跑步耳机推荐

每个人对于运动的方式都不尽相同&#xff0c;但大多数热爱运动的朋友都离不开音乐的陪伴。运动和带有节奏感的音乐能够激发我们更多的热情和动力。特别是在夏日的时候&#xff0c;我非常喜欢跑步。在酷热的天气里&#xff0c;如果没有音乐的伴随&#xff0c;跑步会变得单调乏味…

【Vue3】BEM 架构和 Sass 语法

1. BEM 架构 BEM&#xff08;Block, Element, Modifier&#xff09;是一种命名约定&#xff0c;用于在编写 CSS 和 HTML 类名时创建可维护和可重用的样式。BEM 是一种常用的 CSS 命名规范&#xff0c;它的目的是减少样式之间的耦合&#xff0c;增加样式的可读性&#xff0c;并…

多目标优化:NSGA(Ⅱ)

多目标优化的基本概念 习多目标优化的过程中&#xff0c;其中涉及相关概念如下&#xff1a; Pareto 支配关系 (Pareto Dominance)&#xff1a;支配&#xff1a;对于多个目标值&#xff0c;随机自变量x1、x2&#xff0c;对于任意一个目标函数都存在f(x1)<f(x2)&#xff0c;则…

Vue 3:玩一下web前端技术(三)

前言 本章内容为VUE工作过程与相关使用讨论。 上一篇文章地址&#xff1a; Vue 3&#xff1a;玩一下web前端技术&#xff08;二&#xff09;_Lion King的博客-CSDN博客 下一篇文章地址&#xff1a; Vue 3&#xff1a;玩一下web前端技术&#xff08;四&#xff09;_Lion Ki…

视频监控综合管理平台EasyCVR向上级联时,上级一直回复401是什么原因?

视频监控管理EasyCVR视频融合平台基于云边端一体化架构&#xff0c;可支持多协议、多类型设备接入&#xff0c;具体包括&#xff1a;NVR、IPC、视频编码器、无人机、车载设备、智能手持终端、移动执法仪等。平台具有强大的数据接入、处理及分发能力&#xff0c;可在复杂的网络环…

SSM 书籍借阅管理系统【纯干货分享,免费领源码04770】

摘 要 随着科学技术的告诉发展&#xff0c;我们已经步入数字化、网络化的时代。图书馆是学校的文献信息中心&#xff0c;是为全校教学和科学研究服务的学术性机构&#xff0c;是学校信息化的重要基地。图书馆的工作是学校和科学研究工作的重要组成部分&#xff0c;是全校师生学…

向量距离度量中的几种计算公式

距离度量 衡量两条向量之间的距离&#xff0c;可以将某一张图片通过特征提取来转换为一个特征向量。衡量两张图片的相似度就可以通过衡量这两张图片对应的两个特征向量之间的距离来判断了。 1.欧式距离 欧式距离可以简单理解为两点之间的直线距离。对于两个n维空间点 a ( x…