代理模式以及静态代理、JDK代理、Cglib代理的实现

代理模式(Proxy)

介绍

1、代理模式:为对象提供一个替身,以控制对这个对象的访问,即通过代理对象访问目标对象,这样做的好处是:可以在目标对象实现的基础上,增强额外的功能操作

(即:扩展目标对象的功能,例如Spring AOP)

2、被代理的对象可以是远程对象,创建开销大的对象或需要安全控制的对象

3、代理模式有不同的形式,主要有三种:

  • 静态代理(也是一种基于接口的代理)

  • 动态代理(JDK代理/接口代理)

  • Cglib代理(在内存中动态创建对象,而不需要实现接口,属于动态代理的范畴)

这里是代理的示意图:

image-20240308112221180

静态代理

静态代理在使用时,需要定义接口或父类,被代理对象(即目标对象)与代理对象一起实现相同的接口或者是继承相同的接口

代理对象与目标对象实现相同的接口,通过调用相同的方法来调用目标对象方法。

代码示例

// 定义接口
interface Subject {
    void doAction();
}
​
// 目标对象
class RealSubject implements Subject {
    @Override
    public void doAction() {
        System.out.println("RealSubject is doing action");
    }
}
​
// 代理对象
class ProxySubject implements Subject {
//这里聚合一个RealSubject对象
    private RealSubject realSubject;
​
    public ProxySubject(RealSubject realSubject) {
        this.realSubject = realSubject;
    }
​
    @Override
    public void doAction() {
        System.out.println("ProxySubject is doing something before action");
        realSubject.doAction();
        System.out.println("ProxySubject is doing something after action");
    }
}
​
// 测试类
public class StaticProxyExample {
    public static void main(String[] args) {
        RealSubject realSubject = new RealSubject();
        ProxySubject proxySubject = new ProxySubject(realSubject);
​
        // 通过代理对象调用目标对象的方法
        proxySubject.doAction();
    }
}

image-20240308113224968

小结

优点:

  1. 易于理解和实现: 静态代理模式相对简单,易于理解和实现,适用于一些简单的代理场景。

  2. 对目标对象进行控制: 通过代理可以对目标对象的访问进行控制和管理,例如在调用目标对象方法前后进行额外的处理。

  3. 实现业务功能的解耦: 代理对象可以在不修改目标对象的情况下,增加额外的功能,实现了业务功能的解耦,提高了系统的可维护性。

  4. 保护目标对象: 代理对象可以充当一个保护层,控制用户对目标对象的访问权限,增加系统的安全性。

缺点:

  1. 静态: 静态代理在编译时就已经确定了代理类和目标类,如果需要代理多个类或者频繁地更换代理对象,会导致代码量增加,不利于系统的扩展和维护。

  2. 灵活性差: 静态代理模式的灵活性较低,一旦接口或者目标对象发生变化,代理类也需要相应地修改,不够灵活。

  3. 代码重复: 如果有多个接口或者多个目标对象需要被代理,会导致代理类的重复设计和编码,增加了开发工作量。

  4. 只能代理固定的目标对象: 静态代理模式在编译时就已经确定了代理类和目标类的关系,无法动态地切换或者增加新的代理对象。

总的来说,静态代理模式在一些简单的场景下是适用的,但在面对复杂的系统、多样的需求和频繁变化的代理对象时,可能会显得不够灵活和可扩展。在这种情况下,可以考虑使用动态代理模式或者其他结构型模式来实现更灵活、可扩展的代理功能。

动态代理

介绍

1、代理对象不需要实现接口,但是目标对象要实现接口,否则不能用动态代理

2、代理对象的生成,是利用JDK的API,动态的在内存中构建代理对象

3、动态代理又称:JDK代理,接口代理

JDK中生成代理对象的API

1、代理类所在包:java.lang.reflect.Proxy

2、JDK实现代理只需要使用newProxyInstance方法,但是该方法需要接收3个参数,完整写法是:

static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)

代码示例

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
​
// 定义接口
interface Subject {
    void doAction();
}
​
// 目标对象
class RealSubject implements Subject {
    @Override
    public void doAction() {
        System.out.println("目标对象正在执行");
    }
}
​
// InvocationHandler 实现类
class ProxyHandler implements InvocationHandler {
    private Object target;
​
    public ProxyHandler(Object target) {
        this.target = target;
    }
​
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("目标对象执行前。。。");
        Object result = method.invoke(target, args);
        System.out.println("目标对象执行后。。。");
        return result;
    }
}
​
// 测试类
public class DynamicProxyExample {
    public static void main(String[] args) {
        RealSubject realSubject = new RealSubject();
​
        // 创建动态代理对象
        //newProxySubject的三个参数:
        //1、ClassLoader:指定当前目标对象使用的类加载器,获取加载器的方法固定
        //2、Class<?>[] interfaces:目标对象实现的接口类型,使用泛型方法确定类型
        //3、InvocationHandler h:事情处理。执行目标对象方法时,会触发事情处理方法,会把当前执行的目标对象方法作为参数
        Subject proxySubject = (Subject) Proxy.newProxyInstance(
                Subject.class.getClassLoader(),
                new Class[]{Subject.class},
                new ProxyHandler(realSubject));
​
        // 通过代理对象调用目标对象的方法
        proxySubject.doAction();
    }
}
​

image-20240308130544273

小结

优点:

  1. 灵活性: 动态代理模式相比静态代理模式更加灵活,可以在运行时动态地创建代理对象,无需提前知道目标对象的具体类型。

  2. 减少代码量: 动态代理可以通过反射机制自动生成代理类,减少了手动编写代理类的代码量。

  3. 适应性强: 动态代理模式适用于多个目标类共享一个代理类或者频繁切换代理对象的场景。

  4. 可扩展性: 动态代理模式可以通过自定义 InvocationHandler 的实现类,实现对目标对象方法的统一处理,并且可以根据需求灵活地扩展功能。

缺点:

  1. 性能影响: 动态代理在运行时通过反射机制调用目标对象的方法,相比直接调用目标对象的方法,性能上会有一定的影响。

  2. 复杂性增加: 动态代理模式相对静态代理模式来说,涉及到更多的类和概念,代码结构可能会变得更加复杂,不利于代码的理解和维护。

  3. 无法代理私有方法: 动态代理无法直接代理目标对象中的私有方法,只能代理公共方法。

动态代理模式在一些需要灵活性和可扩展性的场景下非常有用。它可以减少代码量、提高代码的可复用性,并且可以在运行时动态地创建代理对象。然而,它也存在一些性能影响和复杂性增加的问题,需要根据具体情况权衡使用。

Cglib代理

介绍

1、静态代理和JDK代理都要求目标对象实现一个接口,但是有时候目标对象只是一个单独的对象,并没有实现任何的接口,这个时候可使用目标对象子类来实现代理——Cglib代理

2、Cglib代理也叫做子类代理,它是在内存中构建一个子类对象从而实现对目标对象功能扩展,有些地方也将Cglib代理归属动态代理

3、Cglib是一个强大的高性能的代码生成包,它可以在运行期扩展Java类与实现Java接口。

4、Cglib代理广泛使用在AOP框架中,例如Spring AOP,实现方法拦截。

在AOP编程中,如何选择代理模式呢?

  • 目标对象需要实现接口——JDK代理

  • 目标对象不需要实现接口——Cglib代理

5、Cglib包底层是通过使用字节码处理框架ASM来转换字节码并生成新的类

实现步骤

  1. 引入cglib的jar文件

     <dependency>
            <groupId>cglib</groupId>
            <artifactId>cglib</artifactId>
            <version>3.4.1</version>
        </dependency>
  2. 因为在内存中动态构建子类,代理类不能是final,否则会报错

  3. 目标对象的方法如果是final/static,那么就不会被拦截,即不会执行目标对象额外的业务方法。

import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
​
import java.lang.reflect.Method;
​
/**
 * cglib代理测试
 */
​
class HelloWorld {
​
    public void sayHello() {
        System.out.println("CGLIB动态代理模式!");
    }
​
}
class CGLIBExampleProxy implements MethodInterceptor {
​
    /**
     * 指定cglib代理模式的代理类
     */
​
    private Object target;
​
    public Object bind(Object target) {
        this.target = target;
        Enhancer enhancer = new Enhancer();
        //设置超类方法
        enhancer.setSuperclass(this.target.getClass());
        //设置一个回调方法,用来设置哪个类为代理类,this表示当前类为代理类
        enhancer.setCallback(this);
        //创建代理对象
        return enhancer.create();
​
    }
​
    //重写接口中的intercept方法,增强并执行代理方法
    public Object intercept(Object obj,
                            Method method, Object[] args,
                            MethodProxy proxy) throws Throwable {
        System.out.println("CGLIB代理前");
        Object object = proxy.invokeSuper(obj, args);
        System.out.println("CGLIB代理后");
        return object;
    }
}
public class CglibProxy {
    public static void main(String[] args) {
        //1.获取代理对象
        CGLIBExampleProxy proxy=new CGLIBExampleProxy();
        //2.获取代理对象
        HelloWorld hello=(HelloWorld)proxy.bind(new HelloWorld());
        //3.执行代理方法
        hello.sayHello();
    }
}

image-20240308135222605

如果报错

原因:
因为在使用 CGLIB 进行代理时,出现了对 Java 9+ 模块系统的限制导致的异常。在 Java 9+ 中,模块系统对于反射和类加载引入了更严格的访问控制。
​
解决:
尝试通过添加以下 JVM 参数来打开 java.base 模块的反射权限:
--add-opens java.base/java.lang=ALL-UNNAMED

image-20240308164455740

image-20240308164518694

image-20240308164556312

image-20240308164646127

image-20240308164744115

小结

优点:

  1. 不需要接口: CGLib 动态代理不要求目标对象实现接口,可以直接代理目标类的方法,这样就避免了 JDK 动态代理必须基于接口的限制。

  2. 性能高: CGLib 动态代理是通过生成目标类的子类来实现代理,因此在调用目标方法时比 JDK 动态代理更快,无需通过反射调用。

  3. 灵活性: CGLib 可以代理非公共的类,包括 final 类。同时,CGLib 也支持对类中的 final 方法进行代理。

  4. 功能丰富: CGLib 提供了丰富的 API,可以在代理过程中实现更复杂的逻辑,满足更多场景的需求。

缺点:

  1. 引入依赖: 使用 CGLib 动态代理需要引入额外的库,增加了项目的依赖性,可能会对项目构建和部署造成一定影响。

  2. 类加载器: 由于 CGLib 是通过生成目标类的子类来实现代理,因此需要使用字节码技术,在某些情况下可能会导致类加载问题或者冲突。

  3. 无法代理静态方法: CGLib 无法代理目标对象中的静态方法,因为动态代理实际上是通过生成目标对象的子类来实现代理,而静态方法是属于类级别的而不是实例级别的。

  4. 内存占用: 由于 CGLib 动态代理是通过生成子类来实现代理,可能会占用更多的内存,特别是在代理大量对象时可能会影响系统性能。

总的来说,CGLib 动态代理适合对类进行代理,无需目标对象实现接口的情况下使用。它具有高性能、灵活性强等优点,但也存在一些缺点,如引入依赖、无法代理静态方法等。在选择动态代理方式时,需要根据具体需求和场景来权衡选择适合的代理方式。

几种常见的代理模式介绍——几种变体

1、防火墙代理

内网通过代理穿透防火墙,实现对公网的访问

2、缓存代理

比如:当请求图片文件等资源时,先到缓存代理取,如果取不到,就到公网或数据库中取

3、远程代理 把远程对象当本地对象来调用。远程代理通过网络和真正的远程对象沟通信息

4、同步代理

用于多线程编程中,完成多线程间同步工作。

通过一个代理的服务器去访问真正提供服务的。

代理模式总结

本人理解:通过一个代理对象访问并增强另一个对象的功能。

代理模式是一种结构型设计模式,其目的是通过引入一个代理对象来控制对另一个对象的访问。代理对象可以在不改变原始对象的情况下,对其进行一些额外的操作,如控制对原始对象的访问权限、延迟加载、缓存数据、记录日志等。

角色

  • 抽象主题(Subject):定义了代理对象和真实对象的共同接口,客户端可以通过该接口访问真实对象。

  • 真实主题(Real Subject):实际执行业务逻辑的对象。

  • 代理(Proxy):包含了对真实主题的引用,并提供与真实主题相同的接口,负责控制对真实主题的访问。

优势

  • 代理对象可以拦截对真实对象的访问,可以在调用真实对象之前或之后执行一些额外的逻辑。

  • 可以实现延迟加载:代理对象可以延迟创建或加载真实对象,从而提高系统性能和节省资源。

  • 实现访问控制:代理对象可以控制对真实对象的访问权限,实现安全性控制。

  • 实现缓存:代理对象可以缓存真实对象的结果,避免重复计算或获取数据。

缺点

  • 增加系统复杂度

  • 性能损耗

  • 可能引入单点故障

  • 可能导致循环依赖

应用场景

  • 远程代理:控制访问远程对象。

  • 虚拟代理:控制访问创建开销大的对象。

  • 缓存代理:为耗时的操作结果提供缓存。

  • 保护代理:控制对敏感对象的访问权限。

  • 智能引用:在访问对象时执行额外操作,如引用计数。

代理模式能够增加代码的灵活性和可维护性,同时提供了一种优雅的方式来控制对象的访问和行为。通过合理地使用代理模式,可以更好地实现代码解耦和功能扩展。

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

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

相关文章

2024 AI 辅助研发的新纪年

随着人工智能技术的持续发展与突破&#xff0c;2024年AI辅助研发正成为科技界和工业界瞩目的焦点。从医药研发到汽车设计&#xff0c;从软件开发到材料科学&#xff0c;AI正逐渐渗透到研发的各个环节&#xff0c;变革着传统的研发模式。在这一背景下&#xff0c;AI辅助研发不仅…

不允许你不知道Python函数的返回值

函数可以通过return一次性返回多个数据&#xff0c;返回的数据以元组的形式保存。函数中若出现了return&#xff0c;return后面的程序不会执行。若函数中需要将某些数据返回&#xff0c;使用关键字return。若return后面没有返回任何数据&#xff0c;仅仅表示结束程序&#xff0…

Haproxy介绍、安装

Haproxy介绍、安装 文章目录 Haproxy介绍、安装1.Haproxy介绍1.1 企业版1.2 社区版1.3 版本对比1.4 HAProxy功能 2.HAProxy安装2.1 主机初始化2.1.1 设置网卡名和ip地址2.1.2 配置镜像源2.1.3 关闭防火墙2.1.4 禁用SELinux2.1.5 设置时区 2.2 包安装2.2.1 Ubuntu 安装2.2.2 Cen…

WEB接口测试之Jmeter接口测试自动化 (二)(数据分离)

&#x1f525; 交流讨论&#xff1a;欢迎加入我们一起学习&#xff01; &#x1f525; 资源分享&#xff1a;耗时200小时精选的「软件测试」资料包 &#x1f525; 教程推荐&#xff1a;火遍全网的《软件测试》教程 &#x1f4e2;欢迎点赞 &#x1f44d; 收藏 ⭐留言 &#x1…

数据结构八大算法详解

一、直接插入排序 直接插入排序的核心思想就是&#xff1a;将数组中的所有元素依次跟前面已经排好的元素相比较&#xff0c;如果选择的元素比已排序的元素小&#xff0c;则交换&#xff0c;直到全部元素都比较过。 因此&#xff0c;从上面的描述中我们可以发现&#xff0c;直接…

非常好用的Java诊断工具Arthas(阿尔萨斯)

小伙伴们好&#xff0c;欢迎关注&#xff0c;一起学习&#xff0c;无限进步 文章目录 Window 安装Linux 安装SpringBoot 整合 arthas其他使用场景 Arthas是阿里巴巴开源的一款Java诊断工具&#xff0c;可以用于线上诊断问题、监控应用性能等。支持 Linux/Mac/Windows&#xff0…

【学习笔记】开源计算机视觉库OPENCV学习方案

本文中&#xff0c;我们试图提供一些学习OpenCV的详细和实用资源&#xff0c;这些资源包括基础知识、进阶技巧以及实践项目&#xff0c;旨在帮助初学者和进阶学习者更好地掌握和使用OpenCV库。 一、学习资源 官方文档&#xff1a;OpenCV的官方文档是学习OpenCV的最佳起点。它包…

MySQL-Linux安装

JDK安装&#xff08;linux版&#xff09; CentOS7环境&#xff1a; jdk下载地址huaweicloud.com 创建目录&#xff1a; mkdir /opt/jdk通过 ftp 客户端 上传 jdk压缩包&#xff08;linux版本&#xff09;到 1中目录进入目录&#xff1a;cd /opt/jdk解压&#xff1a;tar -zxv…

TRON Builder Tour 丹佛站活动获美联社等外媒高度评价

近日,波场TRON亮相ETH Denver大会并在期间成功举办TRON Builder Tour丹佛站活动,引发海外媒体热议。美联社、费加罗报、Cointelegraph等国际主流媒体及加密行业权威媒体均对此给予了高度评价,认为波场TRON在参会期间的表现及TRON Builder Tour活动的举办都展现了其坚定不移推动…

【Spring云原生系列】Spring RabbitMQ:异步处理机制的基础--消息队列 原理讲解+使用教程

&#x1f389;&#x1f389;欢迎光临&#xff0c;终于等到你啦&#x1f389;&#x1f389; &#x1f3c5;我是苏泽&#xff0c;一位对技术充满热情的探索者和分享者。&#x1f680;&#x1f680; &#x1f31f;持续更新的专栏《Spring 狂野之旅&#xff1a;从入门到入魔》 &a…

业务代码中如何使用装饰器模式?

装饰器模式&#xff08;Decorator Pattern&#xff09;介绍 装饰器模式&#xff08;Decorator Pattern&#xff09;是一种结构型设计模式&#xff0c;我们可以动态地给一个对象添加额外的职责。而不是通过继承增加子类的方式来扩展对象的功能&#xff0c;装饰器模式使用组合的…

二维码门楼牌管理系统应用场景:社会服务与福利的智能化革新

文章目录 前言一、二维码门楼牌管理系统的基本功能二、在社会服务领域的应用三、在福利保障领域的应用四、结论 前言 在数字化浪潮的推动下&#xff0c;二维码门楼牌管理系统以其便捷、高效的特点&#xff0c;正逐渐渗透到社会服务和福利的各个领域。这一系统不仅为市民提供了…

【深入理解设计模式】享元设计模式

享元设计模式 概述 享元设计模式&#xff08;Flyweight Design Pattern&#xff09;是一种用于性能优化的设计模式&#xff0c;它通过共享尽可能多的相似对象来减少对象的创建&#xff0c;从而降低内存使用和提高性能。享元模式的核心思想是将对象的共享部分提取出来&#xff…

lqb省赛日志[2/37]

一只小蒟蒻备考蓝桥杯的日志 文章目录 笔记&#xff01;lqb不能用to_string和atoi历史遗留问题1 刷题心得小结 笔记 &#xff01;lqb不能用to_string和atoi 有替代方法 参考 不使用C 11的整数转字符串 C语言提供了一种方法。 #include sstream &#xff08;我没学&#xff0…

MySQL面试题-锁(答案版)

锁 1、MySQL 有哪些锁&#xff1f; &#xff08;1&#xff09;全局锁 加了全局锁之后&#xff0c;整个数据库就处于只读状态了&#xff0c;这时其他线程执行以下操作&#xff0c;都会被阻塞&#xff1a; 对数据的增删改操作&#xff0c;比如 insert、delete、update等语句&…

【粉丝福利】探秘内部审计数字化之道:精准解析转型方法与成功路径

&#x1f33c;前言 内部审计是一种独立的、客观的确认和咨询活动&#xff0c;包括鉴证、识别和分析问题以及提供管理建议和解决方案。狭义的数字化转型是指将企业经营管理和业务操作的各种行为、状态和结果用数字的形式来记录和存储&#xff0c;据此再对数据进行挖掘、分析和应…

【Linux】--- Linux编译器-gcc/g++、调试器-gdb、项目自动化构建工具-make/Makefile 使用

目录 一、Linux编译器-gcc/g1.1 gcc/g 使用方法1.2 程序的翻译过程1.3 链接 -- 动静态链接特点及区别 二、Linux调试器-gdb2.1 背景2.2 使用方法 三、Linux项目自动化构建工具-make/Makefile3.1 背景3.2 原理3.3 项目清理 一、Linux编译器-gcc/g 1.1 gcc/g 使用方法 格式&…

Linux mmap系统调用

文章目录 前言一、mmap()函数简介二、代码演示2.1 mmap使用场景2.2 私有匿名映射2.3 私有文件映射2.4 共享匿名映射2.5 共享文件映射 参考 前言 NAMEmmap, munmap - map or unmap files or devices into memorySYNOPSIS#include <sys/mman.h>void *mmap(void *addr, siz…

如何使用 ArcGIS Pro 制作三维地形图

伴随硬件性能的提高和软件算法的优化&#xff0c;三维地图的应用场景会越来越多&#xff0c;这里为大家介绍一下在ArcGIS Pro怎么制作三维地形图&#xff0c;希望能对你有所帮助。 数据来源 教程所使用的数据是从水经微图中下载的DEM和影像数据&#xff0c;除了DEM和影像数据…

Python数据处理实战(4)-上万行log数据提取并作图进阶版

系列文章&#xff1a; 0、基本常用功能及其操作 1&#xff0c;20G文件&#xff0c;分类&#xff0c;放入不同文件&#xff0c;每个单独处理 2&#xff0c;数据的归类并处理 3&#xff0c;txt文件指定的数据处理并可视化作图 4&#xff0c;上万行log数据提取并作图进阶版&a…