当前位置: 首页 > 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# 生成对齐后…...

CVPR 2025 MIMO: 支持视觉指代和像素grounding 的医学视觉语言模型

CVPR 2025 | MIMO&#xff1a;支持视觉指代和像素对齐的医学视觉语言模型 论文信息 标题&#xff1a;MIMO: A medical vision language model with visual referring multimodal input and pixel grounding multimodal output作者&#xff1a;Yanyuan Chen, Dexuan Xu, Yu Hu…...

Leetcode 3576. Transform Array to All Equal Elements

Leetcode 3576. Transform Array to All Equal Elements 1. 解题思路2. 代码实现 题目链接&#xff1a;3576. Transform Array to All Equal Elements 1. 解题思路 这一题思路上就是分别考察一下是否能将其转化为全1或者全-1数组即可。 至于每一种情况是否可以达到&#xf…...

(二)TensorRT-LLM | 模型导出(v0.20.0rc3)

0. 概述 上一节 对安装和使用有个基本介绍。根据这个 issue 的描述&#xff0c;后续 TensorRT-LLM 团队可能更专注于更新和维护 pytorch backend。但 tensorrt backend 作为先前一直开发的工作&#xff0c;其中包含了大量可以学习的地方。本文主要看看它导出模型的部分&#x…...

Cilium动手实验室: 精通之旅---20.Isovalent Enterprise for Cilium: Zero Trust Visibility

Cilium动手实验室: 精通之旅---20.Isovalent Enterprise for Cilium: Zero Trust Visibility 1. 实验室环境1.1 实验室环境1.2 小测试 2. The Endor System2.1 部署应用2.2 检查现有策略 3. Cilium 策略实体3.1 创建 allow-all 网络策略3.2 在 Hubble CLI 中验证网络策略源3.3 …...

Frozen-Flask :将 Flask 应用“冻结”为静态文件

Frozen-Flask 是一个用于将 Flask 应用“冻结”为静态文件的 Python 扩展。它的核心用途是&#xff1a;将一个 Flask Web 应用生成成纯静态 HTML 文件&#xff0c;从而可以部署到静态网站托管服务上&#xff0c;如 GitHub Pages、Netlify 或任何支持静态文件的网站服务器。 &am…...

【决胜公务员考试】求职OMG——见面课测验1

2025最新版&#xff01;&#xff01;&#xff01;6.8截至答题&#xff0c;大家注意呀&#xff01; 博主码字不易点个关注吧,祝期末顺利~~ 1.单选题(2分) 下列说法错误的是:&#xff08; B &#xff09; A.选调生属于公务员系统 B.公务员属于事业编 C.选调生有基层锻炼的要求 D…...

【C语言练习】080. 使用C语言实现简单的数据库操作

080. 使用C语言实现简单的数据库操作 080. 使用C语言实现简单的数据库操作使用原生APIODBC接口第三方库ORM框架文件模拟1. 安装SQLite2. 示例代码:使用SQLite创建数据库、表和插入数据3. 编译和运行4. 示例运行输出:5. 注意事项6. 总结080. 使用C语言实现简单的数据库操作 在…...

IT供电系统绝缘监测及故障定位解决方案

随着新能源的快速发展&#xff0c;光伏电站、储能系统及充电设备已广泛应用于现代能源网络。在光伏领域&#xff0c;IT供电系统凭借其持续供电性好、安全性高等优势成为光伏首选&#xff0c;但在长期运行中&#xff0c;例如老化、潮湿、隐裂、机械损伤等问题会影响光伏板绝缘层…...

uniapp中使用aixos 报错

问题&#xff1a; 在uniapp中使用aixos&#xff0c;运行后报如下错误&#xff1a; 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 解决方案&…...

RNN避坑指南:从数学推导到LSTM/GRU工业级部署实战流程

本文较长&#xff0c;建议点赞收藏&#xff0c;以免遗失。更多AI大模型应用开发学习视频及资料&#xff0c;尽在聚客AI学院。 本文全面剖析RNN核心原理&#xff0c;深入讲解梯度消失/爆炸问题&#xff0c;并通过LSTM/GRU结构实现解决方案&#xff0c;提供时间序列预测和文本生成…...