当前位置: 首页 > 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;并满…...

MogFace人脸检测工具问题排查大全:从路径错误到权限问题的解决方案

MogFace人脸检测工具问题排查大全&#xff1a;从路径错误到权限问题的解决方案 1. 工具简介与常见问题概述 MogFace人脸检测工具是基于CVPR 2022发表的MogFace模型开发的本地高精度检测解决方案。它能够准确识别多尺度、多姿态以及部分遮挡的人脸&#xff0c;并自动标注检测框…...

Vivado 时序约束文件 (.xdc) 管理与维护实战指南:从单文件到团队协作

Vivado 时序约束文件 (.xdc) 管理与维护实战指南&#xff1a;从单文件到团队协作 在FPGA设计流程中&#xff0c;时序约束文件&#xff08;.xdc&#xff09;如同交通信号灯&#xff0c;为设计指明方向与规则。随着项目规模扩大和团队协作需求增加&#xff0c;如何高效管理这些约…...

本地Cookie管理工具:安全导出与高效应用指南

本地Cookie管理工具&#xff1a;安全导出与高效应用指南 【免费下载链接】Get-cookies.txt-LOCALLY Get cookies.txt, NEVER send information outside. 项目地址: https://gitcode.com/gh_mirrors/ge/Get-cookies.txt-LOCALLY 在当今数字化环境中&#xff0c;Cookie作为…...

文明降级指南:回归纸笔躲避AI监控

AI监控时代的测试者困境在软件测试领域&#xff0c;人工智能的渗透已从效率工具演变为一种全景式的监控架构。AI驱动的测试套件能够以前所未有的速度执行用例、预测缺陷并生成报告&#xff0c;将测试周期与人力成本压缩至惊人水平。然而&#xff0c;这一技术乌托邦的背后&#…...

PX4无人机Offboard模式实战:从Gazebo仿真到真机避坑指南(附Python/C++代码对比)

PX4无人机Offboard模式全流程实战&#xff1a;从仿真到真机的Python/C双语言开发指南 1. Offboard模式核心原理与开发环境搭建 Offboard模式是PX4飞控系统中最为强大的控制模式之一&#xff0c;它允许开发者通过外部计算机&#xff08;如运行ROS的机载电脑&#xff09;发送精确…...

Vue3+ECharts水球图实战:手把手教你打造个性化数据展示组件

Vue3与ECharts水球图深度整合&#xff1a;打造企业级数据可视化组件 在数据驱动的时代&#xff0c;可视化呈现已成为现代Web应用的核心竞争力。水球图&#xff08;Liquid Fill Chart&#xff09;作为一种直观展示百分比数据的可视化形式&#xff0c;在仪表盘、进度监控和数据看…...

虚拟同步发电机这玩意儿搞并网真心刺激!今天咱们直接拆解一个双机并联的MATLAB/Simulink仿真模型,手把手看它怎么扛住240kW的暴力测试

MATLAB/Simulink虚拟同步发电机&#xff08;vsg) 双机并联 仿真模型&#xff0c;附参考文献。 电压电流双闭环控制&#xff0c;SPWM调制技术&#xff1a;运用正弦波脉宽调制&#xff08;SPWM&#xff09;技术&#xff0c;优化波形输出。 总负荷承载 轻松应对240kW有功功率及10k…...

千问3.5-27B多模态入门:图片理解支持mask区域聚焦,如‘只分析左上角区域’

千问3.5-27B多模态入门&#xff1a;图片理解支持mask区域聚焦&#xff0c;如‘只分析左上角区域’ 你是不是遇到过这种情况&#xff1a;给AI看一张复杂的图片&#xff0c;比如一张满是商品的货架&#xff0c;你只想让它分析左上角那个红色包装的零食&#xff0c;但它却把整张图…...

企业级低代码平台JeecgBoot全攻略:从零基础到实战应用

企业级低代码平台JeecgBoot全攻略&#xff1a;从零基础到实战应用 【免费下载链接】jeecg-boot 一款 AI 驱动的低代码平台&#xff0c;提供"零代码"与"代码生成"双模式——零代码模式一句话搭建系统&#xff0c;代码生成模式自动输出前后端代码与建表 SQL&…...

002:RAG 入门-LangChain 读取文本

正文 异步/等待解决了什么问题&#xff1f; 在传统同步I/O操作中&#xff08;如文件读取或Web API调用&#xff09;&#xff0c;调用线程会被阻塞直到操作完成。这在UI应用中会导致界面冻结&#xff0c;在服务器应用中则造成线程资源的浪费。async/await通过非阻塞的异步操作解…...