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

什么是 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,我们可以在不修改源代码的情况下对程序进行功能增强,实现对方法的拦截、日志记录、权限验证、性能监控等功能。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

STM32 中断复习

中断 打断CPU执行正常的程序&#xff0c;转而处理紧急程序&#xff0c;然后返回原暂停的程序继续运行&#xff0c;就叫中断。 在确定时间内对相应事件作出响应&#xff0c;如&#xff1a;温度监控&#xff08;定时器中断&#xff09;。故障处理&#xff0c;检测到故障&#x…

用python从零开始做一个最简单的小说爬虫带GUI界面(1/3)

目录 前言 三节博客内容概要 PyQt5的配置 设置软件的快捷启动方式 1. 用于设计界面的程序 2. 将Qt Designer设计出来的ui文件转化为py文件 3. 可以把py文件打包成可执行的exe文件 4. 将ico图片放在qrc文件中&#xff0c;再将qrc文件转换成py…

SpringBoot中properties、yml、yaml的优先级

原理 配置优先级低的会先加载然后会被配置优先级高的覆盖 验证 创建SpringBoot项目&#xff08;网址&#xff09; 在resource目录下创建application.properties、application.yml、application.yaml文件 运行 结论 优先级顺序&#xff1a; properties>yml>yaml

安防监控视频云存储平台EasyNVR出现内核报错的情况该如何解决?

安防视频监控汇聚EasyNVR视频集中存储平台&#xff0c;是基于RTSP/Onvif协议的安防视频平台&#xff0c;可支持将接入的视频流进行全平台、全终端分发&#xff0c;分发的视频流包括RTSP、RTMP、HTTP-FLV、WS-FLV、HLS、WebRTC等格式。 近期有用户联系到我们&#xff0c;EasyNVR…

Shell语法揭秘:深入探讨常见Linux Shell之间的语法转换

深入探讨常见Linux Shell之间的语法转换 一、引言二、Linux常用Shell&#xff1a;Bash、Zsh、Ksh、Csh、Tcsh和Fish的简介2.1、Bash、Zsh、Ksh、Csh、Tcsh和Fish的特点和用途2.2、语法差异是常见Shell之间的主要区别 三、变量和环境设置的语法差异3.1、变量定义和使用的不同语法…

MAC钓鱼并Root权限上线CS并权限维持,以及所有的坑如何解决

本文转载于&#xff1a;https://www.freebuf.com/articles/web/350592.html 作者&#xff1a;文鸯涂鸦智能安全实验室 制作MAC 一、下载工具 首先从github上下载CrossC2。链接&#xff1a;https://github.com/gloxec/CrossC2/releases/tag/v3.1.0。 根据你CS客户端的操作系统选…

Linux内核学习(六)—— 中断(基于Linux 2.6内核)

一、中断 中断使得硬件得以发出通知给处理器。中断随时都可以产生&#xff0c;如键盘敲击就会触发中断&#xff0c;通知操作系统有按键按下。 不同设备对应的中断不同&#xff0c;而每个中断都通过一个唯一的数字标识。这些中断值通常被称为中断请求&#xff08;IRQ&#xff…

Dockerfile制作镜像与搭建LAMP环境

1、编写Dockerfile制作Web应用系统nginx镜像&#xff0c;生成镜像nginx:v1.1&#xff0c;并推送其到私有仓库。 具体要求如下&#xff1a; &#xff08;1&#xff09;基于centos基础镜像&#xff1b; &#xff08;2&#xff09;指定作者信息&#xff1b; &#xff08;3&#x…

VMware虚拟机Ubuntu无法连接网络的解决方法

一、解决办法 网络适配器设置 终端依次执行下面命令即可 sudo nmcli networking off sudo nmcli networking onsudo service network-manager start #或者 sudo service NetworkManager start成功出现这个图标&#xff0c;即代表网络连接成功。

AveMaria 传播手段的变化

AveMaria 是一种最早在 2018 年 12 月出现的窃密木马&#xff0c;攻击者越来越喜欢使用其进行攻击&#xff0c;运营方也一直在持续更新和升级。在过去六个月中&#xff0c;研究人员观察到 AveMaria 的传播手段发生了许多变化。 2022 年 12 月攻击行动 研究人员发现了名为 .Vh…

Linux中shell脚本——for、while循环及脚本练习

目录 一.for循环 1.1.基本格式 1.2.类C语言格式 二.while循环 2.1.基本格式 2.2.死循环语句 三.跳出循环 3.1.continue跳出循环 3.2.break跳出循环 四.常用循环 4.1.循环打印九九乘法表 4.2.循环ping测试某个网段网络连通性 4.3.while死循环实现猜数字游戏 4.4.数…

Python自动化小技巧18——自动化资产月报(word设置字体表格样式,查找替换文字)

案例背景 每月都要写各种月报&#xff0c;经营管理月报&#xff0c;资产月报.....这些报告文字目标都是高度相似的&#xff0c;只是需要替换为每个月的实际数据就行&#xff0c;如下&#xff1a; (打码是怕信息泄露.....) 可以看到&#xff0c;这个报告的都是高度模板化&…

Linux 线程安全

一、线程安全的概念 线程安全即就是在多线程运行的时候&#xff0c;不论线程的调度顺序怎样&#xff0c;最终的结果都是 一样的、正确的。那么就说这些线程是安全的。 二、如何保证线程安全 1.线程同步 保证同一时刻只有一个线程访问临界资源。线程同步的方法有4种&#xf…

python中使用xml快速创建Caption和URL书签管理器应用程序

导语&#xff1a; 本文介绍如何使用wxPython库创建一个Caption和URL管理器应用程序。该应用程序具有图形用户界面&#xff0c;允许用户输入Caption和URL&#xff0c;并将其保存到XML文件中。此外&#xff0c;还提供了浏览文件夹并选择HTML文件的功能&#xff0c;并可以运行另一…

低代码开发 轻松解决企业数字化能力建设困局

谈及数字化&#xff0c;这是一个几乎所有领域都在使用的概念。当下&#xff0c;数字化正在经历从以企业为中心向产业为中心转移、从追求效能为主的价值诉求向追求业务创新和业务发展的价值诉求转变&#xff0c;不断增加的不确定性也为数字化的发展蒙上了一层阴影。 除了企业自…

基于Spring Boot的机场VIP客户管理系统的设计与实现(Java+spring boot+MySQL)

获取源码或者论文请私信博主 演示视频&#xff1a; 基于Spring Boot的机场VIP客户管理系统的设计与实现&#xff08;Javaspring bootMySQL&#xff09; 使用技术&#xff1a; 前端&#xff1a;html css javascript jQuery ajax thymeleaf 微信小程序 后端&#xff1a;Java s…

数据结构作业——哈夫曼树

/*【基本要求】 &#xff08;1&#xff09; 从文件中读出一篇英文文章&#xff0c;包含字母和空格等字符。 &#xff08;2&#xff09; 统计各个字符出现的频度。 &#xff08;3&#xff09; 根据出现的频度&#xff0c;为每个出现的字符建立一个哈夫曼编码&#xff0c;并输出。…

深入源码分析kubernetes informer机制(二)Reflector

[阅读指南] 这是该系列第二篇 基于kubernetes 1.27 stage版本 为了方便阅读&#xff0c;后续所有代码均省略了错误处理及与关注逻辑无关的部分。 文章目录 Reflector是什么整体结构工作流程list拉取数据缓存resync操作watch监听操作 总结 Reflector是什么 reflector在informer…

爬虫逆向实战(七)--猿人学第十六题

一、数据接口分析 主页地址&#xff1a;猿人学第十六题 1、抓包 通过抓包可以发现数据接口是api/match/16 2、判断是否有加密参数 请求参数是否加密&#xff1f; 通过查看“载荷”模块可以看出m是加密参数 请求头是否加密&#xff1f; 无响应是否加密&#xff1f; 无cook…

云聊天项目测试

前言 以下将对云聊天项目编写测试用例以及主要功能的自动化测试。 1. 测试用例的编写 2. 自动化测试 以下进行部分自动化测试用例的执行&#xff0c;检验项目功能是否符合预期。 2.1 登录功能测试 测试代码&#xff1a; 输入非法用户名或密码逻辑相似&#xff0c;不重复描…