编织梦想:SpringBoot AOP 教程与自定义日志切面完整实战

news2024/11/15 18:03:57

什么是 AOP

AOP 是指通过预编译方式和运行期动态代理的方式,在不修改源代码的情况下对程序进行功能增强的一种技术。AOP 不是面向对象编程(OOP)的替代品,而是 OOP 的补充和扩展。它是一个新的维度,用来表达横切问题,并提供一个声明式的处理方案。AOP 是 Spring 框架中的一个重要特性。

AOP 的使用场景

AOP 的使用场景一般是在某些纵向逻辑和多个相对独立的横向逻辑中,将横向逻辑进行抽象和封装,使得横向逻辑不再与纵向逻辑混杂在一起,使得应用程序更加易于维护和扩展。在实际开发中,AOP 的使用场景比较广泛,例如:

  • 日志记录:在应用程序中,可以通过 AOP 对方法调用进行拦截,在方法调用前后记录日志信息。
  • 安全处理:通过 AOP 实现安全方案,例如在应用程序中对某些敏感方法添加权限验证。
  • 性能监控:对应用程序进行性能监控,实现性能分析和调优。
  • 事物管理:通过 AOP 对事物进行管理,例如实现事物的回滚和提交。
  • 缓存管理:对应用程序进行缓存管理,例如在读写操作中进行缓存。

AOP 的核心概念

在学习 AOP 的过程中,有一些核心概念是相当重要的。

  1. 连接点(JointPoint)

连接点是程序中可能被拦截的方法。在 AOP 中,连接点是指所有被拦截到的方法。连接点包含两个信息:一个是方法的位置信息,另一个是方法的名称。

  1. 切点(Pointcut)

切点是一组连接点的集合,是要被拦截的连接点。在 Spring AOP 中,切点采用 AspectJ 的切点表达式进行描述,格式如 @Pointcut("execution(public * com.example.demo.controller.*.*(..))")

  1. 通知(Advice)

通知是指拦截到连接点后要执行的代码,包括 @Before@AfterReturning@AfterThrowing@After@Around 五种类型。

  1. 切面(Aspect)

切面是一个包含通知和切点的对象,主要用来维护切点和通知之间的关系。

  1. 织入(Weaving)

织入是将切面应用到目标对象来创建新的代理对象的过程。在 Spring AOP 中,织入可以在编译时、类加载时和运行时进行。

AOP 的实现方式

在 SpringBoot 中,AOP 的实现方式主要有两种:Java 代理(JDK Proxy)和字节码增强(CGLIB)。

  1. Java 代理(JDK Proxy)

Java 代理是一种基于接口的代理,通过实现 Java 动态代理接口 InvocationHandler 来实现对代理类方法的调用。Java 代理只能代理实现了接口的类,在运行时通过生成代理类的方式来实现。Java 代理的优点是操作简单,劣势是只能代理接口。

  1. 字节码增强(CGLIB)

字节码增强是一种基于继承的代理,通过生成代理类来完成对目标对象方法的调用。CGLIB 代理不需要实现接口,对目标对象进行继承并重写其中的方法,从而实现对方法的调用拦截。字节码增强的优点是能够代理非接口的类,劣势是需要引入 CGLIB 依赖包。

SpringBoot AOP 的例子

我们创建一个日志切面来记录调用方法开始时间、结束时间、持续时间等(方法名、参数、返回值…)。

pom.xml引入以下依赖包

<properties>
        <java.version>1.8</java.version>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
        <spring-boot.version>2.3.7.RELEASE</spring-boot.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter</artifactId>
            <version>${spring-boot.version}</version>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
            <version>${spring-boot.version}</version>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-aop</artifactId>
            <version>${spring-boot.version}</version>
        </dependency>
    </dependencies>

定义SysLog日志注解

@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface SysLog {
    String name() default "";
}

定义切面 LogAspect

@Aspect
public class LogAspect {

    // 定义需要被拦截的切点
    @Pointcut("execution(public * com.example.demo.controller.*.*(..))")
    public void pointcut() {
    }

    // 在方法执行时进行通知
    @Around("pointcut()")
    public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        Method method = signature.getMethod();
        SysLog sysLog = method.getAnnotation(SysLog.class);
        if (sysLog != null) {
            System.out.println("日志名称:" + sysLog.name());
        }
        System.out.println("开始时间:" + LocalDateTime.now());
        System.out.println("执行方法 " + joinPoint.getSignature().getName() + " 前置环绕通知");
        Object o = joinPoint.proceed();
        System.out.println("结束时间:" + LocalDateTime.now());
        System.out.println("执行方法 " + joinPoint.getSignature().getName() + " 后置环绕通知");
        return o;
    }

    // 在方法执行之前进行通知
    @Before("pointcut()")
    public void before(JoinPoint joinPoint) {
        System.out.println("执行方法 " + joinPoint.getSignature().getName() + " 前置通知");
    }

    // 在方法执行之后进行通知
    @After("pointcut()")
    public void after(JoinPoint joinPoint) {
        System.out.println("执行方法 " + joinPoint.getSignature().getName() + " 后置通知");
    }

    // 在方法执行之后返回结果时进行通知
    @AfterReturning(returning = "result", pointcut = "pointcut()")
    public void afterReturning(JoinPoint joinPoint, Object result) {
        System.out.println("执行方法 " + joinPoint.getSignature().getName() + " 返回通知,返回值:" + result);
    }

    // 在方法抛出异常时进行通知
    @AfterThrowing(throwing = "ex", pointcut = "pointcut()")
    public void afterThrowing(JoinPoint joinPoint, Exception ex) {
        System.err.println("执行方法 " + joinPoint.getSignature().getName() + " 异常通知,异常:" + ex.getMessage());
    }
}

整个通知流程如下:


注意:如下必须注入LogAspect Bean。

@Configuration
public class MyConfiguration {
    @Bean
    public LogAspect logAspect() {
        return new LogAspect();
    }
}

测试,在Controller方法上添加@SysLog(name = “这是一个日志名称”)注解。

@RestController()
@RequestMapping("user")
public class UserController {

    @SysLog(name = "这是一个日志名称")
    @GetMapping("test")
    public String test() {
        return "hello world";
    }
}

测试:访问 http://localhost:8080/user/test,打印如下结果:

日志名称:这是一个日志名称
开始时间:2023-07-17T16:33:11.935
执行方法 test 前置环绕通知
执行方法 test 前置通知
执行方法 test 返回通知,返回值:hello world
执行方法 test 后置通知
结束时间:2023-07-17T16:33:11.940
执行方法 test 后置环绕通知

总结

以上就是 SpringBoot AOP 的基本用法,通过使用 SpringBoot AOP,我们可以在不修改源代码的情况下对程序进行功能增强,实现对方法的拦截、日志记录、权限验证、性能监控等功能。

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

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

相关文章

一文看懂群晖 NAS 安装 Mysql 远程访问连接

文章目录 1. 安装Mysql2. 安装phpMyAdmin3. 修改User 表4. 本地测试连接5. 安装cpolar6. 配置公网访问地址7. 固定连接公网地址 群晖安装MySQL具有高效、安全、可靠、灵活等优势&#xff0c;可以为用户提供一个优秀的数据管理和分析环境。同时具有良好的硬件性能和稳定性&#…

spring boot 3使用 elasticsearch 提供搜索建议

业务场景 用户输入内容&#xff0c;快速返回建议&#xff0c;示例效果如下 技术选型 spring boot 3elasticsearch server 7.17.4spring data elasticsearch 5.0.1elasticsearch-java-api 8.5.3 pom.xml <dependency><groupId>org.springframework.boot</gr…

如何快速搭建一个预约系统?

如何快速搭建一个预约系统 ? 线上预约系统的出现&#xff0c;为解决线下排队拥堵问题提供了便利和解决方案。通过线上预约&#xff0c;人们可以提前预约时间段&#xff0c;避免了到场后需要长时间等待的情况。以下是线上预约解决线下排队拥堵问题的几个场景&#xff1a; 餐厅…

深入完整的带你了解java对象的比较

目录 元素的比较 1.基本类型的比较 2.对象比较的问题 1.运行结果 2.疑问 3.原因 对象的比较 1.覆写基类的equals 2.基于Comparble接口类的比较 3.基于比较器比较 4.三种方式对比 元素的比较 1.基本类型的比较 在Java 中&#xff0c;基本类型的对象可以直接比较大…

52所韩国大学联合退出QS世界大学排名

因今年QS的全新评估指标&#xff0c;令韩国大学在世界大学的排名中大幅下降&#xff0c;因此韩国52所大学联合发表声明&#xff0c;将不再参加QS世界大学排名。下面知识人网小编做详细介绍。 今年6月28日&#xff0c;QS教育集团正式发布了2024年世界大学排名&#xff0c;见“排…

609. 在系统中查找重复文件

609. 在系统中查找重复文件 原题链接&#xff1a;完成情况&#xff1a;解题思路&#xff1a;参考代码&#xff1a; 原题链接&#xff1a; 609. 在系统中查找重复文件 https://leetcode.cn/problems/find-duplicate-file-in-system/description/ 完成情况&#xff1a; 解题思…

网络编程(三次握手、四次挥手)

一、Wireshark 窗口介绍 二、 wireshark与对应的OSI七层模型 服务器和客户端的代码不能都运行在ubuntu&#xff0c;因为wireshark抓的是流经真实网卡的数据包。 若将服务器客户端都运行在ubuntu&#xff0c;数据直接经过虚拟网卡通信&#xff0c;而不会经过真实网卡。 三、以太…

QT样式表(一)

目录 QSS样式表设置 1、概念 1、通过UI设计器的可视操作修改 2、通过代码修改 3、从样式文件加载 2、样式表语法 选择器的类型 3、设置背景图片 1、添加资源文件 2、使用label控件 显示头像&#xff0c;在样式表文件中进行设置 3、在项目中添加图片资源 QSS样式表设…

动态规划入门之二维数组的动态规划(过河卒)

P1002 [NOIP2002 普及组] 过河卒 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn) 过河卒&#xff0c;首先科普一下象棋里面的马的跳跃一步的规则吧&#xff08;这题真够坑人的&#xff0c;连个规则都不给出&#xff0c;害得我第一次交就全wa&#xff09;。一张图解释 大家看所…

Nvidia Jetson 编解码开发(4)H265解码图像异常问题调试

1. 前言 基于此贴继续验证H265图像 Nvidia Jetson 编解码开发(3)解决H265解码报错“PPS id out of range”_free-xx的博客-CSDN博客 ffmpeg解码后图像颜色异常 2. 梳理YUV格式顺序 相机输出图像格式: YUV422 8bit, UYVY 由于编码模块输入必须是 YUV420, 不能是YUV…

SpringCloud Ribbon中的7种负载均衡策略

SpringCloud Ribbon中的7种负载均衡策略 Ribbon 介绍负载均衡设置7种负载均衡策略1.轮询策略2.权重策略3.随机策略4.最小连接数策略5.重试策略6.可用性敏感策略7.区域敏感策略 总结 负载均衡通器常有两种实现手段&#xff0c;一种是服务端负载均衡器&#xff0c;另一种是客户端…

内网渗透神器CobaltStrike之权限提升(七)

Uac绕过 常见uac攻击模块 UAC-DLL UAC-DLL攻击模块允许攻击者从低权限的本地管理员账户获得更高的权限。这种攻击利用UAC的漏洞&#xff0c;将ArtifactKit生成的恶意DLL复制到需要特权的位置。 适用于Windows7和Windows8及更高版本的未修补版本 Uac-token-duplication 此攻…

软件测试开发实战|接口自动化测试框架开发(pytest+allure+aiohttp+用例自动生成)

近期准备优先做接口测试的覆盖&#xff0c;为此需要开发一个测试框架&#xff0c;经过思考&#xff0c;这次依然想做点儿不一样的东西。 接口测试是比较讲究效率的&#xff0c;测试人员会希望很快能得到结果反馈&#xff0c;然而接口的数量一般都很多&#xff0c;而且会越来越…

鼎友餐饮信息总监杨山海:餐饮新增长依托数智应用,用数字化打造单店盈利模型

杨山海 鼎友餐饮信息总监 近20年餐饮行业信息化、数字化决策经验&#xff0c;曾担任新辣道、雕刻时光、青年餐厅、快乐蜂、鲜果时间信数化负责人&#xff0c;主抓数字化转型、系统选型、多接口系统管理等&#xff0c;拥有丰富的餐饮行业数字化转型经验。 2000年&#xff0c;杨…

vue快捷生成初始代码模板

1.在 vscode 中找到如图设置->代码片段->vue.json 2.配置默认值 {"vue3": {"prefix": "vue3","body": ["<template>"," <div></div>","</template>","",&qu…

【HCIP】09.路由引入

路由引入&#xff1a;路由优先级 路由引入&#xff1a;路由回灌 场景描述 R1访问目标地址5.5.5.5&#xff0c;如果数据发给R3就会形成路由回灌R1访问5.5.5.5时&#xff0c;查询路由表&#xff0c;发现5.5.5.5是R3将ISIS引入产生的路由&#xff0c;则将下一跳指向R3R3收到数据包…

arm:day7

1.软中断处理 .text 文本段 .global _start 声明一个_start全局函数的入口 _start: _start标签&#xff0c;就是c语言的函数 1.构建异常表b resetb undefb softwareb prefetchb datab irqb fiqreset: 2.系统上电&#xff0c;程序运行在svc模式 初始化svc模式的栈指针ldr sp,0x4…

财报解读:“三驾马车”驱动,劲仔食品能否越跑越快?

国内休闲零食市场上半年表现喜人。 8月以来&#xff0c;多家休闲零食上市公司发布半年报告或预告&#xff0c;纷纷迎来利润增长。盐津铺子预计2023年上半年实现扣除非经常性损益后的净利润2.26亿元至2.36亿元&#xff0c;同比增长98.89%至107.69%&#xff1b;甘源食品预计2023…

【Java转Go】快速上手学习笔记(四)之基础篇三

目录 泛型内置泛型的使用切片泛型和泛型函数map泛型泛型约束泛型完整代码 接口反射协程特点WaitGroupgoroutine的调度模型&#xff1a;MPG模型 channel介绍语法&#xff1a;举例&#xff1a;channel遍历基本使用和协程一起使用案例一案例二 select...casemain.go 完整代码 文件…

备份服务器搭建

备份服务器搭建 1、背景2、作用3、选型4、环境5、部署5.1、服务端部署5.1.1、安装5.1.2、配置 5.2、客户端部署5.3、备份策略5.3.1、定时备份策略5.3.2、文件变动备份 6、参考 1、背景 随着项目的推进&#xff0c;备份服务器被提上了工作日程&#xff0c;等保、密评和接入测评…