Java 的静态代理和动态代理

news2024/9/28 6:27:57

文章目录

  • 1. 代理设计模式
    • 1.1 为什么需要代理设计模式
    • 1.2 代理设计模式
      • 1.2.1 概念
      • 1.2.2 名词解释
      • 1.2.3 代理开发的核⼼要素
  • 2. 静态代理
    • 2.1 编码
    • 2.2 静态代理存在的问题
  • 3. 动态代理
    • 3.1 Spring动态代理的概念
    • 3.2 动态代理细节分析
    • 3.3 动态代理的实现
      • 3.3.1 JDK 动态代理
      • 3.3.2 CGlib的动态代理
  • 4. 总结


在这里插入图片描述

1. 代理设计模式

1.1 为什么需要代理设计模式

代理设计模式可以在不改变原始对象的情况下,对其进行扩展、增强或保护。 它是一种结构型设计模式,常用于解决以下问题:

  1. 访问控制:代理对象可作为一个安全接口,可以拦截请求并检查请求的参数和权限,以防止恶意代码或非法用户访问原始对象的敏感数据。
  2. 单一职责原则(SRP): 原始对象可能需要完成多项任务,其中某些任务可能与它主要的业务逻辑无关。此时,代理对象可以承担一部分原始对象的工作,将原始对象从这部分工作中解放出来,从而使得各自的职责更加明确。
  3. 性能优化: 在某些场景下,原始对象的创建和初始化成本很高且不易扩展。为了避免频繁地创建新的原始对象,使用代理对象可以通过缓存机制和懒加载优化性能,特别是当我们需要对大量的原始对象进行管理时。
  4. 透明处理:使用代理模式可以将代码中复杂的流程和算法隔离开来,提高代码的可读性和可维护性。例如,在远程调用和异步调用等场景下,代理对象可以自动处理网络通信和序列化操作相关的细节问题,使得这些复杂的操作对于用户透明不可见。

1.2 代理设计模式

1.2.1 概念

通过代理类,为原始类(⽬标)增加额外的功能

好处:利于原始类(⽬标)的维护

1.2.2 名词解释

  1. ⽬标类 原始类 指的是 业务类 (核⼼功能 --> 业务运算 DAO调⽤)
  2. ⽬标⽅法,原始⽅法 ⽬标类(原始类)中的⽅法 就是⽬标⽅法(原始⽅法)
  3. 额外功能 (附加功能) ⽇志,事务,性能

1.2.3 代理开发的核⼼要素

代理类 = ⽬标类(原始类) + 额外功能 + 原始类(⽬标类)实现相同的接⼝


2. 静态代理

静态代理:为每⼀个原始类,⼿⼯编写⼀个代理类

2.1 编码

在这里插入图片描述

2.2 静态代理存在的问题

  1. 代码冗余:需要手动创建代理对象和代理方法,这些代码会重复且不容易维护。在需要修改代理行为时,必须要同时修改原始对象和代理对象的代理方法。
  2. 耦合度高:由于代理对象与原始对象紧密相连,一旦原始对象的接口发生变化,代理对象就必须进行相应的修改。无论是代理类还是原始类的修改都会对另一个造成影响,从而增加了两个类之间的耦合度。
  3. 动态性问题: 静态代理模式只能在编译期创建新的代理类并在运行时调用,不支持运行时动态地生成代理类。如果我们需要动态地增强或扩展原始对象的功能,则可能需要使用其他代理模式,例如动态代理模式或者CGLIB代理模式。
  4. 只能代理特定类型的对象:由于代理对象对原始对象方法的调用是以编程方式执行的,因此无法实现像JDK提供的InvocationHandler那样能够动态地代理任何实现了指定接口的类的功能。

3. 动态代理

3.1 Spring动态代理的概念

概念:通过代理类为原始类(⽬标类)增加额外功能

好处:利于原始类(⽬标类)的维护

3.2 动态代理细节分析

  1. Spring 创建的动态代理类在哪⾥?

    Spring 框架在运⾏时,通过动态字节码技术,在 JVM 创建的,运⾏在 JVM 内部,等程序 结束后,会和 JVM ⼀起消失

    什么叫动态字节码技术: 通过第三个动态字节码框架,在 JVM 中创建对应类的字节码,进 ⽽创建对象,当虚拟机结束,动态字节码跟着消失。

    结论:动态代理不需要定义类⽂件,都是 JVM 运⾏过程中动态创建的,所以不会造成静态 代理,类⽂件数量过多,影响项⽬管理的问题。

    在这里插入图片描述

  2. 动态代理编程简化代理的开发

    在额外功能不改变的前提下,创建其他⽬标类(原始类)的代理对象时,只需要指 定原始(⽬标)对象即可。

  3. 动态代理额外功能的维护性⼤⼤增强


3.3 动态代理的实现

3.3.1 JDK 动态代理

  • Proxy.newProxyInstance⽅法参数详解

    返回值就是代理后的对象

    在这里插入图片描述
    在这里插入图片描述

  • 编码
    原始对象方法

    @Service
    public class UserServiceImpl implements UserService{
        @Override
        public void add(String name, Integer age) {
            System.out.println("添加 : name= " + name + ", age= " + age);
        }
    }
    

    JDK 动态代理

    public class TestJDKProxy {
        
        public static void main(String[] args) {
            //  创建原始对象
            UserService userService = new UserServiceImpl();
    
            //  JDK 创建动态代理
            InvocationHandler invocationHandler = new InvocationHandler() {
                @Override
                public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                    System.out.println("方法运行前....");
                    Object invoke = method.invoke(userService, args);
                    System.out.println("方法运行后....");
                    return invoke;
                }
            };
    
    
            UserService userServiceProxy
                    = (UserService) Proxy.newProxyInstance(userService.getClass().getClassLoader(),
                    userService.getClass().getInterfaces(), invocationHandler);
    
            userServiceProxy.add("snow", 100);
    
        }
    }
    
  • 运行结果
    在这里插入图片描述


3.3.2 CGlib的动态代理

CGlib创建动态代理的原理:⽗⼦继承关系创建代理对象,原始类作为⽗类,代理类作为⼦类, 这样既可以保证2者⽅法⼀致,同时在代理类中提供新的实现(额外功能+原始⽅法)

在这里插入图片描述

  • 编码
    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 TestCglib {
        public static void main(String[] args) {
            //1 创建原始对象
            UserService userService = new UserService();
            /*
             2 通过cglib⽅式创建动态代理对象
             Enhancer.setClassLoader()
             Enhancer.setSuperClass()
             Enhancer.setCallback(); ---> MethodInterceptor(cglib)
             Enhancer.create() ---> 代理
             */
            Enhancer enhancer = new Enhancer();
            enhancer.setClassLoader(TestCglib.class.getClassLoader());
            enhancer.setSuperclass(userService.getClass());
            MethodInterceptor interceptor = new MethodInterceptor() {
                //等同于 InvocationHandler --- invoke
                @Override
                public Object intercept(Object o, Method method,
                                        Object[] args, MethodProxy methodProxy) throws Throwable {
                    System.out.println("---cglib log----");
                    Object ret = method.invoke(userService, args);
                    return ret;
                }
            };
            enhancer.setCallback(interceptor);
            UserService userServiceProxy = (UserService)
                enhancer.create();
            userServiceProxy.login("suns", "123345");
            userServiceProxy.register(new User());
        }
    }
    

4. 总结

  1. JDK 动态代理 Proxy.newProxyInstance() 通过接⼝创建代理的实现类
  2. Cglib 动态代理 Enhancer 通过继承⽗类创建的代理类



在这里插入图片描述



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

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

相关文章

apachectl: line 79: 20233 Segmentation fault (core dumped) $HTTPD “$@“

[TOC](apachectl: line 79: 20233 Segmentation fault (core dumped) $HTTPD “$”) 1、问题描述 apache 启动报错 apachectl: line 79: 20233 Segmentation fault (core dumped) $HTTPD “$” 2、问题分析 参考链接: https://stackoverflow.com/questions/43726930/apache…

我的服务器为什么会被攻击?

时常的网站运行中,很多站长可能会遇到网站被黑客攻击的情况,其中同行之间恶性竞争,不正当的竞争关系,导致互联网关系越来越差,攻击的方式多种多样,网站遭受攻击的频次也越来越高。其次,就网络黑…

CUDA编程 - 用向量化访存优化 elementwise 核函数 - 学习记录

Cuda elementwise 一、简介1.1、ElementWise1.2、 float4 - 向量化访存 二、实践2.1、如何使用向量化访存2.2、Cuda elementwise - Add2.3、Cuda elementwise - Sigmoid2.3.1、简单的 Sigmoid 函数2.3.2、ElementWise Sigmoid float4(向量化访存) 2.4、C…

Facebook与社交创新:数字时代的社交构建者

在当今数字化时代,社交媒体已经成为人们日常生活中不可或缺的一部分。而在这个庞大的社交网络中,Facebook作为其中的巨头之一,不仅扮演着连接人们的桥梁,更是社交创新的领导者和推动者。本文将探讨Facebook在数字时代的社交构建中…

算法打卡day3|链表篇|Leetcode 203.移除链表元素、 707.设计链表 、 206.反转链表

链表基本概念 定义 链表是一种通过指针串联在一起的线性结构,每一个节点由两部分组成,一个是数据域一个是指针域(存放指向下一个节点的指针),最后一个节点的指针域指向null(空指针的意思)。其…

leetcode.无重复字符的最长字串(刷题日记)

自从刷题开始之后,就突然有种感觉。 就是在刷完题之后当时是知道方法了,但是当再次遇到知道就又不会做了,就只好打开解题观摩大佬的代码,你别说,每次都感觉自己是s13。 所以我就想通过写博客来总结一下每次做完新的题…

十一、计算机视觉-膨胀操作

文章目录 前言一、什么是膨胀二、膨胀操作的实现1.引入库 三、膨胀的原理 前言 上节我们学习了腐蚀操作,本节我们讲一下膨胀操作,膨胀和腐蚀实际上是相反的操作。上节我们把云峰这2个字周围没用的像素去掉了,但是云峰这2个字也变细了&#x…

C#,弗洛伊德-瑞文斯特(Floyd-Rivest)算法与源代码

Robert W. Floyd 1 Floyd-Rivest 算法 Floyd-Rivest 算法是一种选择算法,用于在不同元素的数组中找到第k个最小元素。它类似于快速选择算法,但在实际运行中有更好的运行时间。 和 QuickSelect 一样,该算法基于分区的思想工作。对数组进行分…

SINAMICS V90 指导手册 第2章 2.2_系统配套表

V90 PN配套表一共有三张,分别是200V低惯量配套表、400V高惯量配套表和400V带直型连接器的配套表。其中200V电压等级低惯量伺服功率范围从0.05-2kW,额定扭矩从0.16-6.37Nm,电缆长度分别是3m、5m、10m、20m四种型号;400V电压等级带直…

《数据治理简易速速上手小册》第4章 数据安全与合规性(2024 最新版)

文章目录 4.1 数据安全的基本原则4.1.1 基础知识4.1.2 重点案例:在线零售商的数据加密4.1.3 拓展案例 1:医疗机构的访问控制4.1.4 拓展案例 2:金融服务提供商的数据备份和恢复 4.2 遵循数据合规性的策略4.2.1 基础知识4.2.2 重点案例&#xf…

如何在项目中考虑非功能需求

软件的非功能需求指的是除了软件的功能需求以外,软件需要满足的一些其他需求。常见的非功能需求包括: 性能需求:软件需要在特定的时间内完成特定的任务,例如响应时间、吞吐量等。可靠性需求:软件需要在各种环境下都能…

MySQL基础(二)

文章目录 MySQL基础(二)1. 数据库操作-DQL1.1 介绍1.2 语法1.3 基本查询1.4 条件查询1.5 聚合函数1.6 分组查询1.7 排序查询1.8 分页查询1.9 案例1.9.1 案例一1.9.2 案例二 2. 多表设计2.1 一对多2.1.1 表设计2.1.2 外键约束 2.2 一对一2.3 多对多2.4 案…

电机应用中的大功率电阻器?

在这篇文章中,我们将考虑电机应用中的电阻器。 交流、直流和专用电机用于广泛的应用。一些电机应用相对简单,唯一需要关注的是电机的启动和关闭。在这里,成本、简单性和可靠性是主要问题,而电机控制电阻器是常见的解决方案。 在…

水印相机小程序源码

水印相机前端源码,本程序无需后端,前端直接导入即可,没有添加流量主功能,大家开通后自行添加 源码搜索:源码软件库 注意小程序后台的隐私权限设置,前端需要授权才可使用 真实时间地址拍照记录&#xff0c…

多线程系列(九) -ReentrantLock常用方法详解

一、简介 在上一篇文章中,我们介绍了ReentrantLock类的一些基本用法,今天我们重点来介绍一下ReentrantLock其它的常用方法,以便对ReentrantLock类的使用有更深入的理解。 二、常用方法介绍 2.1、构造方法 ReentrantLock类有两个构造方法&…

(undone) 如何计算 Hessian Matrix 海森矩阵 海塞矩阵

参考视频1:https://www.bilibili.com/video/BV1H64y1T7zQ/?spm_id_from333.337.search-card.all.click 参考视频2(正定矩阵):https://www.bilibili.com/video/BV1Ag411M76G/?spm_id_from333.337.search-card.all.click&vd_…

.NET高级面试指南专题十一【 设计模式介绍,为什么要用设计模式】

设计模式是软件工程中常用的解决特定问题的通用设计方法。它们提供了经过验证的解决方案,可用于解决在软件开发过程中经常遇到的一些常见问题。设计模式不是一种具体的编程语言特性或语法,而是一种通用的设计思想或模板,可以帮助开发人员设计…

Delphi 报错 Type androidx.collection.ArraySet is defined multiple times

Delphi 11 建立一个新的 Multi-Device Application 编译成app的时候报错 报错信息 [PAClient Error] Error: E7688 Unable to execute "E:\Program\Java\jdk1.8.0_301\bin\java.exe" -cp "e:\program\embarcadero\studio\22.0\bin\Android\r8-3.3.28.jar"…

【力扣 - 买卖股票的最佳时机】

题目描述 给定一个数组 prices ,它的第 i 个元素 prices[i] 表示一支给定股票第 i 天的价格。 你只能选择 某一天 买入这只股票,并选择在 未来的某一个不同的日子 卖出该股票。设计一个算法来计算你所能获取的最大利润。 返回你可以从这笔交易中获取的…

新的一年,如何优化企业库存管理?

随着社会的发展和经济的不断增长,库存管理成为了企业运营中非常重要的一环。库存作为企业的资产之一,直接影响着企业的盈利能力和竞争优势。因此,对企业库存进行科学的分析和管理,成为了确保企业持续稳定发展的必要手段之一。企业…