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

eNSP-Cloud(实现本地电脑与eNSP内设备之间通信)

说明&#xff1a; 想象一下&#xff0c;你正在用eNSP搭建一个虚拟的网络世界&#xff0c;里面有虚拟的路由器、交换机、电脑&#xff08;PC&#xff09;等等。这些设备都在你的电脑里面“运行”&#xff0c;它们之间可以互相通信&#xff0c;就像一个封闭的小王国。 但是&#…...

地震勘探——干扰波识别、井中地震时距曲线特点

目录 干扰波识别反射波地震勘探的干扰波 井中地震时距曲线特点 干扰波识别 有效波&#xff1a;可以用来解决所提出的地质任务的波&#xff1b;干扰波&#xff1a;所有妨碍辨认、追踪有效波的其他波。 地震勘探中&#xff0c;有效波和干扰波是相对的。例如&#xff0c;在反射波…...

visual studio 2022更改主题为深色

visual studio 2022更改主题为深色 点击visual studio 上方的 工具-> 选项 在选项窗口中&#xff0c;选择 环境 -> 常规 &#xff0c;将其中的颜色主题改成深色 点击确定&#xff0c;更改完成...

质量体系的重要

质量体系是为确保产品、服务或过程质量满足规定要求&#xff0c;由相互关联的要素构成的有机整体。其核心内容可归纳为以下五个方面&#xff1a; &#x1f3db;️ 一、组织架构与职责 质量体系明确组织内各部门、岗位的职责与权限&#xff0c;形成层级清晰的管理网络&#xf…...

使用van-uploader 的UI组件,结合vue2如何实现图片上传组件的封装

以下是基于 vant-ui&#xff08;适配 Vue2 版本 &#xff09;实现截图中照片上传预览、删除功能&#xff0c;并封装成可复用组件的完整代码&#xff0c;包含样式和逻辑实现&#xff0c;可直接在 Vue2 项目中使用&#xff1a; 1. 封装的图片上传组件 ImageUploader.vue <te…...

Keil 中设置 STM32 Flash 和 RAM 地址详解

文章目录 Keil 中设置 STM32 Flash 和 RAM 地址详解一、Flash 和 RAM 配置界面(Target 选项卡)1. IROM1(用于配置 Flash)2. IRAM1(用于配置 RAM)二、链接器设置界面(Linker 选项卡)1. 勾选“Use Memory Layout from Target Dialog”2. 查看链接器参数(如果没有勾选上面…...

python爬虫:Newspaper3k 的详细使用(好用的新闻网站文章抓取和解析的Python库)

更多内容请见: 爬虫和逆向教程-专栏介绍和目录 文章目录 一、Newspaper3k 概述1.1 Newspaper3k 介绍1.2 主要功能1.3 典型应用场景1.4 安装二、基本用法2.2 提取单篇文章的内容2.2 处理多篇文档三、高级选项3.1 自定义配置3.2 分析文章情感四、实战案例4.1 构建新闻摘要聚合器…...

ElasticSearch搜索引擎之倒排索引及其底层算法

文章目录 一、搜索引擎1、什么是搜索引擎?2、搜索引擎的分类3、常用的搜索引擎4、搜索引擎的特点二、倒排索引1、简介2、为什么倒排索引不用B+树1.创建时间长,文件大。2.其次,树深,IO次数可怕。3.索引可能会失效。4.精准度差。三. 倒排索引四、算法1、Term Index的算法2、 …...

Android15默认授权浮窗权限

我们经常有那种需求&#xff0c;客户需要定制的apk集成在ROM中&#xff0c;并且默认授予其【显示在其他应用的上层】权限&#xff0c;也就是我们常说的浮窗权限&#xff0c;那么我们就可以通过以下方法在wms、ams等系统服务的systemReady()方法中调用即可实现预置应用默认授权浮…...

OpenPrompt 和直接对提示词的嵌入向量进行训练有什么区别

OpenPrompt 和直接对提示词的嵌入向量进行训练有什么区别 直接训练提示词嵌入向量的核心区别 您提到的代码: prompt_embedding = initial_embedding.clone().requires_grad_(True) optimizer = torch.optim.Adam([prompt_embedding...