Java AOP 简单实例演示

天行健,君子以自强不息;地势坤,君子以厚德载物。


每个人都有惰性,但不断学习是好好生活的根本,共勉!


文章均为学习整理笔记,分享记录为主,如有错误请指正,共同学习进步。

文章目录

  • 一、介绍
  • 二、如何使用AOP
    • 1. 标注类为切面类
    • 2. 定义切入点函数列表
    • 3. 定义签名函数
    • 4. 增强方法
    • 5. aop全局注解
  • 三、代码实现
    • 1. 包结构
    • 2. 依赖
    • 3. 项目启动类
    • 4. 请求类
    • 5. 切面类
  • 四、执行AOP
    • 1. 启动程序
    • 2. postman访问
    • 3. 控制台输出


SpringBoot搭建参考文章:SpringBoot的搭建(两种方式)

一、介绍

  • AOP
    aspect oriented programming
    面向切面编程

  • 作用
    在不修改原有代码的基础上,进行代码功能增强

  • 描述
    简而言之就是不动原有的代码,使用aop之后可以对原有功能进行扩展
    如在原有代码执行前执行后进行一些操作的设定
    如在原有代码执行后返回结果触发操作
    如在原有代码执行报错触发操作
    等等

二、如何使用AOP

1. 标注类为切面类

在需要使用aop的类上使用@Aspect注解标注,标注后该类会被默认为切面类

2. 定义切入点函数列表

在切面类中使用@Pointcut注解列出需要增强的方法(即原有代码的函数,我们就叫它们目标函数
语法:execution(修饰符 返回值 包名.包名.包名.类名.方法名(…))

@Pointcu("execution(public void com.u.u.test.aop_test.AopController.test(..))")

也可用*替代进行模糊匹配

@Pointcu("execution(public * com..aop_test.*Controller.*(..))")

@Pointcu("execution(public * com..*Controller.*(..))")

@Pointcu("execution(* com..*Controller.*(..))")

3. 定义签名函数

并在@Pointcut注解下面定义一个没有内容的切入点签名函数(我们将该函数定义为sig()

4. 增强方法

在新的方法中定义目标函数的增强内容,并在新的方法上使用注解来指定触发的时机(目标函数执行前、后、返回结果后等)

5. aop全局注解

常用的全局注解有:

  • 前置通知@Before
    在被切入的目标方法执行前执行
  • 后置通知@After
    在被切入的目标方法执行前执行
  • 环绕通知@Around
    在被切入的目标方法执行前及执行后分别执行
  • 返回通知@AfterReturning
    在被切入的目标方法执行并成功返回结果后执行

三、代码实现

1. 包结构

基于springboot项目框架,创建一个请求类,一个aop类
在这里插入图片描述

2. 依赖

基于springboot进行aop的实现
需要用到依赖如下:

    <dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId><version>2.7.3</version></dependency><!--json 工具--><dependency><groupId>com.alibaba.fastjson2</groupId><artifactId>fastjson2</artifactId><version>2.0.33</version></dependency><!--项目注解工具--><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><version>1.18.24</version></dependency><!--aop--><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-aop</artifactId><version>2.7.3</version></dependency></dependencies>

注意:本篇使用2.7.3版本依赖,最新版本可能会报错,请谨慎使用最新版(我用3.2.3报错了)

3. 项目启动类

Application.java

package com.u.u;import org.aspectj.lang.annotation.Aspect;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;/*** @ClassDescription:* @JdkVersion: 1.8* @Author: 李白* @Created: 2024/3/18 14:12*/
@Aspect
@SpringBootApplication
public class Application {public static void main(String[] args) {SpringApplication.run(Application.class, args);}
}

4. 请求类

AopController.java

package com.u.u.test.aop_test;import lombok.extern.slf4j.Slf4j;
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.*;/*** @ClassDescription:* @JdkVersion: 1.8* @Author: 李白* @Created: 2024/3/19 14:00*/
@Slf4j
@CrossOrigin
@RestController
@RequestMapping(value = "/aop", method = RequestMethod.POST, produces = MediaType.APPLICATION_JSON_VALUE)
public class AopController {@PostMapping(value = "/test")public Object test(@RequestHeader("token")String token){System.out.println("aop test");return "aop test";}@RequestMapping("/test2")public Object test2(@RequestHeader("token")String token){System.out.println("aop test2");return "aop test2";}}

5. 切面类

AspectExecution.java

package com.u.u.test.aop_test;import com.alibaba.fastjson2.JSON;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import java.util.Arrays;/*** @ClassDescription: 切面类* @JdkVersion: 1.8* @Author: 李白* @Created: 2024/3/19 12:49*/
@Component //将当前类注入到spring容器中
@Slf4j //日志注解
@Aspect //注明该类是切面类
//@Order(2) //如同时又多个aop类,可在类上添加此注解,括号中的值越小,越先执行,如两个aop类中都用了@Before注解那么值为1的@Before先执行
public class AspectExecution {//注解@Order(3)表示,当有多个切面可以使用时,通过@Order注解指定顺序,数值越小越先执行//切入点:这里定义待增强的方法(即需要做统一处理的方法),可以是具体的方法,也可以是模糊定义的一类方法//包名中间可以省略,用".."表示,类名可以用"*Controller"模糊定义类名,方法名同理//这里execution定义的函数就是接下来需要执行全局操作的函数列表@Pointcut("execution(public Object com.u.u.test.aop_test.AopController.test(..))||execution(public Object com.u.u.test.aop_test.AopController.test2(..))")//将以Controller结尾的类名中的所有方法执行切入操作
//    @Pointcut("execution(public Object com..aop_test.*Controller.*(..))")@Pointcut("execution(public * com..*Controller.*(..))")//切入点签名public void sig(){
//        System.out.println("PointCut签名---》");}@Around("sig()") //环绕通知,参数必须为ProceedingJoinPointpublic Object doAround(ProceedingJoinPoint pJoinPoint){System.out.println("round begin");Object obj = null;try {obj = pJoinPoint.proceed();} catch (Throwable e) {throw new RuntimeException(e);}System.out.println("round end");return obj;}/*** 前置通知* @param joinPoint j*/@Before("sig()") //前置通知,在被切入方法执行之前执行public void doBefore(JoinPoint joinPoint){log.info("=====Before=====");//接收到请求,记录请求内容ServletRequestAttributes srAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();assert srAttributes != null;HttpServletRequest hsRequest = srAttributes.getRequest();//获取tokenString token = hsRequest.getHeader("token");System.out.println("token: "+token);Cookie[] cookies = hsRequest.getCookies();System.out.println("cookies: " + Arrays.toString(cookies));//记录请求内容log.info("URL:[{}]", hsRequest.getRequestURL().toString());log.info("HTTP_METHOD:[{}]", hsRequest.getMethod());log.info("IP:[{}]", hsRequest.getRemoteAddr());log.info("CLASS_METHOD:[{}]", joinPoint.getSignature().getDeclaringTypeName() + "." + joinPoint.getSignature().getName());log.info("CLASS_METHOD:[{}]", joinPoint);
//        log.info("ARGS:\r\n{}", JSON.toJSONString(joinPoint.getArgs(), String.valueOf(true)));log.info("ARGS:[{}[", JSON.toJSONString(joinPoint.getArgs(), String.valueOf(true)));
//        log.info("ARGS:\r\n{}", Arrays.toString(joinPoint.getArgs()));log.info("ARGS:[{}]", Arrays.toString(joinPoint.getArgs()));}/*** 返回通知* @param returnMsg r*/@AfterReturning(returning = "returnMsg", pointcut = "sig()")public void doAfterReturning(Object returnMsg){log.info("======AfterReturning=====");System.out.println("返回通知:" + returnMsg);}/*** 异常通知* @param joinPoint j* @param exception e*/@AfterThrowing(throwing = "exception", pointcut = "sig()") //在被切入的方法抛出异常后执行public void doThrows(JoinPoint joinPoint, Exception exception){log.info("======AfterThrowing=====");System.out.println("异常通知:方法异常时执行");System.out.println("产生异常的方法:" + joinPoint);System.out.println("异常种类:" + exception);}/*** 后置通知* @param joinPoint j*/@After("sig()") //后置通知,在被切入方法执行之后执行,最后执行,并且一定会执行public void doAfter(JoinPoint joinPoint){log.info("======After=====");//接收到请求,记录请求内容ServletRequestAttributes srAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();assert srAttributes != null;HttpServletRequest hsRequest = srAttributes.getRequest();//记录请求内容log.info("URL:[{}]", hsRequest.getRequestURL().toString());log.info("HTTP_METHOD:[{}]", hsRequest.getMethod());log.info("IP:[{}]", hsRequest.getRemoteAddr());log.info("CLASS_METHOD:[{}]", joinPoint.getSignature().getDeclaringTypeName() + "." + joinPoint.getSignature().getName());log.info("ARGS:[{}]", JSON.toJSONString(joinPoint.getArgs(), String.valueOf(true)));}}

四、执行AOP

1. 启动程序

在这里插入图片描述

2. postman访问

在这里插入图片描述

3. 控制台输出

在这里插入图片描述
如图输出顺序依次是:
环绕通知开始
前置通知
被切入方法的输出内容
返回通知
后置通知
环绕通知结束


感谢阅读,祝君暴富!

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

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

相关文章

WT32-ETH02 plus 串口转以太网开发,WT32-ETH01网关开发板升级款!

广受欢迎的WT32-ETH01网关开发板迎来了升级。 就是这款启明云端新推出的嵌入式串口转以太网开发板——WT32-ETH02 plus。应广大客户的需求&#xff0c;在WT32-ETH01的基础上增加了POE供电&#xff0c;可广泛应用于智能家居和网关等应用。开发板搭载2.4GHz Wi-Fi和蓝牙双模的SO…

一键部署灵境矩阵,属于自己的ai智能平台。

灵境矩阵 | 想象即现实 “灵境杯”智能体创意大赛&#xff0c;瓜分百万超级奖励 打造专属AI智能平台&#xff1a;一键部署灵境矩阵的无限可能 在数字化浪潮席卷全球的今天&#xff0c;人工智能技术已逐渐成为推动社会进步的关键力量。面对这一趋势&#xff0c;许多企业和个人…

永续合约多空双开“戴套”策略的逻辑是什么,胜率惊人的96%是怎么做到的,其实并没有想的那么复杂,会代码的都可以写出来

为什么叫多空双开“戴套”量化策略呢&#xff0c;因为这个策略的特点是永远有一个仓位是被套的&#xff0c;但是这个不影响我们盈利&#xff0c;具体怎么实现大家看下面这个图就明白是怎么回事了。 这个策略的逻辑很简单也容易理解&#xff0c;就是多空双开&#xff0c;盈利平仓…

FREERTOS空闲任务和低功耗

空闲任务 空闲任务是 FreeRTOS 必不可少的一个任务&#xff0c;其他 RTOS 类系统也有空闲任务&#xff0c;比如uC/OS。看名字就知道&#xff0c;空闲任务是处理器空闲的时候去运行的一个任务&#xff0c;当系统中没有其他就绪任务的时候空闲任务就会开始运行&#xff0c;空闲任…

slab分配器

什么是slab分配器&#xff1f; 用户态程序可以使用malloc及其在C标准库中的相关函数申请内存&#xff1b;内核也需要经常分配内存&#xff0c;但无法使用标准库函数&#xff1b;linux内核中&#xff0c;伙伴分配器是一种页分配器&#xff0c;是以页为单位的&#xff0c;但这个…

基于 Echarts + Python Flask ,我搭建了一个动态实时大屏监管系统

一、效果展示 1. 动态实时更新数据效果图 2. 鼠标右键切换主题 二、确定需求方案 支持Windows、Linux、Mac等各种主流操作系统&#xff1b;支持主流浏览器Chrome&#xff0c;Microsoft Edge&#xff0c;360等&#xff1b;服务器采用python语言编写&#xff0c;配置好python环…

计算机设计大赛 题目: 基于深度学习的疲劳驾驶检测 深度学习

文章目录 0 前言1 课题背景2 实现目标3 当前市面上疲劳驾驶检测的方法4 相关数据集5 基于头部姿态的驾驶疲劳检测5.1 如何确定疲劳状态5.2 算法步骤5.3 打瞌睡判断 6 基于CNN与SVM的疲劳检测方法6.1 网络结构6.2 疲劳图像分类训练6.3 训练结果 7 最后 0 前言 &#x1f525; 优…

Orange3数据预处理(公式组件)

公式 为您的数据集添加新特征。 输入 数据&#xff1a;输入数据集 输出 数据&#xff1a;带有额外特征的数据集 公式组件允许通过使用用户定义的表达式来计算新列。结果列可以是分类的、数值的或文本的。 对于数值变量&#xff0c;只需提供名称和表达式。 1.构建变量列表…

学点儿Java_Day7_在实体类当中IDEA无法进行单元测试(@Test没有启动按钮)

在敲代码体会继承和访问修饰符的时候忽然遇到了单元测试不管用的情况&#xff0c;表现为没有启动按钮   经过一番折腾&#xff0c;发现我的测试是在具有构造函数的实体类Person当中进行的&#xff0c;当我把所有的构造函数删除后&#xff0c;启动按钮又出来了&#xff0c;加…

水电能源智能化监控系统

水电能源智能化监控系统是利用现代信息技术&#xff0c;对水电站的运行状态、设备性能、环境参数等进行实时监测和管理的一种智能化系统。随着我国水电能源事业的快速发展&#xff0c;水电能源智能化监控系统在水电能源行业中的应用越来越广泛&#xff0c;为我国水电能源事业的…

关于继承是怎么样的?那当然是很好理解之

本文描述了关于继承的大部分知识&#xff0c;但是并不全&#xff0c;每篇博客之间的知识都有互串&#xff0c;所以需要把几篇文章合起来看&#xff0c;学会融会贯通&#xff01; 温馨提示&#xff1a;使用PC端观看&#xff0c;效果更佳&#xff01; 目录 1.继承是什么 2.什…

【位运算】【 数学】【 哈希映射】2857. 统计距离为 k 的点对

本文涉及知识点 位运算 数学 哈希映射 LeetCode 2857. 统计距离为 k 的点对 给你一个 二维 整数数组 coordinates 和一个整数 k &#xff0c;其中 coordinates[i] [xi, yi] 是第 i 个点在二维平面里的坐标。 我们定义两个点 (x1, y1) 和 (x2, y2) 的 距离 为 (x1 XOR x2) …

STM32最小核心板使用HAL库实现UART接口通讯(中断方式)

正式环境里需要串联电阻&#xff0c;或设计过滤电路。核心板是STM32F103C8T6 这里使用了UART3的接口&#xff0c;具体使用MX创建项目就不放了&#xff0c;百度下都有 /*USART3 GPIO ConfigurationPB10 ------> USART3_TXPB11 ------> USART3_RX */ 因为是串口…

开发技术-FeignClient 对单个接口设置超时时间

1. 背景 FeignClient 调用某个接口&#xff0c;3s 没有结果就需要停止&#xff0c;处理后续业务。 2. 方法 FeignClient 自定义 name 属性 FeignClient(name "aaa" , url "xxx") public interface TestApi {ResponseBodyPOSTMapping(value "xx…

以码会友|PR大征集!2024共绘 MoonBit 新篇章!

首先&#xff0c;感谢 MoonBit 社区所有的贡献者在过去一个月积极的参与和贡献&#xff01;为了感谢与鼓励更多的MoonBit Contributor&#xff0c;我们以码会友&#xff0c;邀请你加入“一起成为MoonBit Contributor&#xff01;” 的活动&#xff01; 活动内容也可以点击文章…

【第十三章】改进神经网络学习方式-其他正则化技术

L1正则化 除了L2正则化之外&#xff0c;还有许多正则化技术。事实上&#xff0c;已经开发出了如此多的技术&#xff0c;以至于我不可能总结它们。在本节中&#xff0c;我简要介绍了三种减少过拟合的其他方法&#xff1a;L1正则化、dropout和人为增加训练集大小。我们不会像之前…

在ComfyUI中,IP-Adapter的一大堆模型应该怎么放?

&#x1f381;背景介绍 IP-Adapter有一大堆的模型&#xff0c;那么这个模型在ComfyUI中&#xff0c;这些模型到底应该怎么放呢&#xff1f;这篇文章简单介绍一下。 首先&#xff0c;大家需要到huggingface上找到对应的模型&#xff0c;把所有的模型先下载下来。 huggingface…

技术工作报告-基于linux的信息转二维码图像方法的研究

一、摘要&#xff1a; 本报告旨在介绍基于Linux的信息转二维码图像方法的研究。通过对二维码技术的背景和相关研究的调研&#xff0c;我们提出了一种基于Linux平台的信息转二维码图像方法&#xff0c;并进行了实验验证。本方法可以在Linux系统上实现高效、准确的信息转二维码图…

数据分析的具体流程

1.导入 表格导入数据时要注意数据的格式问题非表格导入 可以先将文档放入word中 将换行符&#xff08;^p&#xff09;替换为|||&#xff0c;选择特殊格式中的段落标记 进行全部替换 以每一列最后的数据/平&#xff0c;作为换行的标志 将所整理的信息导入excel,对数据进行分列 选…

大数据 - Spark系列《十四》- spark集群部署模式

Spark系列文章&#xff1a; 大数据 - Spark系列《一》- 从Hadoop到Spark&#xff1a;大数据计算引擎的演进-CSDN博客 大数据 - Spark系列《二》- 关于Spark在Idea中的一些常用配置-CSDN博客 大数据 - Spark系列《三》- 加载各种数据源创建RDD-CSDN博客 大数据 - Spark系列《…