【SSM详细教程】-14-SpringAop超详细讲解
精品专题:
01.《C语言从不挂科到高绩点》课程详细笔记
https://blog.csdn.net/yueyehuguang/category_12753294.html?spm=1001.2014.3001.5482
02. 《SpringBoot详细教程》课程详细笔记
https://blog.csdn.net/yueyehuguang/category_12789841.html?spm=1001.2014.3001.5482
03.《SpringBoot电脑商城项目》课程详细笔记
https://blog.csdn.net/yueyehuguang/category_12752883.html?spm=1001.2014.3001.5482
04.《VUE3.0 核心教程》课程详细笔记
https://blog.csdn.net/yueyehuguang/category_12769996.html?spm=1001.2014.3001.5482
05. 《SSM详细教程》课程详细笔记
https://blog.csdn.net/yueyehuguang/category_12806942.html?spm=1001.2014.3001.5482
================================
|| 持续分享系列教程,关注一下不迷路 ||
|| 视频教程:墨轩大楼 ||
================================
📚 AOP 概念及优点
AOP为Aspect Oriented Programming的缩写,被称为面向切面编程。
AOP 主要用于处理共通逻辑,例如:记录日志、性能统计、安全控制、事务处理、异常处理等等。AOP可以将这些共通的逻辑从普通业务逻辑代码中分离出来,这样在日后修改这些逻辑的时候,就不会影响普通业务逻辑的代码。
利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高开发的效率。
AOP 、OOP在名字上虽然非常类似,但却是面向不同领域的两种设计思想。OOP面向对象编程,针对业务处理过程的实体及其属性和行为进行抽象,以获得更加清晰高效的逻辑单元划分。AOP则是针对业务处理过程中的切面进行提取,它所面对的是处理过程中的某个步骤或阶段,以获得逻辑过程中各部分之间低耦合性的隔离效果。
AOP 需要以 OOP为前提和基础。
🌾 什么是方面
面向切面编程,我们首先要知道的一个概念就是方面,也就是把什么东西给隔离出来。方面是指封装处理共通业务的组件,该组件被作用到其他目标组件方法上。
🌾 什么是目标
目标是指被一个或多个方面所作用的对象。
🌾 什么是切入点
切入点是用于指定哪些组件和方法使用方面功能,在Spring中利用一个表达式指定切入目标。
Spring提供了以下常用的切入点表达式:
- 方法限定表达式
execution(修饰符?返回类型 方法名(参数) throws 异常类型?)
- 类型限定表达式
within(包名.类型)
- Bean 名称限定表达式
bean("Bean的id或name属性值")
🌾 什么是通知
通知是用于指定方面组件和目标组件作用的时机,例如方面功能在目标方法之前或之后执行等时机。
Spring框架提供以下几种类型的通知:
- 前置通知:先执行方面功能在执行目标功能
- 后置通知:先执行目标功能再执行方面功能(目标无异常才执行方面)
- 最终通知:先执行目标功能再执行方面功能(目标有无异常都执行方面)
- 异常通知:先执行目标,抛出后执行方面
- 环绕通知:先执行方面前置部分,然后执行目标,最后再执行方面后置部分。
Spring框架提供5种通知,可以按照下面的try-catch-finally结构理解。
try{// 前置通知--执行方面// 环绕通知--前置部分// 执行目标组件方法// 环绕通知--后置部分// 后置通知--执行方面
}catch{// 异常通知--执行方面
}finally{// 最终通知--执行方面
}
🌾 AOP 实现原理
Spring AOP 实现主要是基于动态代理技术。当Spring采用AOP配置后,Spring容器返回的目标对象,实质上是Spring利用动态代理技术生成的一个代理类型。代理类重写了原目标组件方法的功能,在代理类种调用方面对象功能和目标对象功能。
Spring框架采用了两种动态代理实现:
- 利用cglib工具包:目标没有接口时采用此方法,代理类是利用继承方法生成一个目标子类。
- 利用JDK Proxy API:目标有接口时采用此方法,代理类是采用实现目标接口方法生成一个类。
📚 AOP 开发案例
🌾 AOP 前置通知案例
👉 需求:使用Spring AOP 前置通知,在访问Controller中每个方法前,记录用户的操作日志。
👉 步骤:
- 创建方面组件
- 声明方面组件
- 将方面组件作用到目标组件上
🍒 导入依赖
我们基于前面SpringMVC的基础上去添加AOP功能,所以在前面SpringMVC的环境基础上我们需要追加AOP的依赖。
<dependency><groupId>org.springframework</groupId><artifactId>spring-aop</artifactId><version>5.3.8</version>
</dependency>
<dependency><groupId>org.aspectj</groupId><artifactId>aspectjweaver</artifactId><version>1.8.10</version>
</dependency>
<dependency><groupId>org.aspectj</groupId><artifactId>aspectjrt</artifactId><version>1.8.10</version>
</dependency>
🍒 创建Controller
创建一个AOPTestContrller,模拟查询用户数据的Controller,代码如下:
@Controller
public class AOPTestController {@RequestMapping("/find")@ResponseBodypublic String findUser(){// 模拟查询用户数据System.out.println("--》 查询用户数据");return "查询了用户数据";}
}
🍒 创建方面组件
创建方面组件OperateLogger,并在该类中创建记录用户操作日志的方法,代码如下:
package com.moxuan.mvc_study.config;import org.aspectj.lang.ProceedingJoinPoint;import java.text.SimpleDateFormat;
import java.util.Date;/*** 用于记录日志的方面组件,演示Spring AOP 的各种通知类型*/
public class OperateLogger {/*** 前置通知、后置通知、最终通知使用的方法*/public void logUser(JoinPoint p){// 目标组件的类名String className = p.getTarget().getClass().getName();// 调用的方法名String method = p.getSignature().getName();// 当前系统时间String time = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date());// 拼日志信息String msg = "--> 用户在"+time+",执行了"+className+"."+method+"()";// 记录日志System.out.println(msg);}
}
🍒 声明方面组件
在springmvc.xml中,声明该方面组件,关键代码如下:
<bean id="operateLogger" class="com.moxuan.mvc_study.config.OperateLogger"></bean>
🍒 将方面组件作用到目标组件上
在springmvc.xml中,将声明的方面组件作用到controller包下面所有类的所有方法上,关键代码如下:
<aop:config><aop:aspect ref="operateLogger"><!--配置方面组件,作用到的目标方法,pointcut 方面组件的切入点--><aop:before method="logUser"pointcut="within(com.moxuan.mvc_study.controller..*)" /></aop:aspect>
</aop:config>
🍒 测试效果
发送请求:http://localhost:8080/find
可以看到,当配置<aop:before> 前置通知后,方面组件会在执行目标组件的方法时自动触发执行。
🌾 AOP 环绕通知案例
🍒 创建方面组件
依赖和控制器我们延用前置通知案例中的,我们来修改一下方面组件:
public Object logUserRound(ProceedingJoinPoint p) throws Throwable{// 目标组件的类名String className = p.getTarget().getClass().getName();// 调用的方法名String method = p.getSignature().getName();// 当前系统时间String time = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date());// 拼日志信息String msg = "--> 用户在"+time+",执行了"+className+"."+method+"()";// 记录日志System.out.println(msg);// 执行目标组件的方法Object obj = p.proceed();//在调用目标组件业务方法后也可以做一些业务处理System.out.println("---> 已经执行完毕了组件业务了....");return obj;}
🍒 配置环绕通知
组件声明我们前面已经做过了,这里我们直接配置一下环绕通知:
<aop:config><aop:aspect ref="operateLogger"><!--配置方面组件,作用到的目标方法,pointcut 方面组件的切入点--><aop:around method="logUserRound"pointcut="within(com.moxuan.mvc_study.controller..*)"/></aop:aspect></aop:config>
🍒 测试效果
请求地址:http://localhost:8080/find
可以看到,方面组件中的前置部分会在方法执行前执行,方法执行完毕之后执行后置部分。
🌾 AOP 异常通知案例
需求:使用AOP异常通知,在每个Controller的方法发生异常时,记录异常日志。
🍒 编写方面组件
/*** 异常通知使用方法* @param e*/
public void logException(Exception e){StackTraceElement[] elements = e.getStackTrace();// 将异常信息记录System.out.println("--》"+elements[0].toString());
}
🍒 配置异常通知
将异常通知方面组件作用到目标组件上
<aop:config><aop:aspect ref="operateLogger"><aop:after-throwing method="logException" throwing="e"pointcut="within(com.moxuan.mvc_study.controller..*)"/></aop:aspect></aop:config>
🍒 编写目标组件
@RequestMapping("/find")
@ResponseBody
public String findUser(){// 模拟查询用户数据System.out.println("目标组件:--》 查询用户数据");// 制造一个异常,便于测试异常通知Integer.valueOf("abc");return "查询了用户数据";
}
🍒 测试效果
发送请求:http://localhost:8080/find
🌾 AOP 注解使用案例
👉需求: 使用Spring AOP 注解替代XML配置,重构上面三个案例
👉方案:
- @Aspect : 用于声明方面组件
- @Before:用于声明前置通知
- @AfterReturning:用于声明后置通知
- @After:用于声明最终通知
- @Around:用于声明环绕通知
- @AfterThrowing:用于声明异常通知
🍒 开启AOP注解扫描
在springmvc.xml中,去掉方面组件声明以及作用的xml配置,并开启AOP注解扫描,关键代码如下:
<!-- 开启AOP注解扫描-->
<aop:aspectj-autoproxy proxy-target-class="true"></aop:aspectj-autoproxy>
🍒 使用注解声明方面组件
在OperateLogger中使用@Aspect注解声明方面组件,并分别用@Before、@Around、@AfterThrowing注解声明三个方法,将方面组件作用到目标组件上,代码如下:
package com.moxuan.mvc_study.config;import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.stereotype.Component;import java.text.SimpleDateFormat;
import java.util.Date;/*** 用于记录日志的方面组件,演示Spring AOP 的各种通知类型*/
@Component
@Aspect
public class OperateLogger {/*** 前置通知、后置通知、最终通知使用的方法*/@Before("within(com.moxuan.mvc_study.controller..*)")public void logUser(JoinPoint p){System.out.println("^^^^^进入到了前置通知^^^^^^");// 目标组件的类名String className = p.getTarget().getClass().getName();// 调用的方法名String method = p.getSignature().getName();// 当前系统时间String time = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date());// 拼日志信息String msg = "--> 用户在"+time+",执行了"+className+"."+method+"()";// 记录日志System.out.println(msg);System.out.println("^^^^^前置通知结束^^^^^^");}@Around("within(com.moxuan.mvc_study.controller..*)")public Object logUserRound(ProceedingJoinPoint p) throws Throwable{System.out.println("^^^^^进入环绕通知^^^^^^");// 目标组件的类名String className = p.getTarget().getClass().getName();// 调用的方法名String method = p.getSignature().getName();// 当前系统时间String time = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date());// 拼日志信息String msg = "--> 用户在"+time+",执行了"+className+"."+method+"()";// 记录日志System.out.println(msg);// 执行目标组件的方法Object obj = p.proceed();//在调用目标组件业务方法后也可以做一些业务处理System.out.println("---> 已经执行完毕了组件业务了....");System.out.println("^^^^^环绕通知结束^^^^^^");return obj;}/*** 异常通知使用方法* @param e*/@AfterThrowing(pointcut = "within(com.moxuan.mvc_study.controller..*)",throwing ="e")public void logException(Exception e){System.out.println("^^^^^进入异常通知^^^^^^");StackTraceElement[] elements = e.getStackTrace();// 将异常信息记录System.out.println("--》"+elements[0].toString());System.out.println("^^^^^异常通知结束^^^^^^");}
}
🍒 测试效果
请求路径:http://localhost:8080/find
从结果可以看到,当发生异常之后,环绕通知后置部分将不会执行。
相关文章:

【SSM详细教程】-14-SpringAop超详细讲解
精品专题: 01.《C语言从不挂科到高绩点》课程详细笔记 https://blog.csdn.net/yueyehuguang/category_12753294.html?spm1001.2014.3001.5482 02. 《SpringBoot详细教程》课程详细笔记 https://blog.csdn.net/yueyehuguang/category_12789841.html?spm1001.20…...

虚拟机桥接模式连不上,无法进行SSH等远程操作
说明:以下情况在window10上遇到,解决后顺便做了个笔记,以防后续再次用到,也给同道中人提供一个解决方案 一、首先按照以下步骤进行检查 1、是否连接了对应的wifi 2、是否设置了桥接模式 3、上述1、2确认无误的情况下请查看右上…...

jmeter基础01-1_环境准备-windows系统安装jdk
课程大纲 一、步骤解说 step1. jdk官网下载 Java Downloads | Oracle step2. 安装/解压(二选一) 1. 安装包格式(后缀.exe/.msi/.dmg):双击跟随界面向导安装,可以指定安装位置等。 2. 压缩包格式(后缀.z…...
第六天: C语言核心概念与实战技巧全解析
1 主函数(main) 大家好,今天我们来深入探讨一下C语言中非常特殊的一个函数——main函数。虽然大家对它并不陌生,但是它的重要性和特殊性值得我们再次回顾。 main函数的定义 main函数是我们整个C源程序的入口点。计算机在运行程…...

初始JavaEE篇——多线程(5):生产者-消费者模型、阻塞队列
找往期文章包括但不限于本期文章中不懂的知识点: 个人主页:我要学编程程(ಥ_ಥ)-CSDN博客 所属专栏:JavaEE 文章目录 阻塞队列生产者—消费者模型生产者—消费者模型的优势:生产者—消费者模型的劣势: Java标准库中的阻…...

2024年下教师资格证面试报名详细流程❗
⏰ 重要时间节点: (一)下半年笔试成绩查询:11月8日10:00 (二)注册报名:11月8日10:00-11日18:00 (三)网上审核:11月8日10:00-11日18:00 (四&#x…...
软考:常用协议和端口号
常用协议及其对应的端口号如下: TCP/IP协议: TCP(传输控制协议):端口号为6UDP(用户数据报协议):端口号为17 网络应用协议: HTTP(超文本传输协议)…...

Linux更改符号链接
目录 1. 删除旧链接 2. 创建新的符号链接 例如我的电脑上有两个版本的cuda,11.8和12.4 1. 删除旧链接 rm cuda 2. 创建新的符号链接 ln -s /usr/local/cuda-11.8/ /usr/local/cuda...

int main(int argc,char* argv[])详解
#include <stdio.h> //argc 是指命令行输入参数的个数; //argv[]存储了所有的命令行参数, //arg[0]通常指向程序中的可执行文件的文件名。在有些版本的编译器中还包括程序文件所在的路径。 //如:"d:\Production\Software\VC_2005_Test\Win32控制台应用程序\Vc_T…...

单片机原理及应用笔记:C51流程控制语句与项目实践
作者介绍 周瑞康,男,银川科技学院,计算机人工智能学院,2022级计算机科学与技术8班本科生,单片机原理及应用课程第八组。 指导老师:王兴泽 电子邮箱2082545622qq.com 前言: 本篇文章是参考《…...

大数据日志处理框架ELK方案
介绍应用场景大数据ELK日志框架安装部署 一,介绍 大数据日志处理框架ELK(Elasticsearch、Logstash、Kibana)是一套完整的日志集中处理方案,以下是对其的详细介绍: 一、Elasticsearch(ES) 基本…...

VQGAN(2021-06:Taming Transformers for High-Resolution Image Synthesis)
论文:Taming Transformers for High-Resolution Image Synthesis 1. 背景介绍 2022年中旬,以扩散模型为核心的图像生成模型将AI绘画带入了大众的视野。实际上,在更早的一年之前,就有了一个能根据文字生成高清图片的模型——VQGAN…...

docker中使用ros2humble的rviz2不显示问题
这里写目录标题 docker中使用ros2humble的rviz2不显示问题删除 Docker 镜像和容器删除 Docker 容器Linux服务器下查看系统CPU个数、核心数、(make编译最大的)线程数总结: RVIZ2 不能显示数据集 docker中使用ros2humble的rviz2不显示问题 问题描述: roo…...

【AIGC】2024-arXiv-Lumiere:视频生成的时空扩散模型
2024-arXiv-Lumiere: A Space-Time Diffusion Model for Video Generation Lumiere:视频生成的时空扩散模型摘要1. 引言2. 相关工作3. Lumiere3.1 时空 U-Net (STUnet)3.2 空间超分辨率的多重扩散 4. 应用4.1 风格化生成4.2 条件生成 5. 评估和比较5.1 定性评估5.2 …...
正则表达式:文本处理的强大工具
正则表达式是一种强大的文本处理工具,它允许我们通过定义一系列的规则来匹配、搜索、替换或分割文本。在编程、文本编辑、数据分析和许多其他领域中,正则表达式都扮演着重要的角色。本文将介绍正则表达式的基本概念、语法和一些实际应用。 正则表达式的…...
Doris单机安装
1、安装包下载 官网地址:https://doris.apache.org/zh-CN/docs/gettingStarted/quick-start/ 下载地址:https://apache-doris-releases.oss-accelerate.aliyuncs.com/apache-doris-3.0.2-bin-x64.tar.gz 2、操作系统环境准备 #环境准备 cat /proc/cp…...

ubuntu内核更新导致显卡驱动掉的解决办法
方法1,DKMS指定内核版本 用第一个就行 1,借鉴别人博客解决方法 2,借鉴别人博客解决方法 方法2,删除多于内核的方法 系统版本:ubuntu20.24 这个方法是下下策,如果重装驱动还是不行,就删内核在…...

【Java数据结构】树】
【Java数据结构】树 一、树型结构1.1 概念1.2 特点1.3 树的类型1.4 树的遍历方式1.5 树的表示形式1.5.1 双亲表示法1.5.2 孩子表示法1.5.3 孩子双亲表示法1.5.4 孩子兄弟表示法 二、树型概念(重点) 此篇博客希望对你有所帮助(帮助你了解树&am…...

Java面试题——微服务篇
1.微服务的拆分原则/怎么样才算一个有效拆分 单一职责原则:每个微服务应该具有单一的责任。这意味着每个服务只关注于完成一项功能,并且该功能应该是独立且完整的。最小化通信:尽量减少服务之间的通信,服务间通信越少,…...

Python 中 print 函数输出多行并且选择对齐方式
代码 # 定义各类别的标签和对应数量 categories ["class0", "class1", "class2", "class3", "class4", "class5"] counts [4953, 547, 5121, 8989, 6077, 4002]# 设置统一的列宽 column_width 10# 生成对齐后…...

【Axure高保真原型】引导弹窗
今天和大家中分享引导弹窗的原型模板,载入页面后,会显示引导弹窗,适用于引导用户使用页面,点击完成后,会显示下一个引导弹窗,直至最后一个引导弹窗完成后进入首页。具体效果可以点击下方视频观看或打开下方…...

3.3.1_1 检错编码(奇偶校验码)
从这节课开始,我们会探讨数据链路层的差错控制功能,差错控制功能的主要目标是要发现并且解决一个帧内部的位错误,我们需要使用特殊的编码技术去发现帧内部的位错误,当我们发现位错误之后,通常来说有两种解决方案。第一…...
uniapp中使用aixos 报错
问题: 在uniapp中使用aixos,运行后报如下错误: AxiosError: There is no suitable adapter to dispatch the request since : - adapter xhr is not supported by the environment - adapter http is not available in the build 解决方案&…...

学习STC51单片机32(芯片为STC89C52RCRC)OLED显示屏2
每日一言 今天的每一份坚持,都是在为未来积攒底气。 案例:OLED显示一个A 这边观察到一个点,怎么雪花了就是都是乱七八糟的占满了屏幕。。 解释 : 如果代码里信号切换太快(比如 SDA 刚变,SCL 立刻变&#…...

《信号与系统》第 6 章 信号与系统的时域和频域特性
目录 6.0 引言 6.1 傅里叶变换的模和相位表示 6.2 线性时不变系统频率响应的模和相位表示 6.2.1 线性与非线性相位 6.2.2 群时延 6.2.3 对数模和相位图 6.3 理想频率选择性滤波器的时域特性 6.4 非理想滤波器的时域和频域特性讨论 6.5 一阶与二阶连续时间系统 6.5.1 …...

小智AI+MCP
什么是小智AI和MCP 如果还不清楚的先看往期文章 手搓小智AI聊天机器人 MCP 深度解析:AI 的USB接口 如何使用小智MCP 1.刷支持mcp的小智固件 2.下载官方MCP的示例代码 Github:https://github.com/78/mcp-calculator 安这个步骤执行 其中MCP_ENDPOI…...

UE5 音效系统
一.音效管理 音乐一般都是WAV,创建一个背景音乐类SoudClass,一个音效类SoundClass。所有的音乐都分为这两个类。再创建一个总音乐类,将上述两个作为它的子类。 接着我们创建一个音乐混合类SoundMix,将上述三个类翻入其中,通过它管理每个音乐…...
Java多线程实现之Runnable接口深度解析
Java多线程实现之Runnable接口深度解析 一、Runnable接口概述1.1 接口定义1.2 与Thread类的关系1.3 使用Runnable接口的优势 二、Runnable接口的基本实现方式2.1 传统方式实现Runnable接口2.2 使用匿名内部类实现Runnable接口2.3 使用Lambda表达式实现Runnable接口 三、Runnabl…...

5. TypeScript 类型缩小
在 TypeScript 中,类型缩小(Narrowing)是指根据特定条件将变量的类型细化为更具体的过程。它帮助开发者编写更精确、更准确的代码,确保变量在运行时只以符合其类型的方式进行处理。 一、instanceof 缩小类型 TypeScript 中的 in…...
Nginx 事件驱动理解
在做埋点采集服务的过程中,主要依靠openresty加lua脚本来实现采集。高并发还是主要依靠nginx来实现。而其核心就是事件驱动/多路io复用(epoll机制),不同的linux服务器都有对应的实现方式。 而epoll机制就是,应用启动的…...