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

SpringCloud源码探析(十二)-基于SpringBoot开发自定义中间件

1.概述

中间件是一种介于操作系统和应用软件之间,为应用软件提供服务功能的软件,按功能划分有消息中间件(Kafka、RocketMQ)、通信中间件(RPC通信中间件,dubbo等),应用服务器等。中间件屏蔽了底层操作系统的复杂性,让开放工程师可以把更多的专注力放在业务系统上,能够有效提高开发人员效率。本文主要分析利用springboot开发自定义日志中间件,通过此中间件能够打印请求入参及返回结果,帮助大家更好地理解利用springboot如何开发中间件。

2.自定义AOP日志中间件

利用springboot开发中间件主要包含以下几个步骤:

1.创建自定义的starter项目
2.定义Starter需要的配置类
3.编写业务功能
4.编写自动配置类
5.编写spring.factories文件加载自动配置类
6.打包安装

本文将会按照上述步骤,以自定义AOP日志中间件为例进行分析。

2.1 需求背景

在利用spring开发的web应用中,请求会从controller进入并经过多次流转,最后返回结果。在这过程中可能会打印大量日志,进行问题排查时需要耗费大量时间和精力,为了能够提升排查问题效率,可以将每一次的请求进入和结束进行标识,打印请求IP、入参以及返回结果,这样在排查问题时能够快速定位请求内容及结果。
所以,基于上述背景,开发一个利用AOP对于入口Controller文件进行拦截处理,打印入参及返回结果等信息,所有利用spring开发的web应用能够直接引用此中间件,直接实现入口日志打印。

2.2 方案设计

整体设计方案如下图所示:
在这里插入图片描述
上述设计图主要包括以下内容:

1.SpringBoot Starter 的实现会自动加载配置,通过配置文件确定是否生成SpringAopLogAspect Bean;
2.在SpringAopLogAspect定义切面进行入口日志打印输出。

2.3 代码实现

spring-aop-log-starter类图关系如下图所示:在这里插入图片描述

  • AopLogProperties:属性配置类,获取日志打印开关属性,若为true,开启打印;
  • AopLogConfig:配置类,依赖AopLogProperties确定是否生成SpringAopLogAspect;
  • SpringAopLogAspect:业务逻辑类,拦截Controller并进行日志打印。

2.3.1 pom文件

 <dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-configuration-processor</artifactId><optional>true</optional></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-aop</artifactId></dependency><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency></dependencies><build><plugins><plugin><groupId>org.springframework.boot</groupId><artifactId>spring-boot-maven-plugin</artifactId><configuration><classifier>exec</classifier></configuration></plugin></plugins></build>

2.3.2 AopLogProperties

import org.springframework.boot.context.properties.ConfigurationProperties;/*** @Author: Marinc* @CreateTime: 2023-12-18  14:29* @Description: TODO* @Version: 1.0*/
@ConfigurationProperties(prefix = "aop.log")
public class AopLogProperties {private boolean enable;public AopLogProperties() {}public boolean isEnable() {return enable;}public void setEnable(boolean enable) {this.enable = enable;}}

@ConfigurationProperties,用于创建指定前缀( prefix = “aop.log”)的自定义配置信息,这样就在 yml 或者 properties 中读取到我们自己设定的配置信息。

2.3.3 AopLogConfig

import com.eckey.lab.aop.SpringAopLogAspect;
import com.eckey.lab.properties.AopLogProperties;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;/*** @Author: Marinc* @CreateTime: 2023-12-18  15:16* @Description: TODO* @Version: 1.0*/
@Configuration
@EnableConfigurationProperties({AopLogProperties.class})
@ConditionalOnProperty(prefix = "aop.log", value = "enable", havingValue = "true")
public class AopLogConfig {@Bean@ConditionalOnMissingBeanpublic SpringAopLogAspect springLogAspect() {return new SpringAopLogAspect();}}

@Configuration是定义一个配置类;
@EnableConfigurationProperties({AopLogProperties.class})注解的作用是让@ConfigurationProperties注解生效,如果只配置@ConfigurationProperties注解,在IOC容器中是获取不到properties配置文件转化的bean的;
@ConditionalOnProperty(prefix = “aop.log”,value = “enable”,havingValue = “true”)会将配置文件中的值和havingValue的值对比,如果一样则加载Bean;
@ConditionalOnMissingBean仅仅在当前上下文中不存在某个对象时,才会实例化一个 Bean。

2.3.4 SpringAopLogAspect

import com.eckey.lab.utils.IpInfoUtil;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;import javax.servlet.http.HttpServletRequest;
import java.util.Arrays;/*** @Author: Marinc* @CreateTime: 2023-12-18  14:33* @Description: TODO* @Version: 1.0*/
@Slf4j
@Aspect
@Component
public class SpringAopLogAspect {@Autowiredprivate IpInfoUtil ipInfoUtil;@Pointcut("execution(* *..*Controller.*(..))")public void springAopLog(){}@Before("springAopLog()")public void doBefore(JoinPoint joinPoint) throws Throwable {// 接收到请求,记录请求内容ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();HttpServletRequest request = attributes.getRequest();log.info("------------------请求开始------------------");// 记录下请求内容log.info("请求路径:{}", request.getRequestURL().toString());log.info("客户端IP :{}" , ipInfoUtil.getIpAddr(request));log.info("参数值 :{}", Arrays.toString(joinPoint.getArgs()));}@AfterReturning(returning = "res", pointcut = "springAopLog()")public void doAfterReturning(Object res) throws Throwable {// 处理完请求,返回内容log.info("返回值 : {}" , res);log.info("------------------请求结束------------------");}}

注解@Aspect定义类为切面类;
@Component 注解,将类生成为 Bean对象;
@Pointcut(“execution(* *…Controller.(…))”),定义切点。在Pointcut中提供了多种切点寻找方式(指定方法名称、范围筛选表达式、自定义注解等),一般在中间件开发中,自定义注解的使用比较多;
@Before(“springAopLog()”),可以理解为是对方法增强的织入动作,在方法执行前先执行;
@AfterReturning(returning = “res”, pointcut = “springAopLog()”)被代理的方法执行完成之后要执行的代码。

2.3.4 spring.factories

1.在resources下新建META-INF文件夹,然后创建spring.factories文件
2.在该文件中加入如下配置,该配置指定上步骤中定义的配置类为自动装配的配置

org.springframework.boot.autoconfigure.EnableAutoConfiguration=com.eckey.lab.config.AopLogConfig

2.3.5 测试结果

1.引入pom

    <dependency><groupId>com.eckey.lab</groupId><artifactId>spring-aop-log-starter</artifactId><version>1.0-SNAPSHOT</version></dependency>

2.在properties中配置

#配置切面打印日志
aop.log.enable=true

3.测试验证
在订单服务中访问地址:http://127.0.0.1:8082/order/test,结果如下:
在这里插入图片描述
在这里插入图片描述

3.小结

1.本文初步分析了一个基于切面和SpringBoot结合开发的中间件,包括了自定义配置如何设置、SpringBoot如何加载和生成Bean,以及切面拦截后的处理;
2.在切面拦截的逻辑相对比较简单,仅仅时拦截并打印了一些信息,这里可以进行拓展一下,通过自定义注解,配置在需要统计的方法上,统计一些关键信息,进行统计汇总,具体可以看第4节参考文献进行发散;
3.基于springboot开发中间件是一项基本技能,可以基于自己日常中常用的场景(短信发送、邮件发送等),基于不同场景多动手实践。

4.参考文献

1.https://blog.csdn.net/qq_33479841/article/details/116306864
2.https://zhuanlan.zhihu.com/p/642035645

5.附录

1.https://gitee.com/Marinc/nacos/tree/master/spring-aop-log-starter

相关文章:

SpringCloud源码探析(十二)-基于SpringBoot开发自定义中间件

1.概述 中间件是一种介于操作系统和应用软件之间&#xff0c;为应用软件提供服务功能的软件&#xff0c;按功能划分有消息中间件&#xff08;Kafka、RocketMQ&#xff09;、通信中间件&#xff08;RPC通信中间件&#xff0c;dubbo等&#xff09;&#xff0c;应用服务器等。中间…...

基于CNN+数据增强+残差网络Resnet50的少样本高准确度猫咪种类识别—深度学习算法应用(含全部工程源码)+数据集+模型(一)

系列文章目录 基于CNN数据增强残差网络Resnet50的少样本高准确度猫咪种类识别—深度学习算法应用(含全部工程源码)数据集模型&#xff08;一&#xff09; 基于CNN数据增强残差网络Resnet50的少样本高准确度猫咪种类识别—深度学习算法应用(含全部工程源码)数据集模型&#xf…...

python实现贪吃蛇游戏

文章目录 1、项目说明2、项目预览3、开发必备4、贪吃蛇代码实现4.1、窗口和基本参数实现4.2、绘制背景4.3、绘制墙壁4.4、绘制贪吃蛇4.5、绘制食物4.6、实现长度信息显示4.7、定义游戏暂停界面4.8、定义贪吃蛇死亡界面4.9、实现贪吃蛇碰撞效果4.10、实现添加食物功能4.11、实现…...

ios备忘录怎么导入华为 方法介绍

作为一个常常需要在不同设备间切换的人&#xff0c;我深知备忘录的重要性。那些突如其来的灵感、重要的会议提醒、甚至是生活中的琐碎小事&#xff0c;我们都习惯性地记录在备忘录里。但当我决定从iPhone转向华为时&#xff0c;一个问题困扰了我&#xff1a;如何将那些珍贵的备…...

electron与cesium组件入门应用功能

electron与cesium组件入门应用功能 运行应用效果图&#xff1a; electron应用目录&#xff0c;需要包括三个文件: index.html main.js package.json (一)、创建一个新项目 目录名称&#xff1a;project_helloWolrd (二)、生成package.json文件 npm init --yes(三&#x…...

Jenkins Docker Cloud在Linux应用开发CI中的实践

Jenkins Docker Cloud在Linux应用开发CI中的实践 背景 通过代码提交自动触发CI自动构建、编译、打包是任何软件开发组织必不可少的基建&#xff0c;可以最大程度保证产物的一致性&#xff0c;方便跨组跨部门协作&#xff0c;代码MR等。 Docker在流水线中越来越重要&#xff…...

502 Bad Gateway with nginx + apache + subversion + ssl

svn commit的时候返回 unexpected http status 502 bad gateway on解决方法&#xff0c;参考&#xff1a;https://stackoverflow.com/questions/2479346/502-bad-gateway-with-nginx-apache-subversion-ssl-svn-copy 在nginx中代理svn中添加 location /svn {set $fixed_dest…...

【PostgreSQL内核学习(十八)—— 存储管理(存储管理的体系结构)】

存储管理 概述存储管理器的体系结构存储管理器的主要任务读写元组过程 声明&#xff1a;本文的部分内容参考了他人的文章。在编写过程中&#xff0c;我们尊重他人的知识产权和学术成果&#xff0c;力求遵循合理使用原则&#xff0c;并在适用的情况下注明引用来源。 本文主要参考…...

Android的组件、布局学习

介绍 公司组织架构调整&#xff0c;项目组需要承接其他项目组的android项目&#xff0c;负责维护和开发新需求&#xff0c;故学习下基础语法和项目开发。 组件学习 Toolbarheader布局部分 就是app最顶部的部分 他的显示与否&#xff0c;是与F:\androidProject\android_lear…...

【离散数学】——期末刷题题库(树其一)

&#x1f383;个人专栏&#xff1a; &#x1f42c; 算法设计与分析&#xff1a;算法设计与分析_IT闫的博客-CSDN博客 &#x1f433;Java基础&#xff1a;Java基础_IT闫的博客-CSDN博客 &#x1f40b;c语言&#xff1a;c语言_IT闫的博客-CSDN博客 &#x1f41f;MySQL&#xff1a…...

光模块市场分析与发展趋势预测

光模块是光通信领域的重要组成部分&#xff0c;随着数字经济&#xff0c;大数据&#xff0c;云计算&#xff0c;人工智能等行业的兴起&#xff0c;光模块市场经历了快速发展&#xff0c;逐渐在数据中心、无线回传、电信传输等应用场景中得到广泛应用。本文将基于当前光模块全球…...

Python轴承故障诊断 (八)基于EMD-CNN-GRU并行模型的故障分类

目录 前言 1 经验模态分解EMD的Python示例 2 轴承故障数据的预处理 2.1 导入数据 2.2 制作数据集和对应标签 2.3 故障数据的EMD分解可视化 2.4 故障数据的EMD分解预处理 3 基于EMD-CNN-GRU并行模型的轴承故障诊断分类 3.1 训练数据、测试数据分组&#xff0c;数据分ba…...

鸿蒙实现年月日十分选择框,支持年月日、月日、日、年月日时分、时分切换

import DateTimeUtils from ./DateTimeUtils;CustomDialog export default struct RQPickerDialog {controller: CustomDialogControllertitle: string 这是标题TAG: string RQPickerDialog// 0 - 日期类型&#xff08;年月日&#xff09; 1 - 时间类型&#xff08;时分&a…...

IntelliJ IDE 插件开发 | (三)消息通知与事件监听

系列文章 IntelliJ IDE 插件开发 |&#xff08;一&#xff09;快速入门IntelliJ IDE 插件开发 |&#xff08;二&#xff09;UI 界面与数据持久化IntelliJ IDE 插件开发 |&#xff08;三&#xff09;消息通知与事件监听 前言 在前两篇文章中讲解了关于插件开发的基础知识&…...

VUE小知识点

Vue 是一款用于构建用户界面的 JavaScript 框架。它基于标准 HTML、CSS 和 JavaScript 构建&#xff0c;并提供了一套声明式的、组件化的编程模型&#xff0c;帮助你高效地开发用户界面。 Vue 的主要作用是帮助开发者构建现代 Web 应用程序。它允许前端开发人员专注于应用程序…...

深入了解常见的应用层网络协议

目录 1. HTTP协议 1.1. 工作原理 1.2. 应用场景 1.3. 安全性考虑 2. SMTP协议 2.1. 工作原理 2.2. 应用场景 2.3. 安全性考虑 3. FTP协议 3.1. 工作原理 3.2. 应用场景 3.3. 安全性考虑 4. DNS协议 4.1. 工作原理 4.2. 应用场景 4.3. 安全性考虑 5. 安全性考虑…...

网络爬虫 多任务采集

一、JSON文件存储 JSON&#xff0c;全称为 JavaScript 0bject Notation,也就是JavaSript 对象标记&#xff0c;它通过对象和数组的组合来表示数据&#xff0c;构造简洁但是结构化程度非常高&#xff0c;是一种轻量级的数据交换格式。本节中&#xff0c;我们就来了解如何利用 P…...

真实并发编程问题-1.钉钉面试题

&#x1f44f;作者简介&#xff1a;大家好&#xff0c;我是爱吃芝士的土豆倪&#xff0c;24届校招生Java选手&#xff0c;很高兴认识大家&#x1f4d5;系列专栏&#xff1a;Spring源码、JUC源码、Kafka原理、分布式技术原理、数据库技术&#x1f525;如果感觉博主的文章还不错的…...

基于vue+element-plus+echarts制作动态绘图页面(柱状图,饼图和折线图)

前言 我们知道echarts是一个非常强大的绘图库&#xff0c;基于这个库&#xff0c;我们可以绘制出精美的图表。对于一张图来说&#xff0c;其实比较重要的就是配置项&#xff0c;填入不同的配置内容就可以呈现出不同的效果。 当然配置项中除了样式之外&#xff0c;最重要的就是…...

2312llvm,02前端

前端 编译器前端,在生成目标相关代码前,把源码变换为编译器的中间表示.因为语言有独特语法和语义,所以一般,前端只处理一个语言或一组类似语言. 比如Clang,处理C,C,objective-C源码. 介绍Clang Clang项目是C,C,Objective-C官方的LLVM前端.Clang的官方网站在此. 实际编译器(…...

华为云AI开发平台ModelArts

华为云ModelArts&#xff1a;重塑AI开发流程的“智能引擎”与“创新加速器”&#xff01; 在人工智能浪潮席卷全球的2025年&#xff0c;企业拥抱AI的意愿空前高涨&#xff0c;但技术门槛高、流程复杂、资源投入巨大的现实&#xff0c;却让许多创新构想止步于实验室。数据科学家…...

在软件开发中正确使用MySQL日期时间类型的深度解析

在日常软件开发场景中&#xff0c;时间信息的存储是底层且核心的需求。从金融交易的精确记账时间、用户操作的行为日志&#xff0c;到供应链系统的物流节点时间戳&#xff0c;时间数据的准确性直接决定业务逻辑的可靠性。MySQL作为主流关系型数据库&#xff0c;其日期时间类型的…...

超短脉冲激光自聚焦效应

前言与目录 强激光引起自聚焦效应机理 超短脉冲激光在脆性材料内部加工时引起的自聚焦效应&#xff0c;这是一种非线性光学现象&#xff0c;主要涉及光学克尔效应和材料的非线性光学特性。 自聚焦效应可以产生局部的强光场&#xff0c;对材料产生非线性响应&#xff0c;可能…...

Zustand 状态管理库:极简而强大的解决方案

Zustand 是一个轻量级、快速和可扩展的状态管理库&#xff0c;特别适合 React 应用。它以简洁的 API 和高效的性能解决了 Redux 等状态管理方案中的繁琐问题。 核心优势对比 基本使用指南 1. 创建 Store // store.js import create from zustandconst useStore create((set)…...

QMC5883L的驱动

简介 本篇文章的代码已经上传到了github上面&#xff0c;开源代码 作为一个电子罗盘模块&#xff0c;我们可以通过I2C从中获取偏航角yaw&#xff0c;相对于六轴陀螺仪的yaw&#xff0c;qmc5883l几乎不会零飘并且成本较低。 参考资料 QMC5883L磁场传感器驱动 QMC5883L磁力计…...

从深圳崛起的“机器之眼”:赴港乐动机器人的万亿赛道赶考路

进入2025年以来&#xff0c;尽管围绕人形机器人、具身智能等机器人赛道的质疑声不断&#xff0c;但全球市场热度依然高涨&#xff0c;入局者持续增加。 以国内市场为例&#xff0c;天眼查专业版数据显示&#xff0c;截至5月底&#xff0c;我国现存在业、存续状态的机器人相关企…...

postgresql|数据库|只读用户的创建和删除(备忘)

CREATE USER read_only WITH PASSWORD 密码 -- 连接到xxx数据库 \c xxx -- 授予对xxx数据库的只读权限 GRANT CONNECT ON DATABASE xxx TO read_only; GRANT USAGE ON SCHEMA public TO read_only; GRANT SELECT ON ALL TABLES IN SCHEMA public TO read_only; GRANT EXECUTE O…...

PL0语法,分析器实现!

简介 PL/0 是一种简单的编程语言,通常用于教学编译原理。它的语法结构清晰,功能包括常量定义、变量声明、过程(子程序)定义以及基本的控制结构(如条件语句和循环语句)。 PL/0 语法规范 PL/0 是一种教学用的小型编程语言,由 Niklaus Wirth 设计,用于展示编译原理的核…...

大模型多显卡多服务器并行计算方法与实践指南

一、分布式训练概述 大规模语言模型的训练通常需要分布式计算技术,以解决单机资源不足的问题。分布式训练主要分为两种模式: 数据并行:将数据分片到不同设备,每个设备拥有完整的模型副本 模型并行:将模型分割到不同设备,每个设备处理部分模型计算 现代大模型训练通常结合…...

用机器学习破解新能源领域的“弃风”难题

音乐发烧友深有体会&#xff0c;玩音乐的本质就是玩电网。火电声音偏暖&#xff0c;水电偏冷&#xff0c;风电偏空旷。至于太阳能发的电&#xff0c;则略显朦胧和单薄。 不知你是否有感觉&#xff0c;近两年家里的音响声音越来越冷&#xff0c;听起来越来越单薄&#xff1f; —…...