06、Spring AOP
在我们接下来聊Spring AOP之前我们先了解一下设计模式中的代理模式。
一、代理模式
代理模式是23种设计模式中的一种,它属于结构型设计模式。
对于代理模式的理解:
- 程序中对象A与对象B无法直接交互,如:有人要找某个公司的老总得先打前台登记传达
- 程序中某个功能需要在原基础上增强,如:摄像头本来是来录像的,但是现在有一种摄像头除了有基本的录像功能,还可以自动检测到异常后联网报警
- 程序中某个对象需要被保护,它不支持面对客户,这个时候可以使用一个代理类对象来与客户进行交互,对于客户来说使用这个代理类对象与使用被保护的目标对象没有区别
- 程序在在调用目标对象的前后需要做一些额外的工作,如:记录下谁调用了这个目标方法,调用的效率如何,是否要校验权限来保证安全
代理模式的作用:为其他对象提供一种代理以控制对这个对象的访问。在有些情况下,一个客户不想或者不能直接引用一个对象,这个时候就可以通过一个称为"代理"的第三者来实现间接引用。代理对象可以在客户端和目标对象之间起到中介的作用,并且可以通过代理对象去掉客户端不应该看到的内容和服务或者是添加客户需要的额外服务。通过引用一个新的对象来实现对真实对象的操作或者把新的对象作为真实对象的一个”替身“。这种实现机制就是代理模式。
代理模式中的角色有哪些:
- 代理类(代理角色)
- 目标类(真实目标角色)
- 代理类与目标类的公共接口(抽象角色)

代理类在代码实现上分为两种形式:
动态代理
静态代理
静态代理
示例有如下的接口与实现类
public interface OrderService {/*** 生成订单*/void generate();/*** 查看订单详情*/void detail();/*** 修改订单*/void modify();
}
public class OrderServiceImpl implements OrderService {@Overridepublic void generate() {try {Thread.sleep(1520);} catch (InterruptedException e) {e.printStackTrace();}System.out.println("订单生成成功");}@Overridepublic void detail() {try {Thread.sleep(2300);} catch (InterruptedException e) {e.printStackTrace();}System.out.println("订单信息如下:*******");}@Overridepublic void modify() {try {Thread.sleep(1020);} catch (InterruptedException e) {e.printStackTrace();}System.out.println("订单修改成功");}
}
上面是正常的业务代码,当项目上线一段时间后,发现系统运行有些慢,这个时候我们希望知道是哪里慢了,这个需要如何处理呢?
处理方案一:最直接的方式是,在实现类中每个方法运行代码中添加统计耗时的逻辑
public class OrderServiceImpl implements OrderService {@Overridepublic void generate() {long begin = System.currentTimeMillis();try {Thread.sleep(1520);} catch (InterruptedException e) {e.printStackTrace();}System.out.println("订单生成成功");System.out.println("耗时:" + (System.currentTimeMillis() - begin) + "ms");}@Overridepublic void detail() {long begin = System.currentTimeMillis();try {Thread.sleep(2300);} catch (InterruptedException e) {e.printStackTrace();}System.out.println("订单信息如下:*******");System.out.println("耗时:" + (System.currentTimeMillis() - begin) + "ms");}@Overridepublic void modify() {long begin = System.currentTimeMillis();try {Thread.sleep(1020);} catch (InterruptedException e) {e.printStackTrace();}System.out.println("订单修改成功");System.out.println("耗时:" + (System.currentTimeMillis() - begin) + "ms");}
}
这种方式的优点是逻辑清晰,是可行的方案,但是对于功能的修改要改源码,这违背了OCP开闭原则。
第二种方案:编写一个子类,这个子类继承现在的实现类,在这个子类中重写每个方法,添加统计耗时的逻辑
public class OrderServiceImplSub extends OrderServiceImpl{@Overridepublic void generate() {long begin = System.currentTimeMillis();super.generate();System.out.println("耗时:" + (System.currentTimeMillis() - begin) + "ms");}@Overridepublic void detail() {long begin = System.currentTimeMillis();super.detail();System.out.println("耗时:" + (System.currentTimeMillis() - begin) + "ms");}@Overridepublic void modify() {long begin = System.currentTimeMillis();super.modify();System.out.println("耗时:" + (System.currentTimeMillis() - begin) + "ms");}
}
这种方案也是可行的,对扩展开放,对修改关闭,符合OCP开闭原则,但是有如下问题:
- 如果系统中的类比较多,每个类哪不都要写一个子类?
- 由于采用了继承的方式,导致代码之间的耦合度增高了
第三种处理方案:使用代理模式(静态代理)
public class OrderServiceProxy implements OrderService { // 代理类与目标类实现同一接口// 目标对象private OrderService target;public OrderServiceProxy(OrderService target) {this.target = target;}@Overridepublic void generate() {long begin = System.currentTimeMillis();target.generate();System.out.println("耗时:" + (System.currentTimeMillis() - begin) + "ms");}@Overridepublic void detail() {long begin = System.currentTimeMillis();target.detail();System.out.println("耗时:" + (System.currentTimeMillis() - begin) + "ms");}@Overridepublic void modify() {long begin = System.currentTimeMillis();target.modify();System.out.println("耗时:" + (System.currentTimeMillis() - begin) + "ms");}
}
public class Client {public static void main(String[] args) {// 创建目标对象OrderServiceImpl target = new OrderServiceImpl();// 创建代理对象OrderServiceProxy proxy = new OrderServiceProxy(target);// 调用代理对象的方法proxy.generate();proxy.modify();proxy.detail();}
}
上面的三种方案中第三种方案相对可取,它就是静态代理的方式。其中OrderService接口是代理类和目标类的共同接口, OrderServiceImpl是目标类,OrderServiceProxy是代理类。
现在有一个问题:如果业务接口很多,一个接口对应一个代理类,显然也是不合理的,这有可能导致类爆炸。如何解决这个问题呢?这个时候可以考虑动态代理。因为动态代理当中可以在内存中动态生为我们生成代理类的字节码。代理类不需要我们写了。类爆炸的问题就自然解决了。需要复用的代码也只需要写一次了,代码也能得到复用了。
动态代理
程序运行阶段,在内存中动态生成代理类,称之为动态代理。目的是为了减少代理类的数量。解决代码复用的问题。
在内存中动态生成类的字节码常见方式有如下三个:
- JDK动态代理技术:只能代理接口
- CG
相关文章:
06、Spring AOP
在我们接下来聊Spring AOP之前我们先了解一下设计模式中的代理模式。 一、代理模式 代理模式是23种设计模式中的一种,它属于结构型设计模式。 对于代理模式的理解: 程序中对象A与对象B无法直接交互,如:有人要找某个公司的老总得先打前台登记传达程序中某个功能需要在原基…...
c语言学习26字符串的应用
字符串在stm32串口中的应用 串口控制流水灯 pc通过串口发送字符串命令控制流水灯 open 流水灯打开 close 流水灯关闭 speed 1~9速度控制 if(strcmp((char *)usart1_rec_buff,"open")0) { led_flag 1; } else if(strcmp((char *)usart1_rec_buff,"close&qu…...
法语旅游常用口语-柯桥学外语到蓝天广场泓畅学校
以下是一些实用的法语旅游常用口语,帮助你在法国旅行时能够进行基本的交流: 问候与道别 Bonjour: 用于日常问候。Au revoir: 用于告别。 请求帮助 S’il vous plat: 用于请求帮助,例如在需要寻找某个地点或服务时。 询问信息 Excusez-moi: 用…...
Kafka 生产者优化与数据处理经验
Kafka:分布式消息系统的核心原理与安装部署-CSDN博客 自定义 Kafka 脚本 kf-use.sh 的解析与功能与应用示例-CSDN博客 Kafka 生产者全面解析:从基础原理到高级实践-CSDN博客 Kafka 生产者优化与数据处理经验-CSDN博客 Kafka 工作流程解析:…...
MySQL 主从复制之多线程复制
一、MySQL 多线程复制的背景 MySQL 的主从复制延迟一直是受开发者最为关注的问题之一,MySQL 从 5.6 版本开始追加了并行复制功能,目的就是为了改善复制延迟问题,并行复制称为enhanced multi-threaded slave(简称MTS)。…...
Linux2.6内核进程调度队列
文章目录 前言运行队列 runqueue优先级活动队列过期队列活跃队列 VS 过期队列active指针和expired指针O(1)调度算法 前言 在前面学习并认识了进程之后,我们会发出一个疑问:Linux内核是如何调度进程的呢? 接下来我们就以Linux2.6内核为例深入探…...
Infineon(英飞凌) TLE985xQX 芯片电机工作电流、电压AD采样
其他系列文章导航 Java基础合集数据结构与算法合集 设计模式合集 多线程合集 分布式合集 单片机芯片合集 文章目录 其他系列文章导航 文章目录 前言 一、选取合适的端口 1.通过 OP1、OP2 电流采集运放输入端口进行H桥驱动的电流采集。 2.通过 O_D_VBAT_AD_EN、I_A_VBAT_A…...
Sparrow系列拓展篇:对信号量应用问题的深入讨论
前言 笔者之前已经介绍过了Sparrow信号量的源码,但是对于信号量的使用,并没有讲得非常详细,仅仅讲了同步与互斥的概念。 本章让笔者介绍如何使用Sparrow的信号量,深入探讨一下信号量在同步、计数与互斥中的应用。 使用信号量解…...
图文详解Docker下配置、测试Redis
文章目录 前言实测环境:实验思路: 正文1.准备工作2. 配置、运行 Redis 容器3. 配置测试 总结 前言 配置、测试redis数据库服务器,首先确保正确安装docker,并且已启动运行,具体安装docker方法见笔者前面的博文《OpenEu…...
Python编程艺术:优雅与实用的完美平衡(推导式)
在Python这门优雅的编程语言中,处处体现着"简洁即是美"的设计哲学。今天我们深入探讨Python中那些让代码更优雅、更高效的编程技巧,这些技巧不仅能提升代码的可读性,还能让编程过程充满乐趣。 列表推导式的魔力 Python的列表推导…...
Spring Boot框架Starter组件整理
在Spring Boot框架中,starter是一种预定义的依赖集合,旨在简化Maven或Gradle等构建工具中的依赖管理。每个starter都包含了实现特定功能所需的库和组件,以及相应的配置文件。开发者只需在项目中引入相应的starter依赖,即可快速搭建…...
C/C++基础知识复习(27)
1) 移动语义和拷贝语义的区别 拷贝语义和移动语义是C中对象所有权管理的两种机制,主要在对象初始化、赋值或传参时体现。 拷贝语义 (Copy Semantics) 行为:通过深拷贝或浅拷贝,创建一个新对象,并将原对象的值或资源复制到新对象…...
IEC61850实现方案和测试-2
IEC61850实现方案和测试-1作为介绍实现方案和测试的第二篇文章,后续会继续更新,欢迎关注。 第一篇是:IEC61850实现方案和测试-1-CSDN博客 UCA详细测试用例下载: 链接: https://pan.baidu.com/s/1TTMlYRfzKITgrkWwwtcrDg 提取码:…...
flume-将日志采集到hdfs
看到hdfs大家应该做什么? 是的你应该去把集群打开, cd /export/servers/hadoop/sbin 启动集群 ./start-all.sh 在虚拟机hadoop02和hadoop03上的conf目录下配置相同的日志采集方案,‘ cd /export/servers/flume/conf 切换完成之后&#…...
一文学习开源框架LeakCanary
LeakCanary 简介 LeakCanary 是一个由 Square 开发的开源工具,主要用于检测和诊断 Android 应用中的内存泄漏问题。它通过自动化的方式帮助开发者捕捉和分析可能导致内存泄漏的对象,简化了内存问题的排查过程。 LeakCanary 的功能 自动检测内存泄漏&a…...
jetson orin系列开发版安装cuda的gpu版本的opencv
opencv安装包下载地址: https://github.com/opencv/opencv/扩展库下载地址: https://github.com/opencv/opencv_contrib1. 删除jetpack包中的opencv版本 原先的opencv库安装在目录/usr/lib/aarch64-linux-gnu/下(一般其他的第三方库也都安…...
数据结构-8.Java. 七大排序算法(中篇)
本篇博客给大家带来的是排序的知识点, 由于时间有限, 分两天来写, 中篇主要实现后三种排序算法: 冒泡排序,快速排序,下一篇讲 归并排序. 文章专栏: Java-数据结构 若有问题 评论区见 欢迎大家点赞 评论 收藏 分享 如果你不知道分享给谁,那就分享给薯条. 你们的支持是我不断创作…...
数据结构C语言描述4(图文结合)--栈的实现,中序转后序表达式的实现
前言 这个专栏将会用纯C实现常用的数据结构和简单的算法;有C基础即可跟着学习,代码均可运行;准备考研的也可跟着写,个人感觉,如果时间充裕,手写一遍比看书、刷题管用很多,这也是本人采用纯C语言…...
python基本数据类型 -- 元组tuple
在 Python 中,元组(Tuple)是一种轻量级的、不可变的数据结构。与列表类似,元组用于存储有序的数据集合,但它一旦创建,其内容就无法更改。这种特性让元组在某些场景下更加安全和高效。本文将从定义、操作、应…...
tcpdump交叉编译
TCPDUMP在Libpcap上开发。 首先需要编译libcap。 网上那么多教程,下载地址都只给了一个英文的官网首页, 你尽可以试试,从里面找到下载地址都要费半天时间。 \color{red}网上那么多教程,下载地址都只给了一个英文的官网首页&#…...
PyTorch 2.9镜像使用指南:Jupyter与SSH两种方式详细解析
PyTorch 2.9镜像使用指南:Jupyter与SSH两种方式详细解析 1. 镜像概述 PyTorch 2.9镜像是一个开箱即用的深度学习开发环境,预装了PyTorch 2.9框架和CUDA工具包。这个镜像特别适合需要快速搭建GPU加速开发环境的用户,无论是进行模型训练、推理…...
春联生成模型-中文-base环境隔离部署:Anaconda虚拟环境配置指南
春联生成模型-中文-base环境隔离部署:Anaconda虚拟环境配置指南 每次想尝试一个新的AI模型,最头疼的可能不是模型本身,而是环境配置。装了这个包,发现和之前项目的包版本冲突;好不容易跑起来,结果报了一堆…...
Qwen3.5-2B集成IDEA开发环境:Java多模态应用快速开发指南
Qwen3.5-2B集成IDEA开发环境:Java多模态应用快速开发指南 1. 为什么选择Qwen3.5-2B进行Java开发 如果你是一位Java开发者,想要快速为应用添加AI能力,Qwen3.5-2B是个不错的选择。这个轻量级多模态模型不仅支持文本理解,还能处理图…...
PyTorch-CUDA-v2.7镜像应用场景:快速启动AI模型训练与推理
PyTorch-CUDA-v2.7镜像应用场景:快速启动AI模型训练与推理 1. 镜像概述与核心优势 PyTorch-CUDA-v2.7镜像是一个开箱即用的深度学习环境解决方案,专为需要快速启动AI模型训练与推理的开发者设计。这个预配置的Docker镜像集成了PyTorch 2.7框架和完整的…...
告别编译失败:Qt 6.6.0交叉编译到ARM平台最常见的5个错误及解决方法(基于gcc-linaro-14.0.0)
告别编译失败:Qt 6.6.0交叉编译到ARM平台最常见的5个错误及解决方法(基于gcc-linaro-14.0.0) 最近在将Qt 6.6.0交叉编译到i.MX6ULL开发板时,遇到了不少坑。作为一个经历过多次编译失败的老手,我整理了几个最容易导致编…...
Legcord:革命性Discord轻量级客户端,10大特性全面解析
Legcord:革命性Discord轻量级客户端,10大特性全面解析 【免费下载链接】ArmCord Legcord is a custom client designed to enhance your Discord experience while keeping everything lightweight. 项目地址: https://gitcode.com/gh_mirrors/ar/ArmC…...
线程池项目(1)
推荐去看施磊老师的课程 需要课程或者代码的可以评论,看到会回复的,免费的并发与并行定义并发:多个线程在单核上轮流占用 CPU 时间片,物理上串行执行,但由于时间片较短,看起来像是同时执行。并行:多个线程在多核或多 C…...
OpenClaw+Kimi-VL-A3B-Thinking自动化办公:飞书机器人实现图文周报生成
OpenClawKimi-VL-A3B-Thinking自动化办公:飞书机器人实现图文周报生成 1. 为什么选择这个方案 每周五下午,我都会面临同样的困扰:需要从十几个工作群聊、邮件和本地文件中整理出本周工作内容,手动截图关键数据,再拼凑…...
深入解析AdaptiveAvgPool2d:从原理到实践
1. 池化技术基础与核心价值 当你第一次听说"池化"这个词时,可能会联想到游泳池或者资源池。但在深度学习领域,池化(Pooling)是一种非常重要的降维操作,它就像一位精明的数据压缩师,能够在不丢失关键信息的前提下&#x…...
SpringBoot + Ollama + Qdrant + DeepSeek:从零构建企业级本地知识库问答系统
1. 为什么选择这套技术栈? 在企业内部搭建知识库问答系统时,技术选型需要平衡性能、成本和易用性。这套组合拳的巧妙之处在于:SpringBoot提供企业级开发框架,Ollama让大模型本地化运行成为可能,Qdrant解决向量检索的效…...
