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进阶
通知类型
-
@Around:环绕通知,此注解标注的通知在目标方法前,后都被执行。
-
@Before: 前置通知,此注解标注的通知方法在目标方法前被执行。
-
@After: 后置通知,此注解标注的通知方法在目标方法被执行,无论是否异常都会执行。
-
@AfterReturning: 返回后通知,此注解标注的通知方法在,目标方法后被执行,有异常不执行。
-
@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:Aspect Oriented Programming(面向切面编程、面向方面编程),面向特定方法的编程。 动态代理是面向切面编程最主流的实现。 SpringAOP是Spring框架的高级技术,旨在管理bean对象的过程中,…...
Android SurfaceFlinger——GraphicBuffer的提交(三十三)
在 SurfaceFlinger 中,我们 dequeueBuffer 和 queueBuffer 是 Surface 控制接口中非常重要的两个函数,分别用于从 Surface 的 BufferQueue 中取出缓冲区和向 BufferQueue 提交(队列)缓冲区。这两个函数在生产者和消费者模型中扮演着核心角色,确保了图像数据的高效和有序传…...
创维汽车滁州永通体验中心开业仪式暨超充车型区域上市会圆满成功
2024年7月20日,创维汽车滁州永通体验中心盛大开业,当日,创维汽车市场部经理周世鹏、安徽大区总监王大明等领导参加本次开业盛典,共同见证创维汽车滁州永通体验中心成功落地。 2021年,新能源乘用车高速发展,…...
【PHP】系统的登录和注册
一、为什么要学习系统的登录和注册 系统的登录和注册可能存在多种漏洞,这些漏洞可能被恶意攻击者利用,从而对用户的安全和隐私构成威胁。通过学习系统的登录和注册理解整个登录和注册的逻辑方便后续更好站在开发的角度思考问题发现漏洞。以下是一些常见…...
2024.7.29 刷题总结
2024.7.29 **每日一题** 682.棒球比赛,这道题是一道简单的模拟题,用栈模拟题中的四个操作就可以了,操作一是将x加到列表末尾,操作二是将列表的后两项之和加到列表末尾,操作三是把列表最后一项的两倍加到列表末尾&#…...
WebSocket程序设计
协议说明 WebSocket 是一种在单个TCP连接上进行全双工通信的协议。WebSocket 使得客户端和服务器之间的数据交换变得更加简单,允许服务端主动向客户端推送数据。Websocket主要用在B/S架构的应用程序中,在 WebSocket API 中,浏览器和服务器只…...
ES(ElasticSearch)倒排索引
目录 正排与倒排索引 1.正排索引 作用: 优点: 缺点: 2.倒排索引 原理: 倒排索引的构建流程: 倒排索引的搜索流程: 优点: 缺点: 3. 应用场景 倒排索引中有几个非常重要的概念…...
Android Studio Build窗口出现中文乱码问题
刚安装成功的android studio软件打开工程,编译时下方build窗口中中文是乱码。 解决: 可点击studio状态栏的Help—>Edit Custom VM Options ,在打开的studio64.exe.vmoptions文件后面添加:(要注意不能有空格,否则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模型与自定义训练算法的无缝切换
动态定制深度学习:Mojo模型与自定义训练算法的无缝切换 引言 在机器学习领域,算法的选择对模型的性能有着决定性的影响。随着研究的深入和技术的发展,开发者可能需要根据不同的数据特性和业务需求,动态地切换或自定义训练算法。…...
昇思25天学习打卡营第19天|DCGAN生成漫画头像
DCGAN生成漫画头像总结 实验概述 本实验旨在利用深度卷积生成对抗网络(DCGAN)生成动漫头像,通过设置网络、优化器以及损失函数,使用MindSpore进行实现。 实验目的 学习和掌握DCGAN的基本原理和应用。熟悉使用MindSpore进行图像…...
排序题目:按照频率将数组升序排序
文章目录 题目标题和出处难度题目描述要求示例数据范围 解法思路和算法代码复杂度分析 题目 标题和出处 标题:按照频率将数组升序排序 出处:1636. 按照频率将数组升序排序 难度 3 级 题目描述 要求 给定一个整数数组 nums \texttt{nums} nums&a…...
实分析与测度论问题的分类
实分析主要研究实数、实数序列、实数极限以及实值函数的分析,而度量空间则是一个具有距离函数的集合,其分类可以从多个角度进行。 实分析 实分析主要关注实数、实数序列、实数极限以及实值函数的分析。它涉及到多个重要的概念和理论,包括但…...
动态代理更改Java方法的返回参数(可用于优化feign调用后R对象的统一处理)
动态代理更改Java方法的返回参数(可用于优化feign调用后R对象的统一处理) 需求原始解决方案优化后方案1.首先创建AfterInterface.java2.创建InvocationHandler处理代理方法3. 调用 实际运行场景拓展 需求 某些场景,调用别人的方法࿰…...
Redis缓存数据库进阶——Redis与分布式锁(6)
分布式锁简介 1. 什么是分布式锁 分布式锁是一种在分布式系统环境下,通过多个节点对共享资源进行访问控制的一种同步机制。它的主要目的是防止多个节点同时操作同一份数据,从而避免数据的不一致性。 线程锁: 也被称为互斥锁(Mu…...
网络芯片(又称为PHY网络芯片)
Realtek RTL8152B是一种常见的主板集成网络芯片(又称为PHY网络芯片)。PHY芯片是指将网络控制芯片的运算部分交由处理器或南桥芯片处理,以简化线路设计,从而降低成本。 https://www.realtek.com/Download/List?cate_id585 Realt…...
01 Go Web基础_20240728 课程笔记
概述 如果您没有Golang的基础,应该学习如下前置课程。 基础不好的同学每节课的代码最好配合视频进行阅读和学习,如果基础比较扎实,则阅读本教程巩固一下相关知识点即可,遇到不会的知识点再看视频。 视频课程 最近发现越来越多…...
嵌入式学习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,useCallBack的,这是两个优化性能的方法,那么useMemo,useCallBack到底是什么呢?什么时候用呢? 下面将给打击分享相关知识,希望对大家有所帮助同时欢迎讨论指…...
Python爬虫实战:研究MechanicalSoup库相关技术
一、MechanicalSoup 库概述 1.1 库简介 MechanicalSoup 是一个 Python 库,专为自动化交互网站而设计。它结合了 requests 的 HTTP 请求能力和 BeautifulSoup 的 HTML 解析能力,提供了直观的 API,让我们可以像人类用户一样浏览网页、填写表单和提交请求。 1.2 主要功能特点…...
LBE-LEX系列工业语音播放器|预警播报器|喇叭蜂鸣器的上位机配置操作说明
LBE-LEX系列工业语音播放器|预警播报器|喇叭蜂鸣器专为工业环境精心打造,完美适配AGV和无人叉车。同时,集成以太网与语音合成技术,为各类高级系统(如MES、调度系统、库位管理、立库等)提供高效便捷的语音交互体验。 L…...
测试微信模版消息推送
进入“开发接口管理”--“公众平台测试账号”,无需申请公众账号、可在测试账号中体验并测试微信公众平台所有高级接口。 获取access_token: 自定义模版消息: 关注测试号:扫二维码关注测试号。 发送模版消息: import requests da…...
应用升级/灾备测试时使用guarantee 闪回点迅速回退
1.场景 应用要升级,当升级失败时,数据库回退到升级前. 要测试系统,测试完成后,数据库要回退到测试前。 相对于RMAN恢复需要很长时间, 数据库闪回只需要几分钟。 2.技术实现 数据库设置 2个db_recovery参数 创建guarantee闪回点,不需要开启数据库闪回。…...
Spring Boot 实现流式响应(兼容 2.7.x)
在实际开发中,我们可能会遇到一些流式数据处理的场景,比如接收来自上游接口的 Server-Sent Events(SSE) 或 流式 JSON 内容,并将其原样中转给前端页面或客户端。这种情况下,传统的 RestTemplate 缓存机制会…...
转转集团旗下首家二手多品类循环仓店“超级转转”开业
6月9日,国内领先的循环经济企业转转集团旗下首家二手多品类循环仓店“超级转转”正式开业。 转转集团创始人兼CEO黄炜、转转循环时尚发起人朱珠、转转集团COO兼红布林CEO胡伟琨、王府井集团副总裁祝捷等出席了开业剪彩仪式。 据「TMT星球」了解,“超级…...
(二)原型模式
原型的功能是将一个已经存在的对象作为源目标,其余对象都是通过这个源目标创建。发挥复制的作用就是原型模式的核心思想。 一、源型模式的定义 原型模式是指第二次创建对象可以通过复制已经存在的原型对象来实现,忽略对象创建过程中的其它细节。 📌 核心特点: 避免重复初…...
爬虫基础学习day2
# 爬虫设计领域 工商:企查查、天眼查短视频:抖音、快手、西瓜 ---> 飞瓜电商:京东、淘宝、聚美优品、亚马逊 ---> 分析店铺经营决策标题、排名航空:抓取所有航空公司价格 ---> 去哪儿自媒体:采集自媒体数据进…...
【SSH疑难排查】轻松解决新版OpenSSH连接旧服务器的“no matching...“系列算法协商失败问题
【SSH疑难排查】轻松解决新版OpenSSH连接旧服务器的"no matching..."系列算法协商失败问题 摘要: 近期,在使用较新版本的OpenSSH客户端连接老旧SSH服务器时,会遇到 "no matching key exchange method found", "n…...
DeepSeek越强,Kimi越慌?
被DeepSeek吊打的Kimi,还有多少人在用? 去年,月之暗面创始人杨植麟别提有多风光了。90后清华学霸,国产大模型六小虎之一,手握十几亿美金的融资。旗下的AI助手Kimi烧钱如流水,单月光是投流就花费2个亿。 疯…...
