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;里面存储的是元素本…

Linux Make命令详解

1 概述 make命令常用参数-C,-n, -j.其实make还有很多参数也很有用&#xff0c;本文描述将简单介绍。 使用make版本: $ make --version GNU Make 4.2.1 Built for x86_64-pc-linux-gnu Copyright (C) 1988-2016 Free Software Foundation, Inc. License GPLv3: GNU GPL versio…

八股kafka(一)

目录 1、面试官&#xff1a;Kafka是如何保证消息不丢失 2、面试官&#xff1a;Kafka中消息的重复消费问题如何解决的 3、面试官&#xff1a;Kafka是如何保证消费的顺序性 4、面试官&#xff1a;Kafka的高可用机制有了解过嘛 5、面试官&#xff1a;解释一下复制机制中的ISR 6、面…

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

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

人工智能对企业安全的影响与风险控制-内刊

题目&#xff1a;人工智能对企业安全的影响与风险控制 大纲&#xff1a; I. 引言 A. 人工智能的发展背景 B. 企业安全的重要性 C. 研究目的与意义 II. 人工智能对企业安全的影响 A. 人工智能对企业安全管理的优势 提高安全管理效率预测与防范潜在风险定制化安全策略 B. 人工…

好用的电商数据API接口分享(京东|淘宝天猫|1688商品详情数据API)

电商API接口主要用于帮助开发者将电商功能集成到自己的应用程序中&#xff0c;实现诸如商品检索、商品 价格数据获取、订单处理、支付、物流跟踪等功能。以下是一些常用的电商API接口提供商: 主流电商平台API&#xff1a; 淘宝开放平台&#xff1a;提供淘宝、天猫、1688等阿里…

低代码技术赋能未来乡村建设:创新与实践

引言 随着我国新型城镇化进程的推进&#xff0c;乡村建设正面临着前所未有的挑战。如何在有限的人力、物力、财力资源下&#xff0c;高效推动乡村建设&#xff0c;实现城乡一体化发展&#xff0c;成为当下亟待解决的问题。低代码技术作为一种创新性的解决方案&#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框…

了解 macOS 中的系统完整性保护 (SIP):开启与关闭

在 macOS 系统中&#xff0c;有一个名为系统完整性保护 (System Integrity Protection&#xff0c;SIP) 的重要功能。SIP 旨在保护系统文件和进程免受未经授权的访问和修改&#xff0c;从而提高系统的安全性和稳定性。然而&#xff0c;在某些情况下&#xff0c;用户可能需要临时…

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

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

Spring线程池配置

配置Spring线程池,特别是ThreadPoolTaskExecutor,通常涉及设置一些关键参数以控制线程池的行为和性能。以下是一些基本的配置步骤: 定义配置类 首先,需要创建一个配置类,使用@Configuration注解标记,并启用异步执行功能,使用@EnableAsync注解。 @Configuration @Enab…

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…