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

后端:Aop 面向切面编程

文章目录

    • 1. Aop 初步学习面向切面编程,@EnableAspectJAutoProxy
    • 2. AOP的核心概念
    • 3. 前置通知(@Before)
    • 4. 后置通知(@After)
    • 5. 返回通知(@AfterReturning)
    • 6. 异常通知(@AfterThrowing)
    • 7. 通知的执行顺序
    • 8. 切点表达式的抽取
    • 9. 切点表达式的书写
      • 9.1 execution‌ 实现切点表达式
      • 9.2 within 实现切点表达式
      • 9.3 ‌‌@annotation 实现切点表达式

1. Aop 初步学习面向切面编程,@EnableAspectJAutoProxy

添加依赖,在新建的Spring Boot项目下的pom.xml文件添加aop对应的依赖,如下:

<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-aop</artifactId>
</dependency>

通过切面,在不改变原有代码的前提下,增强源代码的业务能力。下面是一段演示aop的代码。

package com.lize.demo.aop;import org.springframework.stereotype.Component;
import org.springframework.stereotype.Service;@Service
public class UserService {public void add(){System.out.println("增加");}public void del(){System.out.println("删除");}public void query(){System.out.println("查询");}public void update(){System.out.println("修改");}
}

切面类,除了需要添加@Aspect注解表示这是一个切面类之外,还需要添加注解@Component表明这是一个Bean。通过注解@Around里边写上具体需要切入的方法,最终实现切面功能。

package com.lize.demo.aop;import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.stereotype.Component;@Aspect
// 标记为切面类
@Component
// 必须设置为切面的Bean
public class MyAspect {// 实现计时方法@Around("execution(* com.lize.demo.aop.UserService.*(..) )")// 切点表达式// * 表示方法的访问权限 可以为public等public void logTime(ProceedingJoinPoint point){long begin = System.currentTimeMillis();// 执行具体的方法try {point.proceed();} catch (Throwable throwable) {throwable.printStackTrace();}long end = System.currentTimeMillis();System.out.println("用时为:"+(end-begin));}
}

单元测试类

package com.lize.demo.aop;import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.EnableAspectJAutoProxy;@SpringBootTest(classes = AopDemo.class)
@ComponentScan
public class AopDemo {@Testpublic void test(@Autowired UserService us){us.add();}
}

运行结果如下:
在这里插入图片描述
只运行了add方法,原本只是输出“增加”,但是添加切面之后,除了输出“增加”这个字符串之外,还额外输出执行这个add方法的用时。在这个过程中,出现问题有,运行上述代码之后,切面没有起作用,也就是打印结果依旧为“增加”,但是我的项目是一个Spring Boot项目,Spring Boot项目会自动通过启动类帮我们加上@EnableAspectJAutoProxy,从而使切面起作用。解决方法可以是在这个单元测试类上加上这个注解@EnableAspectJAutoProxy,或者去掉@ComponentScan注解以及把@SpringBootTest(classes = AopDemo.class)这个注解修改为@SpringBootTest。出现这个问题的原因是因为我的单元测试所在的目录与Spring Boot项目启动类所在的目录不在同一个目录下,如果在同一个目录下,可以省略这个注解 @EnableAspectJAutoProxy,不过,建议加上。
在这里插入图片描述

2. AOP的核心概念

目标对象(target):目标对象指将要被增强的对象。即包含主业务逻辑的类的对象,要增强的对象通常会有很多个
切面(aspect):指放存放增强代码的类。
通知(advice):用来放增强的代码的那个方法,通知方式可以有环绕通知(@Around)【代码增强在目标方法的任意位置,更加通用】、前置通知(@Before)【目标方法之前执行】、后置通知(@After)【目标方法之后执行】、异常通知(@AfterThrowing)【目标方法出现了异常执行】、返回通知(@AfterReturning)【目标方法返回值执行】。
切点(pointcut):增强代码要切入到哪些方法中,在代码中通常写的是切点表达式。
连接点(Join point):通知和目标方法的一个桥梁,要获取目标方法的信息,就得通过JoinPoint。

关于连接点JoinPoint,在上面的代码中使用的是这个ProceedingJoinPoint,查看源码可知,ProceedingJoinPoint继承JoinPoint,并且还添加了如下方法的。
在这里插入图片描述

3. 前置通知(@Before)

现在我想要在执行上述代码中的add方法之前获取add这个方法名,此时可以考虑使用前置通知,在上述代码的基础之上,修改对应的注解即可,参考代码如下:

package com.lize.demo.aop.advice;import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.stereotype.Component;@Aspect
// 标记为切面类
@Component
// 必须设置为切面的Bean
public class MyAspect {@Before("execution(* com.lize.demo.aop.advice.UserService.*(..) )")// 切点表达式public void before(JoinPoint joinPoint){String name = joinPoint.getSignature().getName();// 获取当前执行的方法名System.out.println("当前执行的方法是:"+name);}
}

运行结果:
在这里插入图片描述

4. 后置通知(@After)

在目标方法执行之后再执行的代码,如下:

@After("execution(* com.lize.demo.aop.advice.UserService.*(..) )")
// 切点表达式
public void after(JoinPoint joinPoint){System.out.println("后置通知");
}

在这里插入图片描述

5. 返回通知(@AfterReturning)

在try\catch\finally这个防止异常代码结构中,如果在try中有返回值,依旧会执行finally里边的代码

获取目标方法的返回值:

@AfterReturning(value = "execution(* com.lize.demo.aop.advice.UserService.*(..) )",returning = "ans")
public void afterReturning(JoinPoint joinPoint,Object ans){System.out.println("返回通知。。目标方法的返回值为:"+ans);
}

上述代码中为了获取返回值对象,在注解中使用returning进行接收,查看@AfterReturning的源码就可以知道了。
在这里插入图片描述
运行结果如下:
在这里插入图片描述
因为在add方面里边没有返回值,因此上述返回值为null。

6. 异常通知(@AfterThrowing)

当目标方法出现异常时执行的方法,参考代码如下:
在这里插入图片描述

@AfterThrowing(value = "execution(* com.lize.demo.aop.advice.UserService.*(..) )",throwing = "ans")
public void afterThrowing(JoinPoint joinPoint,Exception ans){System.out.println("异常通知。。目标方法的报错信息为:"+ans);
}

通过throwing 获取异常信息,查看一下@AfterThrowing的源码就知道了。
在这里插入图片描述
运行结果如下:
在这里插入图片描述

7. 通知的执行顺序

  • 正常情况下:先执行前置通知、再执行目标方法,然后执行返回通知、最后执行后置通知;
  • 异常情况下:先执行前置通知、再执行目标方法、然后执行异常通知、最后执行后置通知。

在环绕通知中可以包含其他四种通知,因此环绕通知也是通用的。

8. 切点表达式的抽取

在这里插入图片描述
看一下上面的切点表达式,如果要进行修改,那么这些都需要进行修改,为此,可以利用切点表达式的抽取,把切点表达式抽取出来,这样只需要修改一处地方,在需要引入切点表达式的地方,引入方法即可,如下:

package com.lize.demo.aop.advice;import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;@Aspect
// 标记为切面类
@Component
// 必须设置为切面的Bean
public class MyAspect {@Pointcut("execution(* com.lize.demo.aop.advice.UserService.*(..) )")public void pointCut(){}@Before("pointCut()")// 切点表达式public void before(JoinPoint joinPoint){String name = joinPoint.getSignature().getName();// 获取当前执行的方法名System.out.println("当前执行的方法是:"+name);}@After("pointCut()")// 切点表达式public void after(JoinPoint joinPoint){System.out.println("后置通知");}@AfterReturning(value = "pointCut()",returning = "ans")public void afterReturning(JoinPoint joinPoint,Object ans){System.out.println("返回通知。。目标方法的返回值为:"+ans);}@AfterThrowing(value = "pointCut()",throwing = "ans")public void afterThrowing(JoinPoint joinPoint,Exception ans){System.out.println("异常通知。。目标方法的报错信息为:"+ans);}
}

9. 切点表达式的书写

9.1 execution‌ 实现切点表达式

execution‌ 能匹配到方法级别

execution(modifiers-pattern? ret-type-pattern declaring-type-pattern? name-pattern(param-pattern) throws-pattern?)
  • execution 表示切点标识符
  • modifiers-pattern 表示访问修饰符,即目标方法的访问权限,不写代表所有
  • ret-type-pattern 表示目标方法的返回值类型,可以为void,*代表所有
  • declaring-type-pattern 表示完整限定名,包含包名和类名;
    • 包名
      • 可以写完整的,如com.xx.service
      • 也可以这样写,如com.xx.* 等价于 com.xx.dao、com.xx.service等,反正”*“只能代表一个层级吧!比如com.xx.service.inter是不能匹配到的
      • 如果想任意层级 ,可以这样写com.xx…,这样就可以匹配到com.xx.service.inter、com.xx.service.inter.until等
    • 类名
      • ”*“代表所有类
      • com.xx…*可以匹配到com.xx.service.utils.inter.任意包.任意类
  • name-pattern 表示方法,"*"代表匹配所有
  • param-pattern 表示方法的参数,”…“表示任意参数,不写表示匹配无参数

9.2 within 实现切点表达式

within 只能匹配到级别,表示类下面的所有方法都能匹配到。

@Before("within(com.lize.demo.aop.advice.UserService)")
// 切点表达式
public void before(JoinPoint joinPoint){String name = joinPoint.getSignature().getName();// 获取当前执行的方法名System.out.println("当前执行的方法是:"+name);
}

9.3 ‌‌@annotation 实现切点表达式

@annotation 用于匹配特定注解的方法
这里定义了一个自定义的注解

package com.lize.demo.aop.advice;import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD})
public @interface MyLog {String value();
}

在这里插入图片描述

@Before("@annotation(log)")
// 切点表达式
public void before(JoinPoint joinPoint,MyLog log){String name = joinPoint.getSignature().getName();// 获取当前执行的方法名System.out.println("当前执行的方法是:"+name+"  "+log);
}

运行结果:
在这里插入图片描述

相关文章:

后端:Aop 面向切面编程

文章目录 1. Aop 初步学习面向切面编程&#xff0c;EnableAspectJAutoProxy2. AOP的核心概念3. 前置通知&#xff08;Before&#xff09;4. 后置通知&#xff08;After&#xff09;5. 返回通知&#xff08;AfterReturning&#xff09;6. 异常通知&#xff08;AfterThrowing&…...

大数据机器学习算法与计算机视觉应用02:线性规划

Linear Programming Definition of linear programmingmax and min-cost max flowlinear program to solve minimax optimal strategies in gamesAlgoithms for linear programmingl1 regressionSeidel’s 2-dimensional linear programming algorithm linear program 线性规…...

godot——主题、Theme、StyleBox

我刚开始被这些术语吓到了&#xff0c;一直不敢去接触它们&#xff0c;都用的默认样式。现在好不容易有点思路了&#xff0c;记录下来。 下面看看怎么自定义样式。 1.先新建一个Theme 2.再次点击创建好的Theme 得到 图1 这样一个面板。&#xff08;看不懂没事&#xff0c;继…...

深入理解接口测试:实用指南与最佳实践5.0(一)

✨博客主页&#xff1a; https://blog.csdn.net/m0_63815035?typeblog &#x1f497;《博客内容》&#xff1a;.NET、Java.测试开发、Python、Android、Go、Node、Android前端小程序等相关领域知识 &#x1f4e2;博客专栏&#xff1a; https://blog.csdn.net/m0_63815035/cat…...

SQL面试题——飞猪SQL面试 重点用户

飞猪SQL面试题—重点用户 在一些场景中我们经常听到这样的一些描述&#xff0c;例如20%的用户贡献了80%的销售额&#xff0c;或者是20%的人拥有着80%的财富&#xff0c;你知道这样的数据是怎么算出来的吗 数据如下,uid 是用户的id ,amount是用户的消费金额 |uid|amount| ---…...

Angular 和 Vue2.0 对比

前言 &#xff1a;“业精于勤&#xff0c;荒于嬉&#xff1b;行成于思&#xff0c;毁于随” 很久没写博客了&#xff0c;大多记录少进一步探查。 Angular 和 Vue2.0 对比&#xff1a; 一.概念 1.1 Angular 框架&#xff1a; 是一款由谷歌开发的开源web前端框架&#xff08;核…...

websocket服务器(协程风格)--swoole进阶篇

swoole的websocket服务器(协程风格)示例真不算友善,从头了解到尾,那还好,但是谁有那么多时间从头到尾了解。示例不够针对性,写websocket就该单独写websocket的东西,偏偏又加上http的东西。这里我来解读一下websocket服务器(协程风格)示例 <?php use Swoole\Http\…...

Windows C/C++ Socket 编程

承接上文&#xff1a;socket 编程 本文目录 Windows Client 端WSADATA 结构体WSAStartup() 函数SOCKET 以及 socket() 函数sockaddr_ininet_pton() 函数in_addr structmemcpy()connect() 函数send() 函数recv() 函数 Windows Server 端 在进行 socket 编程之前&#xff0c;你要…...

计算两个结构的乘法

在行列可自由变换的平面上&#xff0c;2点结构有3个 3点结构有6个 计算2*2 2a1*2a14a6 2a1*2a24a8 2a1*2a34a12 显然2a1*2a14a6因为这3个结构都分布在同一列上&#xff0c;就是整数乘法。2a1*2a2的结果有2种写法&#xff0c;一种外形像2a1细节为2a2&#xff0c;一种外形为2…...

学校服务器连接pycharm配置2

上一个可能还是有点问题&#xff0c;因为实际在跑的时候读取的其实是本地的anaconda&#xff0c;这个重新整了一下流程 首先在学校服务器先激活自己创建的虚拟环境&#xff0c;这里就不截图了 然后在pycharm里面打开设置 选择这个python解释器 这里有添加解释器 选择SSH …...

AI赋能电商:创新应用提升销售与用户体验

目录 一、引言 二、AI技术在电商领域的创新应用 三、AI技术提高电商销售效率和用户体验的实践路径 一、引言 随着人工智能&#xff08;AI&#xff09;技术的不断成熟&#xff0c;电商行业正迎来一场深刻的变革。AI技术在购物推荐、会员分类、商品定价等方面的创新应用&…...

详解kafka消息发送重试机制的案例

在 Kafka 生产者中实现消息发送的重试机制&#xff0c;可以通过配置 KafkaProducer 的相关属性来实现。以下是一些关键的配置项&#xff1a; retries&#xff1a;设置生产者发送失败后重试的次数。 retry.backoff.ms&#xff1a;设置生产者在重试前等待的时间。 buffer.memo…...

linux文本管理!!!

文章目录 第1章 文本过滤/查看命令1.echo&#xff1a;输出文本2.cat&#xff1a;合并文件或查看文件内容3.head&#xff1a;显示文件头部信息4.tail&#xff1a;显示文件尾部信息5.wc: 统计文本行号6.less&#xff1a;分页显示文件内容7.grep&#xff1a;文本过滤工具8.定向符号…...

软件设计师-计算机体系结构分类

计算机体系结构分类 Flynn分类法 根据不同的指令流数据流组织方式分类单指令流但数据流SISD,单处理器系统单指令多数据流SIMD&#xff0c;单指令流多数据流是一种采用一个控制器来控制多个处理器&#xff0c;同时对一组数据&#xff08;又称“数据矢量”&#xff09;中的每一…...

《基于深度学习的车辆行驶三维环境双目感知方法研究》

复原论文思路&#xff1a; 《基于深度学习的车辆行驶三维环境双目感知方法研究》 1、双目测距的原理 按照上述公式算的话&#xff0c;求d的话&#xff0c;只和xl-xr有关系&#xff0c;这样一来&#xff0c;是不是只要两张图像上一个测试点的像素位置确定&#xff0c;对应的深…...

jwt用户登录,网关给微服务传递用户信息,以及微服务间feign调用传递用户信息

1、引入jwt依赖 <dependency><groupId>io.jsonwebtoken</groupId><artifactId>jjwt</artifactId><version>0.9.1</version></dependency> 2、Jwt工具类&#xff0c;生成token以及解析token package com.niuniu.gateway.uti…...

ubontu安装anaconda

1.下载 Anaconda 安装脚本 2. 复制到服务器上/home/username文件夹中&#xff0c;进入文件夹&#xff0c;执行&#xff1a; bash Anaconda3-2024.10-1-Linux-x86_64.sh一直按回车&#xff0c;然后输入yes同意协议。 3. 初始化 Anaconda 环境&#xff0c;会自动配置环境变量&a…...

【Docker容器化技术】docker安装与配置、常用命令、容器数据卷、应用部署实战、Dockerfile、服务编排docker-compose、私有仓库

文章目录 一、Docker的安装与配置1、docker概述2、安装docker3、docker架构4、配置镜像加速器 二、Docker命令1、服务相关命令2、镜像相关命令3、容器相关命令 三、Docker容器数据卷1、数据卷概念及作用2、配置数据卷3、配置数据卷容器 四、Docker应用部署实战1、部署MySQL2、部…...

Python模拟A卷实操题

1.某机械公司生产两种产品。A的单件利润分别是100元&#xff0c;B的单件利润是150元。 每种产品由三种材料构成&#xff0c;现给出每种材料的库存&#xff08;库存小于100000&#xff09;&#xff0c;求利润最大的生产方案。输入说明&#xff1a;第一行给出生产每件A产品所需要…...

Leetcode 检测相邻递增子数组

3349. 检测相邻递增子数组 I 给你一个由 n 个整数组成的数组 nums &#xff0c;请你找出 k 的 最大值&#xff0c;使得存在 两个 相邻 且长度为 k 的 严格递增 子数组 。具体来说&#xff0c;需要检查是否存在从下标 a 和 b (a < b) 开始的 两个 子数组&#xff0c;并满…...

(二)原型模式

原型的功能是将一个已经存在的对象作为源目标,其余对象都是通过这个源目标创建。发挥复制的作用就是原型模式的核心思想。 一、源型模式的定义 原型模式是指第二次创建对象可以通过复制已经存在的原型对象来实现,忽略对象创建过程中的其它细节。 📌 核心特点: 避免重复初…...

TRS收益互换:跨境资本流动的金融创新工具与系统化解决方案

一、TRS收益互换的本质与业务逻辑 &#xff08;一&#xff09;概念解析 TRS&#xff08;Total Return Swap&#xff09;收益互换是一种金融衍生工具&#xff0c;指交易双方约定在未来一定期限内&#xff0c;基于特定资产或指数的表现进行现金流交换的协议。其核心特征包括&am…...

项目部署到Linux上时遇到的错误(Redis,MySQL,无法正确连接,地址占用问题)

Redis无法正确连接 在运行jar包时出现了这样的错误 查询得知问题核心在于Redis连接失败&#xff0c;具体原因是客户端发送了密码认证请求&#xff0c;但Redis服务器未设置密码 1.为Redis设置密码&#xff08;匹配客户端配置&#xff09; 步骤&#xff1a; 1&#xff09;.修…...

稳定币的深度剖析与展望

一、引言 在当今数字化浪潮席卷全球的时代&#xff0c;加密货币作为一种新兴的金融现象&#xff0c;正以前所未有的速度改变着我们对传统货币和金融体系的认知。然而&#xff0c;加密货币市场的高度波动性却成为了其广泛应用和普及的一大障碍。在这样的背景下&#xff0c;稳定…...

Linux C语言网络编程详细入门教程:如何一步步实现TCP服务端与客户端通信

文章目录 Linux C语言网络编程详细入门教程&#xff1a;如何一步步实现TCP服务端与客户端通信前言一、网络通信基础概念二、服务端与客户端的完整流程图解三、每一步的详细讲解和代码示例1. 创建Socket&#xff08;服务端和客户端都要&#xff09;2. 绑定本地地址和端口&#x…...

基于IDIG-GAN的小样本电机轴承故障诊断

目录 🔍 核心问题 一、IDIG-GAN模型原理 1. 整体架构 2. 核心创新点 (1) ​梯度归一化(Gradient Normalization)​​ (2) ​判别器梯度间隙正则化(Discriminator Gradient Gap Regularization)​​ (3) ​自注意力机制(Self-Attention)​​ 3. 完整损失函数 二…...

日常一水C

多态 言简意赅&#xff1a;就是一个对象面对同一事件时做出的不同反应 而之前的继承中说过&#xff0c;当子类和父类的函数名相同时&#xff0c;会隐藏父类的同名函数转而调用子类的同名函数&#xff0c;如果要调用父类的同名函数&#xff0c;那么就需要对父类进行引用&#…...

Axure 下拉框联动

实现选省、选完省之后选对应省份下的市区...

医疗AI模型可解释性编程研究:基于SHAP、LIME与Anchor

1 医疗树模型与可解释人工智能基础 医疗领域的人工智能应用正迅速从理论研究转向临床实践,在这一过程中,模型可解释性已成为确保AI系统被医疗专业人员接受和信任的关键因素。基于树模型的集成算法(如RandomForest、XGBoost、LightGBM)因其卓越的预测性能和相对良好的解释性…...

深入理解 React 样式方案

React 的样式方案较多,在应用开发初期,开发者需要根据项目业务具体情况选择对应样式方案。React 样式方案主要有: 1. 内联样式 2. module css 3. css in js 4. tailwind css 这些方案中,均有各自的优势和缺点。 1. 方案优劣势 1. 内联样式: 简单直观,适合动态样式和…...