Java三种代理模式:静态代理、动态代理和CGLIB代理

news2025/1/12 22:52:34

Java三种代理模式:静态代理、动态代理和CGLIB代理

代理模式

代理模式是23种设计模式种的一种。代理模式是一种结构型设计模式,它允许为其他对象提供一个替代品或占位符,以控制对这个对象的访问。代理模式可以在不修改被代理对象的基础上,通过扩展代理类,进行一些功能的附加与增强。

代理模式的构成

适配器模式一般包含三种角色:

  • 抽象主题角色(Subject):通过接口或抽象类声明真实角色实现的业务方法。
  • 代理主题角色(Proxy):实现抽象角色,是真实角色的代理(访问层),通过真实角色的业务逻辑方法来实现抽象方法,并可以附加自己的操作。
  • 真实主题角色(RealSubject):实现抽象角色,定义真实角色所要实现的业务逻辑,供代理角色调用。

代理模式主要有三种形式,分别是静态代理、动态代理(也称JDK代理、接口代理)和CGLIB代理(在内存动态创建对象而不需要实现接口,也可属于动态代理得范畴)

静态代理

静态代理是定义父类或者接口,然后被代理对象(即目标对象)与代理对象一起实现相同的接口或者是继承相同父类。代理对象与目标对象实现相同的接口,然后通过调用相同的方法来调用目标对象的方法。

  • 优点:可不修改目标对象的功能,通过代理对象对目标功能扩展。
  • 缺点:因为代理对象需要与目标对象实现一样的接口,所以会有很多代理类,一旦接口增加方法,目标对象与代理对象都要维护。
public interface Animal {
	void eat();
}
public class Dog implements Animal {
    @Override
    public void eat() {
        System.out.println("吃吃吃");
    }
}
public class DogProxy implements Animal {
    private Animal target; //通过接口聚合目标对象
    public DogProxy(Animal target) {
        this.target = target;
    }
    @Override
    public void eat() {
        System.out.println("静态代理开始");
        target.eat();
        System.out.println("静态代理结束");
    }
}
public class Main {
    public static void main(String[] args) {
        //创建被代理对象
        Dog dog = new Dog();
        //创建代理对象, 同时将被代理对象传递给代理对象
        DogProxy dogProxy = new DogProxy(dog);
        //通过代理对象,调用到被代理对象的方法
        dogProxy.eat();
    }
}

image-20231128203835440

动态代理

动态代理是在运行时动态生成代理类,不需要手动编写代理类。Java种的动态代理主要是使用java.lang.reflect.Proxy和java.lang.reflect.InvocationHandler接口实现。

优点:灵活性高、减少重复代码、统一处理逻辑、可以代理多个真实类。
缺点:基于反射机制,性能较低,且无法代理final类和方法。

动态代理最主要的就是Proxy.newProxyInstance方法,它是用于创建动态代理对象的静态方法。它接受三个参数:

ClassLoader:用于加载动态代理类的类加载器。
interfaces:要代理的接口数组。
InvocationHandler:实现了InvocationHandler接口的对象,用于处理代理对象的方法调用。

public interface Animal {
	void eat();
}
public class Dog implements Animal {
    @Override
    public void eat() {
        System.out.println("吃吃吃");
    }
}
public class AnimalInvocationHandler implements InvocationHandler {
    private Object target;
    public AnimalInvocationHandler(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 AnimalProxy {
    public static Animal createProxy(Animal animal) {
        return (Animal) Proxy.newProxyInstance(
                animal.getClass().getClassLoader(),
                animal.getClass().getInterfaces(),
                new AnimalInvocationHandler(animal));
    }
}
public class Main {
    public static void main(String[] args) {
        Dog dog = new Dog();
        Animal dogProxy = AnimalProxy.createProxy(dog);
        dogProxy.eat();
    }
}

image-20231128205554639

JDK 动态代理有一个最致命的问题是其只能代理实现了接口的类。

为了解决这个问题,我们可以用 CGLIB 动态代理机制来避免。

CGLIB代理

CGLIB代理也叫作子类代理,它使目标对象不需要实现接口,是在内存中构建一个子类对象从而实现对目标对象功能扩展,有的也将CGLIB代理归属到动态代理。

CGLIB是一个高性能的代码生成包,它可以在运行期扩展java类与实现java接口。被许多AOP的框架使用(如Spring AOP)。Cglib包的底层是通过使用字节码处理框架ASM来转换字节码并生成新的类。

  • 优点:可以为没有实现接口的类提供代理;性能比动态代理更高
  • 缺点:生成的代理类较大、不支持final方法和类、对于final类和方法的处理相对复杂。
public class Dog {
    public void eat() {
        System.out.println("吃吃吃");
    }
}
public class AnimalMethodInterceptor implements MethodInterceptor {
    @Override
    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
        System.out.println("吃前加热");
        Object result = methodProxy.invokeSuper(o, objects);
        System.out.println("吃后清理");
        return result;
    }
}
public class DogCglibProxy {
    public static Dog createProxy() {
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(Dog.class);
        enhancer.setCallback(new AnimalMethodInterceptor());
        return (Dog) enhancer.create();
    }
}
public class Main {
    public static void main(String[] args) {
        Dog dogProxy = DogCglibProxy.createProxy();
        dogProxy.eat();
    }
}

image-20231128211037905

CGLIB与java动态代理的区别

  1. 实现方式:
    • Java动态代理:使用java.lang.reflect.ProxyInvocationHandler接口。Java动态代理只能为接口创建代理对象,它是基于接口的代理。通过Proxy.newProxyInstance()方法可以动态地生成实现了指定接口的代理类。
    • CGLIB:通过继承目标类的方式创建代理对象。CGLIB可以为类创建代理,而不仅仅是接口。它通过生成目标类的子类,在子类中增加代理逻辑来实现动态代理。
  2. 代理对象类型:
    • Java动态代理:只能代理实现了接口的类。它要求目标对象实现一个或多个接口,然后通过代理对象来实现这些接口。
    • CGLIB:可以代理没有实现任何接口的类。它通过继承目标类来创建代理对象,因此目标类不需要实现任何接口。
  3. 性能:
    • Java动态代理:由于生成的代理对象是基于接口的,因此在调用代理方法时,会通过接口的方法调用InvocationHandlerinvoke方法,再由invoke方法调用实际的目标方法。这一层额外的调用可能会引入一些性能开销。
    • CGLIB:生成的代理对象是目标类的子类,因此调用代理方法时,直接调用子类中的方法,避免了通过接口的中间层,可能会在一些情况下具有更好的性能。
  4. 构造方式:
    • Java动态代理:通过Proxy.newProxyInstance()方法动态生成代理对象,需要提供一个实现InvocationHandler接口的对象。
    • CGLIB:通过CGLIB库动态生成代理对象,无需提供InvocationHandler。CGLIB通过继承目标类并重写其中的方法来实现代理逻辑。

应用场景

代理模式可以在多种场景下使用,包括但不限于以下几个方面:

  1. 访问控制:代理模式可以用来控制对实际对象的访问权限。比如,只有特定用户或角色才能访问某些敏感数据。
  2. 远程访问:代理模式可以用来处理远程对象的访问。比如,通过代理对象来访问远程Web服务。
  3. 延迟加载:代理模式可以用来实现延迟加载。比如,通过代理对象来加载某些资源或数据,以避免在程序启动时就加载所有数据。
  4. 虚拟代理:当需要延迟加载或预加载大量数据时,可以使用虚拟代理来提高程序的性能和效率。

应用场景

代理模式可以在多种场景下使用,包括但不限于以下几个方面:

  1. 访问控制:代理模式可以用来控制对实际对象的访问权限。比如,只有特定用户或角色才能访问某些敏感数据。
  2. 远程访问:代理模式可以用来处理远程对象的访问。比如,通过代理对象来访问远程Web服务。
  3. 延迟加载:代理模式可以用来实现延迟加载。比如,通过代理对象来加载某些资源或数据,以避免在程序启动时就加载所有数据。
  4. 虚拟代理:当需要延迟加载或预加载大量数据时,可以使用虚拟代理来提高程序的性能和效率。
  5. 缓存代理:当需要对经常使用的数据进行缓存时,可以使用缓存代理来管理和优化数据的访问效率。

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

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

相关文章

JavaScript基础知识20——循环结构:退出循环

哈喽,大家好,我是雷工! 最近一段时间没学习JavaScript,今天看数字孪生的资料,发现很多低代码开发还是得必须熟悉JavaScript才行,为了以后方便搞数字孪生,有时间还是继续学习下JavaScript。 以下…

矩阵元素求和:按行、按列、所有元素np.einsum()

【小白从小学Python、C、Java】 【计算机等考500强证书考研】 【Python-数据分析】 矩阵元素求和: 按行、按列、所有元素 np.einsum() [太阳]选择题 下列说法正确的是: import numpy as np A np.array([[1, 2],[3, 4]]) print("【显示】A") p…

HR看好的字符函数和字符串处理函数!!!

本篇会加入个人的所谓‘鱼式疯言’❤️❤️❤️鱼式疯言:❤️❤️❤️此疯言非彼疯言,而是理解过并总结出来通俗易懂的大白话,我会尽可能的在每个概念后插入鱼式疯言,帮助大家理解的,可能说的不是那么严谨.但小编初心是能让更多人能接受我们这个概念 前言 在本篇…

【Google2023】利用TiDE进行长期预测实战(时间序列密集编码器)

一、本文介绍 大家好,最近在搞论文所以在研究各种论文的思想,这篇文章给大家带来的是TiDE模型由Goggle在2023.8年发布,其主要的核心思想是:基于多层感知机(MLP)构建的编码器-解码器架构,核心创…

【设计模式-4.3】行为型——责任链模式

说明:本文介绍设计模式中行为型设计模式中的,责任链模式; 审批流程 责任链模式属于行为型设计模式,关注于对象的行为。责任链模式非常典型的案例,就是审批流程的实现。如一个报销单的审批流程,根据报销单…

44 - 几款常用的性能测试工具

熟练掌握一款性能测试工具,是我们必备的一项技能。他不仅可以帮助我们模拟测试场景(包括并发、复杂的组合场景),还能将测试结果转化成数据或图形,帮助我们更直观地了解系统性能。 1、常用的性能测试工具 常用的性能测…

前端笔记(二):CSS 选择器与特性

CSS(层叠样式表)是一种样式表语言,用于描述HTML或XML文档的呈现方式。它定义了如何在屏幕、纸张或其他媒体上显示文档的样式、布局和外观。 里面的代码由 选择器 { } 组成 体验 CSS CSS 可以让我们界面变得更加美观,这是 CSS 的…

Kafka 架构深度解析:生产者(Producer)和消费者(Consumer)

Apache Kafka 作为分布式流处理平台,其架构中的生产者和消费者是核心组件,负责实现高效的消息生产和消费。本文将深入剖析 Kafka 架构中生产者和消费者的工作原理、核心概念以及高级功能。 Kafka 生产者(Producer) 1 发送消息到…

RPG项目01_脚本代码

基于“RPG项目01_场景及人物动画管理器”,我们创建一个XML文档 在资源文件夹下创建一个文件夹, 命名为Xml 将Xnl文档拖拽至文件夹中, 再在文件夹的Manager下新建脚本LoadManager 写代码: using System.Collections; using System…

基于CNN对彩色图像数据集CIFAR-10实现图像分类--keras框架实现

项目地址(kaggle):基于CNN对彩色图像数据集CIFAR-10实现图像分类--keras | Kaggle 项目地址(Colab):https://colab.research.google.com/drive/1gjzglPBfQKuhfyT3RlltCLUPgfccT_G9 导入依赖 在tensorflow…

若依的基本使用

演示使用网址:若依管理系统 网站:RuoYi 若依官方网站 |后台管理系统|权限管理系统|快速开发框架|企业管理系统|开源框架|微服务框架|前后端分离框架|开源后台系统|RuoYi|RuoYi-Vue|RuoYi-Cloud|RuoYi框架|RuoYi开源|RuoYi视频|若依视频|RuoYi开发文档|若依开发文档|Java开源框架…

绝地求生在steam叫什么?

绝地求生在Steam的全名是《PlayerUnknowns Battlegrounds》,简称为PUBG。作为一款风靡全球的多人在线游戏,PUBG于2017年3月23日正式上线Steam平台,并迅速成为一部热门游戏。 PUBG以生存竞技为核心玩法,玩家将被投放到一个辽阔的荒…

基于 ESP32 的带触摸显示屏的 RFID 读取器

如何设计一款基于 ESP32 且具有 ILI9341 触摸屏显示屏且适合壁挂式安装的美观 RFID 读取器。 本项目中用到的东西 硬件组件 ESP32 开发套件 C 1 AZ-Touch ESP 套件 1 RFID-RC522 IC卡读写器 1 ​编辑 电线、绕包线 1 详细设计流程 …

结构体实现位段

一.什么是位段 位段的声明和结构是类似的,有两个不同: 位段的成员必须是 int、unsigned int 或 signed int ,在C99中位段成员的类型也可以 选择其他类型。 位段的成员名后边有⼀个冒号和⼀个数字 struct A {int a : 5;int b : 4;int c : 2…

SpringBoot的配置加载优先级

目录 一、背景分析 二、学习资源 三、具体使用 四、一些小技巧 方式一 方式二 一、背景分析 SpringBoot项目在打包之后&#xff0c;其配置文件就在jar包内&#xff0c;如果没有<配置文件优先级>这个机制&#xff0c;那么项目打成jar包之后&#xff0c;如果启动项目…

avue-crud中时间范围选择默认应该是0点却变成了12点

文章目录 一、问题二、解决三、最后 一、问题 在avue-crud中时间范围选择&#xff0c;正常默认应该是0点&#xff0c;但是不知道怎么的了&#xff0c;选完之后就是一直是12点。具体问题如下动图所示&#xff1a; <template><avue-crud :option"option" /&g…

数据结构入门————树(C语言/零基础/小白/新手+模拟实现+例题讲解)

目录 1. 树的概念及其结构 1.1 树的概念&#xff1a; 1.2 树的相关概念&#xff1a; 1.3 树的表示方法&#xff1a; ​编辑 1.4 树的应用&#xff1a; 2. 二叉树的概念及其结构 2.1 概念: 2.2 特点&#xff1a; 2.3 特殊二叉树&#xff1a; 2.4 二叉树的性质&#xf…

shell编程-awk命令详解(超详细)

文章目录 前言一、awk命令介绍1. awk命令简介2. awk命令的基本语法3. 常用的awk命令选项4. 常用的awk内置变量 二、awk命令示例用法1. 打印整行2. 打印特定字段3. 根据条件筛选行4. 自定义分隔符5. 从文件中读取awk脚本 总结 前言 awk命令是一种强大的文本处理工具&#xff0c…

Qt OpenCV 学习(一):环境搭建

对应版本 Qt 5.15.2OpenCV 3.4.9MinGW 8.1.0 32-bit 1. OpenCV 下载 确保安装 Qt 时勾选了 MinGW 编译器 本文使用 MinGW 编译好的 OpenCV 库&#xff0c;无需自行编译 确保下载的 MinGW 和上述安装 Qt 时勾选的 MinGW 编译器位数一致&#xff0c;此处均为 x86/32-bit下载地址…

LongAddr

目录 1. 引言 2. AtomicInteger的局限性 3. AtomicInteger与LongAdder 的性能差异 4.LongAdder 的结构 LongAddr架构 Striped64中重要的属性 Striped64中一些变量或者方法的定义 Cell类 5. 分散热点的原理 具体流程图 6. 在实际项目中的应用 7. 总结 1. 引言 在这一…