springboot-aop-学习笔记

什么是AOP?

  • AOP英文全称:Aspect Oriented Programming(面向切面编程、面向方面编程),其实说白了,就是 需要 某个通用的方法时,可以创建一个模板,模板里面就有这些通用的方法,然后再把需要这些方法的方法们嵌套进去运行,很像动态代理

同时需要增加一个方法,改动原来的代码很麻烦,所以直接使用一个模板来调用原来的代码,模板里面就有需要增加的方法:

这么说可能还有点抽象,举个例子,现在需要给项目中逻辑层每个方法添加一个记录运行耗时的功能,如果在每个方法里面都敲上这么一段新增的代码即麻烦又显得代码臃肿,这个时候就可以使用AOP:

import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.stereotype.Component;@Slf4j
@Aspect //声明为AOP类
@Component
public class TimeAspect {@Around("execution(* com.zeyu.service.*.*(..))") //切入点表达式,声明要生效的范围public Object recordTime(ProceedingJoinPoint joinPoint) throws Throwable {//1、记录开始时间long begin = System.currentTimeMillis();//2、调用原始方法运行Object result = joinPoint.proceed();//3、记录结束时间,计算方法执行耗时long end = System.currentTimeMillis();log.info(joinPoint.getSignature() + "方法执行耗时{}ms", end - begin);return result;}}

 如上述代码,在执行逻辑层每个方法前都会先进行记录开始时间的操作,然后再执行目标方法,目标方法结束之后,再记录结束时间,再输出耗时,这样不仅节省了代码量,也更加便于维护管理

AOP的核心概念:

1. 连接点:JoinPoint,可以被AOP控制的方法(暗含方法执行时的相关信息)

2. 通知:Advice,指哪些重复的逻辑,也就是共性功能(最终体现为一个方法)

3.切入点:PointCut,匹配连接点的条件,通知仅会在切入点方法执行时被应用

4.切面:Aspect,描述通知与切入点的对应关系(通知+切入点)

5.目标对象:Target,通知所应用的对象

AOP的运用

第一步:导入依赖

在pom.xml文件中添加AOP的起步依赖

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

第二步,编写程序:

建议新建一个aop包专门放aop程序

一个aop程序的规范有如下几点

  • 类声明:必须在类上添加@Aspect注解声明当前类为切面类
  • 方法声明:必须在方法上添加@通知类型注解(切入点表达式),声明通知类型和切入点范围
  • 如果通知类型为Around,需要定义ProceedingJoinPoint  joinPoint形参(形参名可自定义),并使用joinPoint的process方法调用连接点
  • 如果同通知类型的aop程序有多个,可以添加@Order()注解设置执行优先级,直接在括号里面填写数字,数字越小越先执行

AOP的通知类型:

  • @Around:环绕通知,此注解标注的通知方法在目标方法前、后都被执行

  • @Before:前置通知,此注解标注的通知方法在目标方法前被执行

  • @After :后置通知,此注解标注的通知方法在目标方法后被执行,无论是否有异常都会执行

  • @AfterReturning : 返回后通知,此注解标注的通知方法在目标方法后被执行,有异常不会执行

  • @AfterThrowing : 异常后通知,此注解标注的通知方法发生异常后执行

AOP的切入点表达式:

一、@execution

主要根据方法的返回值、包名、类名、方法名、方法参数等信息来匹配,语法为:

execution(访问修饰符?  返回值  包名.类名.?方法名(方法参数) throws 异常?)

其中带?的表示可以省略的部分

  • 访问修饰符:可省略(比如: public、protected)

  • 包名.类名: 可省略

  • throws 异常:可省略(注意是方法上声明抛出的异常,不是实际抛出的异常)

示例:

@Before("execution(void com.zeyu.service.impl.DeptServiceImpl.delete(java.lang.Integer))")

可以使用通配符描述切入点

  • * :单个独立的任意符号,可以通配任意返回值、包名、类名、方法名、任意类型的一个参数,也可以通配包、类、方法名的一部分

  • .. :多个连续的任意符号,可以通配任意层级的包,或任意类型、任意个数的参数

切入点表达式的语法规则:

  1. 方法的访问修饰符可以省略

  2. 返回值可以使用*号代替(任意返回值类型)

  3. 包名可以使用*号代替,代表任意包(一层包使用一个*

  4. 使用..配置包名,标识此包以及此包下的所有子包

  5. 类名可以使用*号代替,标识任意类

  6. 方法名可以使用*号代替,表示任意方法

  7. 可以使用 * 配置参数,一个任意类型的参数

  8. 可以使用.. 配置参数,任意个任意类型的参数

注意事项:

  • 根据业务需要,可以使用 且(&&)、或(||)、非(!) 来组合比较复杂的切入点表达式

execution(* com.zeyu.service.DeptService.list(..)) || execution(* com.zeyu.service.DeptService.delete(..))

切入点表达式的书写建议:

  • 所有业务方法名在命名时尽量规范,方便切入点表达式快速匹配。如:查询类方法都是 find 开头,更新类方法都是update开头

二、@annotation

适用于无规则匹配

需要:

编写自定义注解

在作为连接点的方法上添加自定义注解

自定义注解不需要再添加特别的注解,只需要声明作用范围和生效时间即可

例:

@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface OPLog {
}

 像这样定义好一个自定义注解好,再去需要使用aop的连接点业务方法上添加自定义注解,例:

@OPLog@Overridepublic List<Dept> list() {return deptMapper.list();}//rollbackFor = Exception.class 表示所有异常都会触发回滚,rollbackFor = ? 指定什么异常会回滚事务,默认是运行时异常将回滚@OPLog@Transactional(rollbackFor = Exception.class)     //事务管理Transactional  如果打上该注解的方法、对象、接口出现异常,就会进行回滚@Overridepublic void delete(Integer id) {try {deptMapper.deleteById(id);  //根据id删除部门empService.deleteByDeptId(id);  //根据部门id删除员工} finally {DeptLog deptlog = new DeptLog();
//            deptlog.setCreateTime(LocalDateTime.now());
//            deptlog.setOperation("执行了解散部门的操作,此次解散的是" + id + "号部门");
//            deptLogService.insert(deptlog);}}@OPLog@Overridepublic void add(Dept dept) {dept.setCreateTime(LocalDateTime.now());dept.setUpdateTime(LocalDateTime.now());deptMapper.insert(dept);}@OPLog@Overridepublic void upadte(Dept dept) {dept.setUpdateTime(LocalDateTime.now());deptMapper.update(dept);}@OPLog@Overridepublic Dept getById(Integer id) {Dept dept = deptMapper.getById(id);return dept;}

 打上注解之后,再在AOP程序上的通知类型注解里面使用@annotation注解声明范围,例:

@Pointcut("@annotation(com.zeyu.aop.OPLog)")public void log()

如此,该通知的切入点范围就是那些添加了自定义注解的业务方法

@Pointcut

如果有多个通知的切入点范围都是同一个切入点表达式,则可以把该切入点表达式提取出来:
创建一个方法,不需要方法体,在其上添加@Pointcut注解,注解value值为提取出来的切入点表达式,然后在需要使用该切入点表达式的通知注解里写上该方法名即可,示例:

连接点

前面有提到,在Around类型通知里,获取连接点信息需要在形参列表里定义一个ProceedingJoinPoint类型的形参,如果需要调用连接点,则使用该形参的process方法

而在其它类型通知里,获取连接点信息需要的是JoinPoint类型的形参,它是ProceedingJoinPoint的父类型

通过该形参,可以获取连接点的各种信息

下面是一个记录日志的例子,其中就用到了很多连接点信息:

package com.zeyu.aop;import com.alibaba.fastjson.JSONObject;
import com.zeyu.pojo.OperateLog;
import com.zeyu.service.OperateLogService;
import com.zeyu.utils.JwtUtils;
import jakarta.servlet.http.HttpServletRequest;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;import java.util.Arrays;
import java.util.Map;@Slf4j
@Aspect
@Component
public class OperateLogAspect { //操作日志@Autowiredprivate HttpServletRequest request;@Autowiredprivate OperateLogService operateLogService;@Pointcut("@annotation(com.zeyu.aop.OPLog)")public void log(){}@Around("log()")public Object record(ProceedingJoinPoint JoinPoint) throws Throwable {log.info("开始记录本次操作...");//记录日志OperateLog operateLog = new OperateLog();//操作人IDString jwt = request.getHeader("token");Map<String, Object> claims = JwtUtils.parseJWT(jwt);operateLog.setOperateUser((Integer) claims.get("id"));//操作类名operateLog.setClassName(JoinPoint.getTarget().getClass().getName());//操作方法名operateLog.setMethodName(JoinPoint.getSignature().getName());//操作方法参数operateLog.setMethodParams(Arrays.toString(JoinPoint.getArgs()));long start = System.currentTimeMillis();//执行目标方法Object proceed = JoinPoint.proceed();long end = System.currentTimeMillis();//方法返回值operateLog.setReturnValue(JSONObject.toJSONString(proceed));//操作耗时operateLog.setCostTime(end - start);operateLogService.insert(operateLog);return proceed;}
}

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

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

相关文章

effective python学习笔记_类与接口

用组合类实现多层结构而不用内置类型 例子&#xff1a;成绩单&#xff0c;存储学生各科成绩多个然后加权重&#xff0c;如果用字典类型会导致字典有多层嵌套结构 思想 当用内置类型如字典元组等结构出现超过二层的多层嵌套结构时&#xff0c;读起来会比较难懂&#xff0c;此时…

nestjs 全栈进阶--中间件

视频教程 22_nest中中间件_哔哩哔哩_bilibili 1. 介绍 在Nest.js框架中&#xff0c;中间件&#xff08;Middleware&#xff09;是一个非常重要的概念&#xff0c;它是HTTP请求和响应生命周期中的一个重要组成部分&#xff0c;允许开发者在请求到达最终的目的控制器方法之前或…

04.进程间通信

进程间通信基本概念 IPC&#xff08;Inter Process Communication&#xff09; 进程间通信 进程通信就是不同进程之间进行信息的交换或传播 为什么进程之间实现通信和困难 因为进程之间具有独立性&#xff0c;数据独立&#xff0c;程序可能独立也可能不独立&#xff08;父子进…

从面试官视角出发,聊聊产品经理的面试攻略

一、请进行自我介绍 这题基本是面试的开胃菜了&#xff0c;估计面试多的&#xff0c;自己答案都能倒背如流啦。 其实自我介绍还是蛮重要的&#xff0c;对我来说主要有 3 个作用&#xff1a;面试准备、能力预估、思维评估。 面试准备&#xff1a;面试官每天都要面 3 ~6 人&am…

Windows系统下通过nginx配置多项目

文章目录 前言大概思路实际操作记录&#xff1a;查看nginx 错误日志问下AI注意点&#xff1a; 当访问域名根路径时&#xff0c;重定向到/pc总结 前言 在windows电脑启动一个nginx 测试配置多前端项目&#xff0c;一个pc端&#xff08;vue3tsvite &#xff0c;history路由&…

【C++进阶】C++中的map和set

一、关联式容器 在初阶阶段&#xff0c;我们已经接触过STL 中的部分容器&#xff0c;比如&#xff1a; vector 、 list 、 deque&#xff0c; forward_list 等&#xff0c;这些容器统称为序列式容器&#xff0c;因为其底层为线性序列的数据结构&#xff0c;里面存储的是元素本…

ChatGLM3-6B部署与微调及微调后使用

记录ChatGLM3-6B部署及官方Lora微调示例详细步骤及如何使用微调后的模型进行推理 一、下载代码 使用git clone 命令下载源码 git clone https://github.com/THUDM/ChatGLM3.git 如图所示 二、下载模型 模型权重文件从魔塔进行下载&#xff0c;不需要翻墙。权重文件比较大&…

【docker 】push 镜像到私服

查看镜像 docker images把这个hello-world 推送到私服 docker push hello-world:latest 报错了。不能推送。需要标记镜像 标记Docker镜像 docker tag hello-world:latest 192.168.2.1:5000/hello-world:latest 将Docker镜像推送到私服 docker push 192.168.2.1:5000/hello…

设计合理的IT运维服务目录:打造高效运维的蓝图

在数字化转型的浪潮中&#xff0c;一个设计合理、内容详尽的IT运维服务目录是连接服务提供者与消费者之间的桥梁&#xff0c;它不仅体现了服务设计的专业性&#xff0c;还直接影响着运维效率和服务质量。如何设计出既合理又高效的IT运维服务目录&#xff1f;让我们结合ITIL 4框…

【全开源】JAVA台球助教台球教练多端系统源码支持微信小程序+微信公众号+H5+APP

功能介绍 球厅端&#xff1a;球厅认证、教练人数、教练的位置记录、助教申请、我的项目、签到记录、我的钱包、数据统计 教练端&#xff1a;我的页面&#xff0c;数据统计、订单详情、保证金、实名认证、服务管理、紧急求助、签到功能 用户端&#xff1a;精准分类、我的助教…

Maven的使用

1.第一个Maven工程 1.1 创建约定目录结构 ​ Hello ​ src ​ ——main(存放主程序) ​ ————java(存放源代码文件) ​ ————resources(存放配置文件和资源文件) ​ ——test(存放测试程序) ​ ————java ​ ————resources ​ pom.xml 1.2 创建核心文件 pom.xml …

mysql等保测评2.0命令-三级

版本 Win默认安装位置 C:\Program Files\MySQL\MySQL Server 8.0\bin 版本&#xff1a;select version() from dual; 身份鉴别 a应对登录的用户进行身份标识和鉴别&#xff0c;身份标识具有唯一性&#xff0c;身份鉴别信息具有复杂度要求并定期更换&#xff1b; 1、SELEC…

新能源汽车中HEV与PHEV分别代表什么车型,它们与传统燃油车都有什么区别?

前言 新能源汽车正逐渐成为全球汽车工业的主流方向&#xff0c;而HEV&#xff08;Hybrid Electric Vehicle&#xff09;和PHEV&#xff08;Plug-in Hybrid Electric Vehicle&#xff09;这两种混合动力车型在这一转型过程中扮演着重要角色。下面我们详细探讨HEV与PHEV的定义&a…

RAG查询改写方法概述

在RAG系统中&#xff0c;用户的查询是丰富多样的&#xff0c;可能存在措辞不准确和缺乏语义信息的问题。这导致使用原始的查询可能无法有效检索到目标文档。 因此&#xff0c;将用户查询的语义空间与文档的语义空间对齐至关重要&#xff0c;目前主要有查询改写和嵌入转换两种方…

扫码查看文件是如何实现的?文件活码在线生成的方法

现在很多场景下会通过扫码的方式来查看文件&#xff0c;这种方式可以让更多的人同时通过扫码的方式来查看二维码&#xff0c;有利于文件的快速分享以及用户获取内容的个人体验&#xff0c;而且可以保护文件的安全性&#xff0c;那么如何制作文件二维码呢&#xff1f; 文件二维…

中国211大学全部排名一览表

211大学是指中国教育部实施的名为“211工程”的高等教育发展战略中被选为重点支持的高等院校。这个名称来源于项目的启动背景和目标&#xff1a;“211”中的“21”代表21世纪&#xff0c;意味着该项目面向21世纪的中国高等教育发展&#xff1b;“1”则意指要重点建设大约100所左…

商机无限:实景无人自动直播软件带动实体店和电商行业新一波繁荣!

直播带货风潮的兴起确实是近年来电商行业的一个显著趋势。短视频平台的崛起为直播电商开辟了新的商业蓝海&#xff0c;商家们也纷纷加入到直播带货的行列中。然而&#xff0c;对于许多商家来说&#xff0c;找到合适的主播并不容易&#xff0c;这给他们带来了一定的困扰。hhgg加…

API接口开发实现一键智能化自动抓取电商平台数据商品详情支持高并发免费接入示例

要开发一个API接口&#xff0c;用于自动抓取电商平台的商品详情数据&#xff0c;并支持高并发和免费接入&#xff0c;你需要考虑以下几个步骤&#xff1a; 确定目标电商平台和商品详情的数据结构。设计API接口规范&#xff0c;包括请求和响应格式。实现数据抓取逻辑&#xff0…

Ryght 在 Hugging Face 专家助力下赋能医疗保健和生命科学之旅

本文是 Ryght 团队的客座博文。 Ryght 是何方神圣&#xff1f; Ryght 的使命是构建一个专为医疗保健和生命科学领域量身定制的企业级生成式人工智能平台。最近&#xff0c;公司正式公开了 Ryght 预览版 平台。 Ryght 预览版https://www.ryght.ai/signup?utm_campaignPreview%2…

各种数据获取stream流的方式

1.单列集合&#xff08;直接调用&#xff09; ArrayList<Integer> list new ArrayList<>();list.stream(); 2.双列集合 HashMap<String, Integer> map new HashMap<>();map.put("aaa",111);map.put("bbb",222);map.put("c…