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}网上那么多教程,下载地址都只给了一个英文的官网首页&#…...
AI-调查研究-01-正念冥想有用吗?对健康的影响及科学指南
点一下关注吧!!!非常感谢!!持续更新!!! 🚀 AI篇持续更新中!(长期更新) 目前2025年06月05日更新到: AI炼丹日志-28 - Aud…...
iPhone密码忘记了办?iPhoneUnlocker,iPhone解锁工具Aiseesoft iPhone Unlocker 高级注册版分享
平时用 iPhone 的时候,难免会碰到解锁的麻烦事。比如密码忘了、人脸识别 / 指纹识别突然不灵,或者买了二手 iPhone 却被原来的 iCloud 账号锁住,这时候就需要靠谱的解锁工具来帮忙了。Aiseesoft iPhone Unlocker 就是专门解决这些问题的软件&…...
Frozen-Flask :将 Flask 应用“冻结”为静态文件
Frozen-Flask 是一个用于将 Flask 应用“冻结”为静态文件的 Python 扩展。它的核心用途是:将一个 Flask Web 应用生成成纯静态 HTML 文件,从而可以部署到静态网站托管服务上,如 GitHub Pages、Netlify 或任何支持静态文件的网站服务器。 &am…...
高等数学(下)题型笔记(八)空间解析几何与向量代数
目录 0 前言 1 向量的点乘 1.1 基本公式 1.2 例题 2 向量的叉乘 2.1 基础知识 2.2 例题 3 空间平面方程 3.1 基础知识 3.2 例题 4 空间直线方程 4.1 基础知识 4.2 例题 5 旋转曲面及其方程 5.1 基础知识 5.2 例题 6 空间曲面的法线与切平面 6.1 基础知识 6.2…...
#Uniapp篇:chrome调试unapp适配
chrome调试设备----使用Android模拟机开发调试移动端页面 Chrome://inspect/#devices MuMu模拟器Edge浏览器:Android原生APP嵌入的H5页面元素定位 chrome://inspect/#devices uniapp单位适配 根路径下 postcss.config.js 需要装这些插件 “postcss”: “^8.5.…...
day36-多路IO复用
一、基本概念 (服务器多客户端模型) 定义:单线程或单进程同时监测若干个文件描述符是否可以执行IO操作的能力 作用:应用程序通常需要处理来自多条事件流中的事件,比如我现在用的电脑,需要同时处理键盘鼠标…...
探索Selenium:自动化测试的神奇钥匙
目录 一、Selenium 是什么1.1 定义与概念1.2 发展历程1.3 功能概述 二、Selenium 工作原理剖析2.1 架构组成2.2 工作流程2.3 通信机制 三、Selenium 的优势3.1 跨浏览器与平台支持3.2 丰富的语言支持3.3 强大的社区支持 四、Selenium 的应用场景4.1 Web 应用自动化测试4.2 数据…...
Qt的学习(一)
1.什么是Qt Qt特指用来进行桌面应用开发(电脑上写的程序)涉及到的一套技术Qt无法开发网页前端,也不能开发移动应用。 客户端开发的重要任务:编写和用户交互的界面。一般来说和用户交互的界面,有两种典型风格&…...
数据库正常,但后端收不到数据原因及解决
从代码和日志来看,后端SQL查询确实返回了数据,但最终user对象却为null。这表明查询结果没有正确映射到User对象上。 在前后端分离,并且ai辅助开发的时候,很容易出现前后端变量名不一致情况,还不报错,只是单…...
Java数组Arrays操作全攻略
Arrays类的概述 Java中的Arrays类位于java.util包中,提供了一系列静态方法用于操作数组(如排序、搜索、填充、比较等)。这些方法适用于基本类型数组和对象数组。 常用成员方法及代码示例 排序(sort) 对数组进行升序…...
