当前位置: 首页 > news >正文

AOP~面向切面编程介绍

AOP基础

概述

  • AOP:Aspect Oriented Programming(面向切面编程、面向方面编程),面向特定方法的编程。

  • 动态代理是面向切面编程最主流的实现。

  • SpringAOP是Spring框架的高级技术,旨在管理bean对象的过程中,主要通过底层的动态代理机制,对特定的方法进行编程。

入门程序

  • 需求

    • 统计各个业务层方法的执行耗时

  • 步骤

    • 导入依赖

    • <!--        AOP依赖-->
      <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-aop</artifactId>
      </dependency>
    • 编写代码

    • package com.testpeople.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;@Component
      @Slf4j
      @Aspect //AOP类
      public class TimeAspect {@Around("execution(* com.testpeople.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;}}
    • 效果

  


优势

  • 场景

    • 记录日志

    • 权限控制

    • 事务管理

  • 优势

    • 代码无侵入

    • 减少重复代码

    • 提高开发效率

    • 维护方便

核心概念

  • 连接点

    • JoinPoint,可以被AOP控制的方法(暗含方法执行时的相关信息)

  • 通知

    • Advice,指哪些重复的逻辑,也就是共性功能(体现为一个方法)

  • 切入点

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

  • 切面

    • Aspect,描述通知与切入点的对应关系(通知+切入点)

  • 目标对象

          Target,通知所应用的对象

图解

流程

  • 进行开发后,使用的是代理对象,而不是实际对象。

AOP进阶

通知类型

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

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

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

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

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

测试

package com.testpeople.aop;import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;@Component
@Slf4j
@Aspect
public class MyAspect {@Before("execution(* com.testpeople.service.impl.DeptServiceImpl.*(..))")public void before(){log.info("前置通知");}@Around("execution(* com.testpeople.service.impl.DeptServiceImpl.*(..))")public Object around(ProceedingJoinPoint joinPoint) throws Throwable {log.info("环绕通知"+"before");Object result = joinPoint.proceed();log.info("环绕通知"+"after");return result;}@After("execution(* com.testpeople.service.impl.DeptServiceImpl.*(..))")public void after(){log.info("后置通知");}@AfterReturning("execution(* com.testpeople.service.impl.DeptServiceImpl.*(..))")public void afterReturning(){log.info("后置返回通知");}@AfterThrowing("execution(* com.testpeople.service.impl.DeptServiceImpl.*(..))")public  void afterThrowing(){log.info("后置异常通知");}}

效果

注意

  • @Around:环绕通知需要自己调用 ProceedingJoinPoint.proceed();

  • @Around:环绕通知方法的返回值,必须指定为Object,来接受原始方法的返回值。

tips

  • 切入点表达式抽取

package com.testpeople.aop;import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;@Component
@Slf4j
@Aspect
public class MyAspect {@Pointcut("execution(* com.testpeople.service.impl.DeptServiceImpl.*(..))")private void pt(){}//这个方法可以改修订范围,然后被别的包引用。@Before("pt()")public void before(){log.info("前置通知");}@Around("pt()")public Object around(ProceedingJoinPoint joinPoint) throws Throwable {log.info("环绕通知"+"before");Object result = joinPoint.proceed();log.info("环绕通知"+"after");return result;}@After("pt()")public void after(){log.info("后置通知");}@AfterReturning("pt()")public void afterReturning(){log.info("后置返回通知");}@AfterThrowing("pt()")public  void afterThrowing(){log.info("后置异常通知");}}

通知顺序

  • 不同切面类中,默认按照切面类的类名字母排序

    • 目标方法前的通知方法:字母排名靠前的先执行。

    • 目标方法后的通知方法:字母排名靠前的后执行。

  • 用@Order(数字)加在切面类上来控制顺序

    • 目标方法前的通知方法:数字小的先执行

    • 目标方法后的通知方法:数字小的后执行

  • 概述

    • 描述切入点方法的一种表达式

  • 作用

    • 主要用来决定项目中的哪些方法需要加入通知

  • 常见形式

    • execution(...);根据方法的签名来匹配

  • 特殊符号

  • 如果匹配两个方法,可以使用“||”连接。

  • Tips

  • @annotation(...);根据注解匹配

    • 新建注解

    • package com.testpeople.aop;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 {}
    • 在需要添加方法上添加注解,更换切入点表达式。

    • @Around("@annotation(com.testpeople.aop.MyLog)")

连接点

  • 开发

  • @Around("@annotation(com.testpeople.aop.MyLog)")
    public Object testJoinPoint(ProceedingJoinPoint joinPoint) throws Throwable {//1.获取 目标对象的类名String className = joinPoint.getTarget().getClass().getName();log.info(className);//2.获取 目标方法的方法名String methodName = joinPoint.getSignature().getName();log.info(methodName);//3.获取 目标方法运行时传入的参数Object[] methodArgs = joinPoint.getArgs();log.info(Arrays.toString(methodArgs));//4.放行 目标方法执行Object result = joinPoint.proceed();//5.获取 目标方法的返回值log.info(result.toString());return result;//此处可以改变函数的返回结果(添加其他方法)
    }

效果

AOP案例(操作日志记录功能)

需求

  • 将案例中 增、删、改相关的接口的操作日志记录到数据库表中

  • 日志信息~操作人、操作时间、执行方法的全类名、执行方法、方法运行时的参数、返回值、方法执行的时长。

思路

步骤

  • 准备

    • 在案例中引入AOP的依赖

    • <!--        AOP依赖--><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-aop</artifactId>
      </dependency>
    • 设计好数据库表

    • # 操作日志记录表
      create table operate_log(id int unique primary key auto_increment comment 'ID',operate_user int unsigned comment '操作人ID',operate_time datetime comment '操作时间',class_name varchar(100) comment '操作类名',method_name varchar(100) comment '操作方法名',method_params varchar(1000) comment '操作方法参数',return_value varchar(2000) comment '返回值',cost_time bigint comment '耗时,单位:ms'
      ) comment '操作日志记录表';
    • 设计好实体类

    • package com.testpeople.pojo;import lombok.AllArgsConstructor;
      import lombok.Data;
      import lombok.NoArgsConstructor;import java.time.LocalDateTime;@Data
      @AllArgsConstructor
      @NoArgsConstructor
      public class OperateLog {private Integer id;//IDprivate Integer operateUser;//操作人IDprivate LocalDateTime operateTime;//操作时间private String className;//操作类名private String methodName;//方法名private String methodParams;//方法参数private String returnValue;//返回值private Long costTime;//耗时}
    • Mapper接口

    • package com.testpeople.mapper;import com.testpeople.pojo.OperateLog;
      import org.apache.ibatis.annotations.Delete;
      import org.apache.ibatis.annotations.Insert;
      import org.apache.ibatis.annotations.Mapper;@Mapper
      public interface OperateLogMapper {//插日志数据@Insert("insert into operate_log (operate_user,operate_time,class_name,method_name,method_params,return_value,cost_time) " +"values(#{operateUser},#{operateTime},#{className},#{methodName},#{methodParams},#{returnValue},#{costTime})")void insert(OperateLog operateLog);}
    • 编码

      • 自定义注解

      • package com.testpeople.anno;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 Log {
        }
      • 定义切面类,完成记录操作日志的逻辑

      • package com.testpeople.aop;import com.alibaba.fastjson.JSONObject;
        import com.testpeople.mapper.OperateLogMapper;
        import com.testpeople.pojo.OperateLog;
        import com.testpeople.utils.JwtUtils;
        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.beans.factory.annotation.Autowired;
        import org.springframework.stereotype.Component;import javax.servlet.http.HttpServletRequest;
        import java.time.LocalDateTime;
        import java.util.Arrays;@Slf4j
        @Component
        @Aspect
        public class LogAspct {@Autowiredprivate HttpServletRequest request;@Autowiredprivate OperateLogMapper operateLogMapper;@Around("@annotation(com.testpeople.anno.Log)")public Object recordLog(ProceedingJoinPoint joinPoint) throws Throwable {//操作人ID~ 当前员工ID//获取请求头中的jwt令牌中的员工IDString jwt = request.getHeader("token");//解析Integer operateUser = (Integer) JwtUtils.parseJwt(jwt).get("id");//操作时间LocalDateTime operateTime = LocalDateTime.now();//类名String className = joinPoint.getTarget().getClass().getName();//方法名String methodName = joinPoint.getSignature().getName();//方法参数Object[] args = joinPoint.getArgs();String methodParams = Arrays.toString(args);//记录时间long begin = System.currentTimeMillis();//调用原始目标方法运行Object result = joinPoint.proceed();//记录结束时间long end = System.currentTimeMillis();//方法返回值String returnValue = JSONObject.toJSONString(result);//耗时long costTime = end - begin;//记录操作日志OperateLog operateLog = new OperateLog();operateLog.setOperateUser(operateUser);operateLog.setOperateTime(operateTime);operateLog.setClassName(className);operateLog.setMethodName(methodName);operateLog.setMethodParams(methodParams);operateLog.setReturnValue(returnValue);operateLog.setCostTime(costTime);operateLogMapper.insert(operateLog);log.info("AOP记录操作日志 {}",operateLog.toString()+"\n");return result;}}
      • 给有需求的类添加@Log注解

效果

注意

  • 获取当前用户

    • 从request中获取token 在token中提取当前用户id


以上是对SpringBoot框架中的AOP相关的介绍以及简单的使用,通过一个操作记录日志功能进行练习。多点关注、多点爱。(以上知识点笔记来自于小编学习黑马程序员的课程所记录)

项目地址

admin_web_project: 黑马程序员项目javaWebjavaWeb开发学习仓库,前后端分离项目前端Vue后端springboot数据库Mysql

相关文章:

AOP~面向切面编程介绍

AOP基础 概述 AOP&#xff1a;Aspect Oriented Programming&#xff08;面向切面编程、面向方面编程&#xff09;&#xff0c;面向特定方法的编程。 动态代理是面向切面编程最主流的实现。 SpringAOP是Spring框架的高级技术&#xff0c;旨在管理bean对象的过程中&#xff0c…...

Android SurfaceFlinger——GraphicBuffer的提交(三十三)

在 SurfaceFlinger 中,我们 dequeueBuffer 和 queueBuffer 是 Surface 控制接口中非常重要的两个函数,分别用于从 Surface 的 BufferQueue 中取出缓冲区和向 BufferQueue 提交(队列)缓冲区。这两个函数在生产者和消费者模型中扮演着核心角色,确保了图像数据的高效和有序传…...

创维汽车滁州永通体验中心开业仪式暨超充车型区域上市会圆满成功

2024年7月20日&#xff0c;创维汽车滁州永通体验中心盛大开业&#xff0c;当日&#xff0c;创维汽车市场部经理周世鹏、安徽大区总监王大明等领导参加本次开业盛典&#xff0c;共同见证创维汽车滁州永通体验中心成功落地。 2021年&#xff0c;新能源乘用车高速发展&#xff0c;…...

【PHP】系统的登录和注册

一、为什么要学习系统的登录和注册 系统的登录和注册可能存在多种漏洞&#xff0c;这些漏洞可能被恶意攻击者利用&#xff0c;从而对用户的安全和隐私构成威胁。通过学习系统的登录和注册理解整个登录和注册的逻辑方便后续更好站在开发的角度思考问题发现漏洞。以下是一些常见…...

2024.7.29 刷题总结

2024.7.29 **每日一题** 682.棒球比赛&#xff0c;这道题是一道简单的模拟题&#xff0c;用栈模拟题中的四个操作就可以了&#xff0c;操作一是将x加到列表末尾&#xff0c;操作二是将列表的后两项之和加到列表末尾&#xff0c;操作三是把列表最后一项的两倍加到列表末尾&#…...

WebSocket程序设计

协议说明 WebSocket 是一种在单个TCP连接上进行全双工通信的协议。WebSocket 使得客户端和服务器之间的数据交换变得更加简单&#xff0c;允许服务端主动向客户端推送数据。Websocket主要用在B/S架构的应用程序中&#xff0c;在 WebSocket API 中&#xff0c;浏览器和服务器只…...

ES(ElasticSearch)倒排索引

目录 正排与倒排索引 1.正排索引 作用&#xff1a; 优点&#xff1a; 缺点&#xff1a; 2.倒排索引 原理&#xff1a; 倒排索引的构建流程&#xff1a; 倒排索引的搜索流程&#xff1a; 优点&#xff1a; 缺点&#xff1a; 3. 应用场景 倒排索引中有几个非常重要的概念…...

Android Studio Build窗口出现中文乱码问题

刚安装成功的android studio软件打开工程&#xff0c;编译时下方build窗口中中文是乱码。 解决&#xff1a; 可点击studio状态栏的Help—>Edit Custom VM Options &#xff0c;在打开的studio64.exe.vmoptions文件后面添加&#xff1a;(要注意不能有空格&#xff0c;否则st…...

java生成随机数

代码 startValue 开始值 endValue 结束值 per生成的位数也就是精度 /*** 随机数的生成* param startValue* param endValue* return*/private BigDecimal randomBigDecimal(String startValue, String endValue,int per) {BigDecimal min new BigDecimal(startValue);BigDeci…...

动态定制深度学习:Mojo模型与自定义训练算法的无缝切换

动态定制深度学习&#xff1a;Mojo模型与自定义训练算法的无缝切换 引言 在机器学习领域&#xff0c;算法的选择对模型的性能有着决定性的影响。随着研究的深入和技术的发展&#xff0c;开发者可能需要根据不同的数据特性和业务需求&#xff0c;动态地切换或自定义训练算法。…...

昇思25天学习打卡营第19天|DCGAN生成漫画头像

DCGAN生成漫画头像总结 实验概述 本实验旨在利用深度卷积生成对抗网络&#xff08;DCGAN&#xff09;生成动漫头像&#xff0c;通过设置网络、优化器以及损失函数&#xff0c;使用MindSpore进行实现。 实验目的 学习和掌握DCGAN的基本原理和应用。熟悉使用MindSpore进行图像…...

排序题目:按照频率将数组升序排序

文章目录 题目标题和出处难度题目描述要求示例数据范围 解法思路和算法代码复杂度分析 题目 标题和出处 标题&#xff1a;按照频率将数组升序排序 出处&#xff1a;1636. 按照频率将数组升序排序 难度 3 级 题目描述 要求 给定一个整数数组 nums \texttt{nums} nums&a…...

实分析与测度论问题的分类

实分析主要研究实数、实数序列、实数极限以及实值函数的分析&#xff0c;而度量空间则是一个具有距离函数的集合&#xff0c;其分类可以从多个角度进行。 实分析 实分析主要关注实数、实数序列、实数极限以及实值函数的分析。它涉及到多个重要的概念和理论&#xff0c;包括但…...

动态代理更改Java方法的返回参数(可用于优化feign调用后R对象的统一处理)

动态代理更改Java方法的返回参数&#xff08;可用于优化feign调用后R对象的统一处理&#xff09; 需求原始解决方案优化后方案1.首先创建AfterInterface.java2.创建InvocationHandler处理代理方法3. 调用 实际运行场景拓展 需求 某些场景&#xff0c;调用别人的方法&#xff0…...

Redis缓存数据库进阶——Redis与分布式锁(6)

分布式锁简介 1. 什么是分布式锁 分布式锁是一种在分布式系统环境下&#xff0c;通过多个节点对共享资源进行访问控制的一种同步机制。它的主要目的是防止多个节点同时操作同一份数据&#xff0c;从而避免数据的不一致性。 线程锁&#xff1a; 也被称为互斥锁&#xff08;Mu…...

网络芯片(又称为PHY网络芯片)

Realtek RTL8152B是一种常见的主板集成网络芯片&#xff08;又称为PHY网络芯片&#xff09;。PHY芯片是指将网络控制芯片的运算部分交由处理器或南桥芯片处理&#xff0c;以简化线路设计&#xff0c;从而降低成本。 https://www.realtek.com/Download/List?cate_id585 Realt…...

01 Go Web基础_20240728 课程笔记

概述 如果您没有Golang的基础&#xff0c;应该学习如下前置课程。 基础不好的同学每节课的代码最好配合视频进行阅读和学习&#xff0c;如果基础比较扎实&#xff0c;则阅读本教程巩固一下相关知识点即可&#xff0c;遇到不会的知识点再看视频。 视频课程 最近发现越来越多…...

嵌入式学习Day12---C语言提升

目录 一、指针数组 1.1.什么是指针数组 2.2. 格式 2.3.存储 2.4.与字符型二维数组相比 2.5.什么时候使用指针数组 2.6.练习 二、数组指针 2.1.什么是数组指针 2.2.格式 2.3.一维数组 2.3.特点 2.4.什么时候使用 三、指针和数组的关系 3.1.一维数组和指针 …...

6.6 使用dashboard商城搜索导入模板

本节重点介绍 : 模板商城中搜索模板导入模板修改模板 大盘模板商城地址 免费的 地址 https://grafana.com/grafana/dashboards 搜索模板技巧 详情 导入dashboard 两种导入模式 url导入id导入json文件导入 导入 node_exporter模板 https://grafana.com/grafana/dashboa…...

一文讲透useMemo和useCallback

在React项目中是经常会使用到useMemo&#xff0c;useCallBack的&#xff0c;这是两个优化性能的方法&#xff0c;那么useMemo&#xff0c;useCallBack到底是什么呢&#xff1f;什么时候用呢&#xff1f; 下面将给打击分享相关知识&#xff0c;希望对大家有所帮助同时欢迎讨论指…...

使用docker在3台服务器上搭建基于redis 6.x的一主两从三台均是哨兵模式

一、环境及版本说明 如果服务器已经安装了docker,则忽略此步骤,如果没有安装,则可以按照一下方式安装: 1. 在线安装(有互联网环境): 请看我这篇文章 传送阵>> 点我查看 2. 离线安装(内网环境):请看我这篇文章 传送阵>> 点我查看 说明&#xff1a;假设每台服务器已…...

SkyWalking 10.2.0 SWCK 配置过程

SkyWalking 10.2.0 & SWCK 配置过程 skywalking oap-server & ui 使用Docker安装在K8S集群以外&#xff0c;K8S集群中的微服务使用initContainer按命名空间将skywalking-java-agent注入到业务容器中。 SWCK有整套的解决方案&#xff0c;全安装在K8S群集中。 具体可参…...

在QWebEngineView上实现鼠标、触摸等事件捕获的解决方案

这个问题我看其他博主也写了&#xff0c;要么要会员、要么写的乱七八糟。这里我整理一下&#xff0c;把问题说清楚并且给出代码&#xff0c;拿去用就行&#xff0c;照着葫芦画瓢。 问题 在继承QWebEngineView后&#xff0c;重写mousePressEvent或event函数无法捕获鼠标按下事…...

Linux系统部署KES

1、安装准备 1.版本说明V008R006C009B0014 V008&#xff1a;是version产品的大版本。 R006&#xff1a;是release产品特性版本。 C009&#xff1a;是通用版 B0014&#xff1a;是build开发过程中的构建版本2.硬件要求 #安全版和企业版 内存&#xff1a;1GB 以上 硬盘&#xf…...

深度学习之模型压缩三驾马车:模型剪枝、模型量化、知识蒸馏

一、引言 在深度学习中&#xff0c;我们训练出的神经网络往往非常庞大&#xff08;比如像 ResNet、YOLOv8、Vision Transformer&#xff09;&#xff0c;虽然精度很高&#xff0c;但“太重”了&#xff0c;运行起来很慢&#xff0c;占用内存大&#xff0c;不适合部署到手机、摄…...

ZYNQ学习记录FPGA(一)ZYNQ简介

一、知识准备 1.一些术语,缩写和概念&#xff1a; 1&#xff09;ZYNQ全称&#xff1a;ZYNQ7000 All Pgrammable SoC 2&#xff09;SoC:system on chips(片上系统)&#xff0c;对比集成电路的SoB&#xff08;system on board&#xff09; 3&#xff09;ARM&#xff1a;处理器…...

Matlab实现任意伪彩色图像可视化显示

Matlab实现任意伪彩色图像可视化显示 1、灰度原始图像2、RGB彩色原始图像 在科研研究中&#xff0c;如何展示好看的实验结果图像非常重要&#xff01;&#xff01;&#xff01; 1、灰度原始图像 灰度图像每个像素点只有一个数值&#xff0c;代表该点的​​亮度&#xff08;或…...

EEG-fNIRS联合成像在跨频率耦合研究中的创新应用

摘要 神经影像技术对医学科学产生了深远的影响&#xff0c;推动了许多神经系统疾病研究的进展并改善了其诊断方法。在此背景下&#xff0c;基于神经血管耦合现象的多模态神经影像方法&#xff0c;通过融合各自优势来提供有关大脑皮层神经活动的互补信息。在这里&#xff0c;本研…...

免费批量Markdown转Word工具

免费批量Markdown转Word工具 一款简单易用的批量Markdown文档转换工具&#xff0c;支持将多个Markdown文件一键转换为Word文档。完全免费&#xff0c;无需安装&#xff0c;解压即用&#xff01; 官方网站 访问官方展示页面了解更多信息&#xff1a;http://mutou888.com/pro…...

若依项目部署--传统架构--未完待续

若依项目介绍 项目源码获取 #Git工具下载 dnf -y install git #若依项目获取 git clone https://gitee.com/y_project/RuoYi-Vue.git项目背景 随着企业信息化需求的增加&#xff0c;传统开发模式存在效率低&#xff0c;重复劳动多等问题。若依项目通过整合主流技术框架&…...