后端: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);
}

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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/470015.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

无插件H5播放器EasyPlayer.js网页web无插件播放器vue和react详细介绍

EasyPlayer.js H5播放器&#xff0c;是一款能够同时支持HTTP、HTTP-FLV、HLS&#xff08;m3u8&#xff09;、WS、WEBRTC、FMP4视频直播与视频点播等多种协议&#xff0c;支持H.264、H.265、AAC、G711A、Mp3等多种音视频编码格式&#xff0c;支持MSE、WASM、WebCodec等多种解码方…

管家婆财贸ERP BB045.销售批量收款

最低适用版本: 财贸系列 22.8 插件简要功能说明: 销售类单据支持批量收款,简化收款做单流程更多细节描述见下方详细文档插件操作视频: 进销存类定制插件--销售批量收款 插件详细功能文档: 1. 应用中心增加菜单【销售批量收款】 a. 参考23.0应用中心-移动管理-物联宝-【…

基于MATLAB+opencv人脸疲劳检测

我们可以通过多种方式从现实世界中获取数字图像&#xff0c;比如&#xff1a;数码相机、扫描仪、计算机扫描和磁共振成像等等。在这些情况中&#xff0c;虽然我们肉眼看到的是图像&#xff0c;但是当需要将图像在数字设备中变换传输时&#xff0c;图像的每个像素则对应一个数值…

Prompt 工程

Prompt 工程 1. Prompt 工程简介 “预训练-提示预测”范式是近年来自然语言处理&#xff08;NLP&#xff09;领域的一个重要趋势&#xff0c;它与传统的“预训练-微调-预测”范式相比&#xff0c;提供了一种更为灵活和高效的模型应用方式。 Prompt工程是指在预训练的大型语言…

【Python TensorFlow】进阶指南(续篇一)

在前两篇文章中&#xff0c;我们介绍了TensorFlow的基础知识及其在实际应用中的初步使用&#xff0c;并探讨了更高级的功能和技术细节。本篇将继续深入探讨TensorFlow的高级应用&#xff0c;包括但不限于模型压缩、模型融合、迁移学习、强化学习等领域&#xff0c;帮助读者进一…

yolov7论文翻译

YOLOv7: Trainable bag-of-freebies sets new state-of-the-art for real-time object detectors 论文&#xff1a;https://arxiv.org/abs/2207.02696 代码&#xff1a;https://github.com/WongKinYiu/yolov7 摘要 YOLOv7 在速度和准确性方面均超越了所有已知的目标检测器&a…

Java基于SpringBoot+Vue的宠物共享平台的设计与实现(附源码,文档)

博主介绍&#xff1a;✌程序员徐师兄、7年大厂程序员经历。全网粉丝12w、csdn博客专家、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域和毕业项目实战✌ &#x1f345;文末获取源码联系&#x1f345; &#x1f447;&#x1f3fb; 精彩专栏推荐订阅&#x1f447;…

对称加密与非对称加密:密码学的基石及 RSA 算法详解

对称加密与非对称加密&#xff1a;密码学的基石及 RSA 算法详解 在当今数字化的时代&#xff0c;信息安全至关重要。对称加密和非对称加密作为密码学中的两种基本加密技术&#xff0c;为我们的数据安全提供了强大的保障。本文将深入探讨对称加密和非对称加密的特点、应用场景&…

43.第二阶段x86游戏实战2-提取游戏里面的lua

免责声明&#xff1a;内容仅供学习参考&#xff0c;请合法利用知识&#xff0c;禁止进行违法犯罪活动&#xff01; 本次游戏没法给 内容参考于&#xff1a;微尘网络安全 本人写的内容纯属胡编乱造&#xff0c;全都是合成造假&#xff0c;仅仅只是为了娱乐&#xff0c;请不要…

基于matlab的CNN食物识别分类系统,matlab深度学习分类,训练+数据集+界面

文章目录 前言&#x1f393;一、数据集准备&#x1f393;二、模型训练&#x1f340;&#x1f340;1.初始化&#x1f340;&#x1f340;2.加载数据集&#x1f340;&#x1f340;3.划分数据集&#xff0c;并保存到新的文件夹&#x1f340;&#x1f340;4.可视化数据集&#x1f34…

【webrtc】 RTP 中的 MID(Media Stream Identifier)

RTP 中的 MID(Media Stream Identifier) RID及其与MID的区别 cname与mid的对比【webrtc】CNAME 是rtprtcp中的Canonical Name(规范化名称) 同样都是RTP头部扩展: 基于mediasoup的最新的代码,学习,发现mid在创建RtpSendStream时是必须传递的参数: 例如 D:\XTRANS\soup\…

Node.Js+Knex+MySQL增删改查的简单示例(Typescript)

数据库: CREATE DATABASE MyDB; CREATE TABLE t_users (user_id int(11) NOT NULL,user_name varchar(10) NOT NULL ) ENGINEInnoDB DEFAULT CHARSETutf8; 项目结构: package.json如下&#xff0c;拷贝并替换你们本地的package.json后运行 npm install 命令安装所需要的依赖。…

【MATLAB代码】二维平面上的TDOA,使用加权最小二乘法,不限制锚点数量,代码可复制粘贴

本文所述的MATLAB代码实现了一个基于两步加权最小二乘法的二维目标定位算法,利用多个锚点(基站)和时间差到达(TDOA)数据来估计未知目标的位置。 订阅专栏后可以看到完整代码,复制到MATLAB空脚本上面即可直接运行。若需要单独下载,可通过下面的链接:https://download.cs…

python数据写入excel文件

主要思路&#xff1a;数据 转DataFrame后写入excel文件 一、数据格式为字典形式1 k e &#xff0c; v [‘1’, ‘e’, 0.83, 437, 0.6, 0.8, 0.9, ‘好’] 1、这种方法使用了 from_dict 方法&#xff0c;指定了 orient‘index’ 表示使用字典的键作为行索引&#xff0c;然…

【深度学习】LSTM、BiLSTM详解

文章目录 1. LSTM简介&#xff1a;2. LSTM结构图&#xff1a;3. 单层LSTM详解4. 双层LSTM详解5. BiLSTM6. Pytorch实现LSTM示例7. nn.LSTM参数详解 1. LSTM简介&#xff1a; LSTM是一种循环神经网络&#xff0c;它可以处理和预测时间序列中间隔和延迟相对较长的重要事件。LSTM通…

使用ookii-dialogs-wpf在WPF选择文件夹时能输入路径

在进行WPF开发时&#xff0c;System.Windows.Forms.FolderBrowserDialog的选择文件夹功能不支持输入路径&#xff1a; 希望能够获得下图所示的选择文件夹功能&#xff1a; 于是&#xff0c;通过NuGet中安装Ookii.Dialogs.Wpf包&#xff0c;并创建一个简单的工具类&#xff1a; …

【leetcode练习·二叉树】用「分解问题」思维解题 II

本文参考labuladong算法笔记[【强化练习】用「分解问题」思维解题 II | labuladong 的算法笔记] 技巧一 类似于判断镜像二叉树、翻转二叉树的问题&#xff0c;一般也可以用分解问题的思路&#xff0c;无非就是把整棵树的问题&#xff08;原问题&#xff09;分解成子树之间的问…

Qt 编写插件plugin,支持接口定义信号

https://blog.csdn.net/u014213012/article/details/122434193?spm1001.2014.3001.5506 本教程基于该链接的内容进行升级&#xff0c;在编写插件的基础上&#xff0c;支持接口类定义信号。 环境&#xff1a;Qt5.12.12 MSVC2017 一、创建项目 新建一个子项目便于程序管理【…

PaaS云原生:分布式集群中如何构建自动化压测工具

场景 测试环境中&#xff0c;压测常常依赖环境中的各种工具获取基础信息&#xff0c;而这些工具可能集中在某个中控机上&#xff0c;此时想打造的自动化工具的运行模式是&#xff1a; 通过中控机工具获取压测所需的基本信息在中控机部署压测工具&#xff0c;实际压测任务分发…