当前位置: 首页 > news >正文

【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超详细讲解

精品专题&#xff1a; 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等远程操作

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

jmeter基础01-1_环境准备-windows系统安装jdk

课程大纲 一、步骤解说 step1. jdk官网下载 Java Downloads | Oracle step2. 安装/解压&#xff08;二选一&#xff09; 1. 安装包格式&#xff08;后缀.exe/.msi/.dmg&#xff09;&#xff1a;双击跟随界面向导安装&#xff0c;可以指定安装位置等。 2. 压缩包格式(后缀.z…...

第六天: C语言核心概念与实战技巧全解析

1 主函数&#xff08;main&#xff09; 大家好&#xff0c;今天我们来深入探讨一下C语言中非常特殊的一个函数——main函数。虽然大家对它并不陌生&#xff0c;但是它的重要性和特殊性值得我们再次回顾。 main函数的定义 main函数是我们整个C源程序的入口点。计算机在运行程…...

初始JavaEE篇——多线程(5):生产者-消费者模型、阻塞队列

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

2024年下教师资格证面试报名详细流程❗

⏰ 重要时间节点&#xff1a; &#xff08;一&#xff09;下半年笔试成绩查询&#xff1a;11月8日10:00 &#xff08;二&#xff09;注册报名&#xff1a;11月8日10:00-11日18:00 &#xff08;三&#xff09;网上审核&#xff1a;11月8日10:00-11日18:00 &#xff08;四&#x…...

软考:常用协议和端口号

常用协议及其对应的端口号如下&#xff1a; TCP/IP协议&#xff1a; TCP&#xff08;传输控制协议&#xff09;&#xff1a;端口号为6UDP&#xff08;用户数据报协议&#xff09;&#xff1a;端口号为17 网络应用协议&#xff1a; HTTP&#xff08;超文本传输协议&#xff09;…...

Linux更改符号链接

目录 1. 删除旧链接 2. 创建新的符号链接 例如我的电脑上有两个版本的cuda&#xff0c;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流程控制语句与项目实践

作者介绍 周瑞康&#xff0c;男&#xff0c;银川科技学院&#xff0c;计算机人工智能学院&#xff0c;2022级计算机科学与技术8班本科生&#xff0c;单片机原理及应用课程第八组。 指导老师&#xff1a;王兴泽 电子邮箱2082545622qq.com 前言&#xff1a; 本篇文章是参考《…...

大数据日志处理框架ELK方案

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

VQGAN(2021-06:Taming Transformers for High-Resolution Image Synthesis)

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

docker中使用ros2humble的rviz2不显示问题

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

【AIGC】2024-arXiv-Lumiere:视频生成的时空扩散模型

2024-arXiv-Lumiere: A Space-Time Diffusion Model for Video Generation Lumiere&#xff1a;视频生成的时空扩散模型摘要1. 引言2. 相关工作3. Lumiere3.1 时空 U-Net (STUnet)3.2 空间超分辨率的多重扩散 4. 应用4.1 风格化生成4.2 条件生成 5. 评估和比较5.1 定性评估5.2 …...

正则表达式:文本处理的强大工具

正则表达式是一种强大的文本处理工具&#xff0c;它允许我们通过定义一系列的规则来匹配、搜索、替换或分割文本。在编程、文本编辑、数据分析和许多其他领域中&#xff0c;正则表达式都扮演着重要的角色。本文将介绍正则表达式的基本概念、语法和一些实际应用。 正则表达式的…...

Doris单机安装

1、安装包下载 官网地址&#xff1a;https://doris.apache.org/zh-CN/docs/gettingStarted/quick-start/ 下载地址&#xff1a;https://apache-doris-releases.oss-accelerate.aliyuncs.com/apache-doris-3.0.2-bin-x64.tar.gz 2、操作系统环境准备 #环境准备 cat /proc/cp…...

ubuntu内核更新导致显卡驱动掉的解决办法

方法1&#xff0c;DKMS指定内核版本 用第一个就行 1&#xff0c;借鉴别人博客解决方法 2&#xff0c;借鉴别人博客解决方法 方法2&#xff0c;删除多于内核的方法 系统版本&#xff1a;ubuntu20.24 这个方法是下下策&#xff0c;如果重装驱动还是不行&#xff0c;就删内核在…...

【Java数据结构】树】

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

Java面试题——微服务篇

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

Python 中 print 函数输出多行并且选择对齐方式

代码 # 定义各类别的标签和对应数量 categories ["class0", "class1", "class2", "class3", "class4", "class5"] counts [4953, 547, 5121, 8989, 6077, 4002]# 设置统一的列宽 column_width 10# 生成对齐后…...

利用最小二乘法找圆心和半径

#include <iostream> #include <vector> #include <cmath> #include <Eigen/Dense> // 需安装Eigen库用于矩阵运算 // 定义点结构 struct Point { double x, y; Point(double x_, double y_) : x(x_), y(y_) {} }; // 最小二乘法求圆心和半径 …...

LBE-LEX系列工业语音播放器|预警播报器|喇叭蜂鸣器的上位机配置操作说明

LBE-LEX系列工业语音播放器|预警播报器|喇叭蜂鸣器专为工业环境精心打造&#xff0c;完美适配AGV和无人叉车。同时&#xff0c;集成以太网与语音合成技术&#xff0c;为各类高级系统&#xff08;如MES、调度系统、库位管理、立库等&#xff09;提供高效便捷的语音交互体验。 L…...

[2025CVPR]DeepVideo-R1:基于难度感知回归GRPO的视频强化微调框架详解

突破视频大语言模型推理瓶颈,在多个视频基准上实现SOTA性能 一、核心问题与创新亮点 1.1 GRPO在视频任务中的两大挑战 ​安全措施依赖问题​ GRPO使用min和clip函数限制策略更新幅度,导致: 梯度抑制:当新旧策略差异过大时梯度消失收敛困难:策略无法充分优化# 传统GRPO的梯…...

智慧医疗能源事业线深度画像分析(上)

引言 医疗行业作为现代社会的关键基础设施,其能源消耗与环境影响正日益受到关注。随着全球"双碳"目标的推进和可持续发展理念的深入,智慧医疗能源事业线应运而生,致力于通过创新技术与管理方案,重构医疗领域的能源使用模式。这一事业线融合了能源管理、可持续发…...

Opencv中的addweighted函数

一.addweighted函数作用 addweighted&#xff08;&#xff09;是OpenCV库中用于图像处理的函数&#xff0c;主要功能是将两个输入图像&#xff08;尺寸和类型相同&#xff09;按照指定的权重进行加权叠加&#xff08;图像融合&#xff09;&#xff0c;并添加一个标量值&#x…...

C++ 基础特性深度解析

目录 引言 一、命名空间&#xff08;namespace&#xff09; C 中的命名空间​ 与 C 语言的对比​ 二、缺省参数​ C 中的缺省参数​ 与 C 语言的对比​ 三、引用&#xff08;reference&#xff09;​ C 中的引用​ 与 C 语言的对比​ 四、inline&#xff08;内联函数…...

MySQL中【正则表达式】用法

MySQL 中正则表达式通过 REGEXP 或 RLIKE 操作符实现&#xff08;两者等价&#xff09;&#xff0c;用于在 WHERE 子句中进行复杂的字符串模式匹配。以下是核心用法和示例&#xff1a; 一、基础语法 SELECT column_name FROM table_name WHERE column_name REGEXP pattern; …...

Linux离线(zip方式)安装docker

目录 基础信息操作系统信息docker信息 安装实例安装步骤示例 遇到的问题问题1&#xff1a;修改默认工作路径启动失败问题2 找不到对应组 基础信息 操作系统信息 OS版本&#xff1a;CentOS 7 64位 内核版本&#xff1a;3.10.0 相关命令&#xff1a; uname -rcat /etc/os-rele…...

[大语言模型]在个人电脑上部署ollama 并进行管理,最后配置AI程序开发助手.

ollama官网: 下载 https://ollama.com/ 安装 查看可以使用的模型 https://ollama.com/search 例如 https://ollama.com/library/deepseek-r1/tags # deepseek-r1:7bollama pull deepseek-r1:7b改token数量为409622 16384 ollama命令说明 ollama serve #&#xff1a…...

Golang——9、反射和文件操作

反射和文件操作 1、反射1.1、reflect.TypeOf()获取任意值的类型对象1.2、reflect.ValueOf()1.3、结构体反射 2、文件操作2.1、os.Open()打开文件2.2、方式一&#xff1a;使用Read()读取文件2.3、方式二&#xff1a;bufio读取文件2.4、方式三&#xff1a;os.ReadFile读取2.5、写…...