springBoot_自定义starter
Spring Boot 自定义 Starter 详解
一、Spring Boot Starter 基础概念
1.1 什么是 Spring Boot Starter
Spring Boot Starter 是 Spring Boot 的一个核心概念,它是一种特殊的依赖描述符,包含了一组可以集成到应用中的依赖项。简单来说,Starter 就是一组预配置的依赖项,使开发者能够快速地将特定功能添加到 Spring Boot 应用中,而无需手动管理每个单独的依赖及其版本。
1.2 Starter 的核心价值
-
简化依赖管理:
- 一个 Starter 通常会引入多个必要的依赖,这些依赖已经过版本兼容性测试
- 开发者只需引入一个 Starter,而不是手动管理多个独立依赖及其版本关系
-
自动配置:
- Starter 不仅仅提供依赖,还通常包含自动配置类
- 能根据应用环境自动配置 Bean,减少手动配置的工作量
-
约定大于配置:
- 遵循"约定大于配置"的设计理念,提供合理的默认配置
- 开发者只需关注与默认行为不同的部分
二、Spring Boot Starter 工作原理
2.1 依赖管理
Maven/Gradle 的依赖管理是 Starter 的基础。每个 Starter 都定义了一组协调工作的依赖:
<!-- Spring Boot Web Starter 示例 -->
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId>
</dependency>
这个简单的依赖会引入:
- spring-web 和 spring-webmvc 核心库
- 内嵌 Tomcat 服务器
- JSON 处理库
- 日志库
- 其他 Web 应用所需的各种组件
2.2 自动配置机制
Starter 的强大之处在于它们的自动配置能力,主要通过以下方式实现:
-
@EnableAutoConfiguration 注解:
- Spring Boot 应用通过 @SpringBootApplication(其中包含 @EnableAutoConfiguration)启用自动配置
- 该注解触发自动配置的加载过程
-
spring.factories 文件:
- 每个 Starter 通常在 META-INF/spring.factories 文件中声明其自动配置类
- Spring Boot 在启动时会扫描 classpath 下所有的 spring.factories 文件
- 加载其中声明的自动配置类
-
条件化配置:
- 自动配置类使用 @Conditional 系列注解,只在满足特定条件时才创建 Bean
- 例如 @ConditionalOnClass、@ConditionalOnMissingBean、@ConditionalOnProperty 等
2.3 属性绑定
- Starter 使用 @ConfigurationProperties 注解绑定配置属性
- 开发者可以在 application.properties/yml 中覆盖默认配置
三、常见的 Spring Boot Starter
Spring Boot 提供了多种官方 Starter,以下是一些常用的:
| Starter 名称 | 作用 |
|---|---|
| spring-boot-starter | 核心 Starter,包含自动配置、日志和 YAML 支持 |
| spring-boot-starter-web | 构建 Web 应用,包括 RESTful 应用程序 |
| spring-boot-starter-data-jpa | 使用 Spring Data JPA 与数据库交互 |
| spring-boot-starter-security | 添加 Spring Security 支持 |
| spring-boot-starter-test | 测试支持,包括 JUnit、Hamcrest 和 Mockito |
| spring-boot-starter-jdbc | 使用 JDBC 访问数据库 |
| spring-boot-starter-actuator | 提供生产就绪功能,帮助监控和管理应用 |
| spring-boot-starter-thymeleaf | 使用 Thymeleaf 视图构建 MVC 应用 |
| spring-boot-starter-cache | 提供缓存支持 |
| spring-boot-starter-validation | 提供 Java Bean 校验功能 |
四、Starter 的命名规范
Spring Boot 官方 Starter 遵循特定的命名模式:
- 官方 Starter:
spring-boot-starter-*,如 spring-boot-starter-web - 第三方 Starter:
*-spring-boot-starter,如 mybatis-spring-boot-starter
这种命名约定使开发者可以轻松区分官方和社区提供的 Starter。
五、自定义 Spring Boot Starter 开发步骤
创建自定义 Starter 通常需要以下步骤:
5.1 创建配置属性类
@ConfigurationProperties(prefix = "acme.service")
public class AcmeServiceProperties {private boolean enabled = true;private String prefix = "默认前缀";private String suffix = "默认后缀";// getter 和 setter 方法
}
5.2 创建自动配置类
@Configuration
@ConditionalOnClass(AcmeService.class)
@EnableConfigurationProperties(AcmeServiceProperties.class)
public class AcmeServiceAutoConfiguration {@Autowiredprivate AcmeServiceProperties properties;@Bean@ConditionalOnMissingBean@ConditionalOnProperty(prefix = "acme.service", value = "enabled", havingValue = "true", matchIfMissing = true)public AcmeService acmeService() {return new AcmeServiceImpl(properties.getPrefix(), properties.getSuffix());}
}
5.3 注册自动配置类
在 META-INF/spring.factories 文件中添加:
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.example.acme.AcmeServiceAutoConfiguration
5.4 Maven 依赖结构
通常,自定义 Starter 项目分为两个模块:
- 自动配置模块:包含自动配置代码
- Starter 模块:包含必要的依赖,依赖于自动配置模块
<!-- Starter 模块的 pom.xml -->
<dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter</artifactId></dependency><!-- Spring Boot 自动配置核心依赖 --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-autoconfigure</artifactId></dependency><!-- 用于生成配置元数据,提供IDE自动提示功能 --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-configuration-processor</artifactId><optional>true</optional></dependency><!-- 其他必要依赖 -->
</dependencies>
六、Starter 最佳实践
-
提供明确的文档:
- 详细说明 Starter 的功能
- 列出所有可配置的属性及其默认值
- 提供使用示例
-
遵循命名约定:
- 第三方 Starter 使用
xxx-spring-boot-starter命名
- 第三方 Starter 使用
-
设计合理的默认值:
- 默认配置应适合大多数使用场景
- 同时允许灵活定制
-
合理使用条件注解:
- 避免与其他配置冲突
- 允许用户覆盖默认行为
七、Starter 的优势总结
- 大幅减少配置工作:通过自动配置减少样板代码
- 提供经过验证的依赖组合:解决依赖冲突和版本不兼容问题
- 简化技术集成:使不同技术栈的整合变得更加容易
- 增强可维护性:依赖更新时只需更新 Starter 版本
- 加速开发过程:开发者可以专注于业务逻辑而非基础设施配置
八、自定义日志切面 Starter 实战案例
本部分将介绍如何创建一个自定义的日志切面 Starter,用于在 Spring Boot 应用中自动记录方法调用日志,包括注解方式和非注解方式两种实现。
代码地址
8.1 整体方案设计
我们将创建一个名为 log-aspect-spring-boot-starter 的自定义 Starter,它能够:
- 自动记录方法的调用信息(入参、返回值、执行时间等)
- 支持通过注解指定需要记录日志的方法
- 支持全局配置,无需注解也能记录日志
- 允许自定义日志格式和输出方式
8.2 项目结构
log-aspect-spring-boot-starter/
├── src/
│ └── main/
│ ├── java/
│ │ └── com/
│ │ └── example/
│ │ └── logaspect/
│ │ ├── annotation/
│ │ │ └── LogRecord.java
│ │ ├── aspect/
│ │ │ ├── AnnotationLogAspect.java
│ │ │ └── GlobalLogAspect.java
│ │ ├── config/
│ │ │ ├── LogAspectAutoConfiguration.java
│ │ │ └── LogAspectProperties.java
│ │ └── service/
│ │ ├── LogRecordService.java
│ │ └── DefaultLogRecordServiceImpl.java
│ └── resources/
│ └── META-INF/
│ ├── spring.factories
│ └── additional-spring-configuration-metadata.json
├── pom.xml
└── README.md
8.3 核心代码实现
8.3.1 创建日志记录注解
首先,我们定义一个注解,用于标记需要记录日志的方法:
package com.example.logaspect.annotation;import java.lang.annotation.*;/*** 日志记录注解,用于标记需要记录日志的方法*/
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface LogRecord {/*** 日志描述信息*/String description() default "";/*** 是否记录入参*/boolean recordParams() default true;/*** 是否记录返回值*/boolean recordResult() default true;/*** 是否记录执行时间*/boolean recordExecutionTime() default true;/*** 日志级别*/LogLevel level() default LogLevel.INFO;/*** 日志级别枚举*/enum LogLevel {DEBUG, INFO, WARN, ERROR}
}
8.3.2 创建日志记录服务接口
定义日志记录的服务接口:
package com.example.logaspect.service;import com.example.logaspect.annotation.LogRecord.LogLevel;/*** 日志记录服务接口*/
public interface LogRecordService {/*** 记录方法开始执行** @param methodSignature 方法签名* @param description 描述信息* @param args 方法参数* @param level 日志级别*/void recordMethodStart(String methodSignature, String description, Object[] args, LogLevel level);/*** 记录方法执行完成** @param methodSignature 方法签名* @param description 描述信息* @param args 方法参数* @param result 返回结果* @param executionTime 执行时间(毫秒)* @param level 日志级别*/void recordMethodEnd(String methodSignature, String description, Object[] args,Object result, long executionTime, LogLevel level);/*** 记录方法执行异常** @param methodSignature 方法签名* @param description 描述信息* @param args 方法参数* @param throwable 异常信息* @param executionTime 执行时间(毫秒)* @param level 日志级别*/void recordMethodException(String methodSignature, String description, Object[] args,Throwable throwable, long executionTime, LogLevel level);
}
8.3.3 创建默认的日志记录服务实现
package com.example.logaspect.service;import com.example.logaspect.annotation.LogRecord.LogLevel;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.util.StringUtils;/*** 默认日志记录服务实现*/
public class DefaultLogRecordServiceImpl implements LogRecordService {private static final Logger log = LoggerFactory.getLogger(DefaultLogRecordServiceImpl.class);private final ObjectMapper objectMapper = new ObjectMapper();@Overridepublic void recordMethodStart(String methodSignature, String description, Object[] args, LogLevel level) {String message = String.format("开始执行方法: %s", methodSignature);if (StringUtils.hasText(description)) {message += String.format(" | 描述: %s", description);}if (args != null && args.length > 0) {try {message += String.format(" | 入参: %s", objectMapper.writeValueAsString(args));} catch (Exception e) {message += String.format(" | 入参: %s (序列化失败)", args);}}logByLevel(message, level);}@Overridepublic void recordMethodEnd(String methodSignature, String description, Object[] args,Object result, long executionTime, LogLevel level) {String message = String.format("方法执行完成: %s | 耗时: %d毫秒", methodSignature, executionTime);if (StringUtils.hasText(description)) {message += String.format(" | 描述: %s", description);}if (result != null) {try {message += String.format(" | 返回值: %s", objectMapper.writeValueAsString(result));} catch (Exception e) {message += String.format(" | 返回值: %s (序列化失败)", result);}}logByLevel(message, level);}@Overridepublic void recordMethodException(String methodSignature, String description, Object[] args,Throwable throwable, long executionTime, LogLevel level) {String message = String.format("方法执行异常: %s | 耗时: %d毫秒 | 异常: %s",methodSignature, executionTime, throwable.getMessage());if (StringUtils.hasText(description)) {message += String.format(" | 描述: %s", description);}logByLevel(message, LogLevel.ERROR);}private void logByLevel(String message, LogLevel level) {switch (level) {case DEBUG:log.debug(message);break;case INFO:log.info(message);break;case WARN:log.warn(message);break;case ERROR:log.error(message);break;default:log.info(message);break;}}
}
8.3.4 创建配置属性类
package com.example.logaspect.config;import org.springframework.boot.context.properties.ConfigurationProperties;import java.util.ArrayList;
import java.util.List;/*** 日志切面配置属性*/
@ConfigurationProperties(prefix = "logging.aspect")
public class LogAspectProperties {/*** 是否启用日志切面*/private boolean enabled = true;/*** 是否启用基于注解的日志切面*/private boolean enableAnnotationAspect = true;/*** 是否启用全局日志切面(无需注解)*/private boolean enableGlobalAspect = false;/*** 全局切面的切入点表达式*/private String globalPointcut = "execution(* com.example..*.*(..))";/*** 是否记录方法入参*/private boolean recordParams = true;/*** 是否记录方法返回值*/private boolean recordResult = true;/*** 是否记录方法执行时间*/private boolean recordExecutionTime = true;/*** 排除的包路径,这些包下的方法不会被记录日志*/private List<String> excludePackages = new ArrayList<>();// getters and setters// ...
}
8.3.5 创建基于注解的日志切面
package com.example.logaspect.aspect;import com.example.logaspect.annotation.LogRecord;
import com.example.logaspect.annotation.LogRecord.LogLevel;
import com.example.logaspect.config.LogAspectProperties;
import com.example.logaspect.service.LogRecordService;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.core.annotation.Order;import java.lang.reflect.Method;/*** 基于注解的日志切面*/
@Aspect
@Order(1)
public class AnnotationLogAspect {private final LogRecordService logRecordService;private final LogAspectProperties properties;public AnnotationLogAspect(LogRecordService logRecordService, LogAspectProperties properties) {this.logRecordService = logRecordService;this.properties = properties;}@Around("@annotation(com.example.logaspect.annotation.LogRecord)")public Object around(ProceedingJoinPoint joinPoint) throws Throwable {// 实现逻辑// ...}
}
8.3.6 创建全局日志切面(非注解方式)
package com.example.logaspect.aspect;import com.example.logaspect.annotation.LogRecord.LogLevel;
import com.example.logaspect.config.LogAspectProperties;
import com.example.logaspect.service.LogRecordService;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.core.annotation.Order;import java.lang.reflect.Method;/*** 全局日志切面(无需注解)*/
@Aspect
@Order(2)
public class GlobalLogAspect {private final LogRecordService logRecordService;private final LogAspectProperties properties;public GlobalLogAspect(LogRecordService logRecordService, LogAspectProperties properties) {this.logRecordService = logRecordService;this.properties = properties;}@Pointcut("execution(* *.*(..))")public void defaultPointcut() {}@Around("defaultPointcut()")public Object around(ProceedingJoinPoint joinPoint) throws Throwable {// 实现逻辑// ...}
}
8.3.7 创建自动配置类
package com.example.logaspect.config;import com.example.logaspect.aspect.AnnotationLogAspect;
import com.example.logaspect.aspect.GlobalLogAspect;
import com.example.logaspect.service.DefaultLogRecordServiceImpl;
import com.example.logaspect.service.LogRecordService;
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;/*** 日志切面自动配置类*/
@Configuration
@EnableConfigurationProperties(LogAspectProperties.class)
@ConditionalOnProperty(prefix = "logging.aspect", name = "enabled", havingValue = "true", matchIfMissing = true)
public class LogAspectAutoConfiguration {@Bean@ConditionalOnMissingBeanpublic LogRecordService logRecordService() {return new DefaultLogRecordServiceImpl();}@Bean@ConditionalOnProperty(prefix = "logging.aspect", name = "enable-annotation-aspect", havingValue = "true", matchIfMissing = true)public AnnotationLogAspect annotationLogAspect(LogRecordService logRecordService, LogAspectProperties properties) {return new AnnotationLogAspect(logRecordService, properties);}@Bean@ConditionalOnProperty(prefix = "logging.aspect", name = "enable-global-aspect", havingValue = "true", matchIfMissing = false)public GlobalLogAspect globalLogAspect(LogRecordService logRecordService, LogAspectProperties properties) {return new GlobalLogAspect(logRecordService, properties);}
}
8.3.8 创建 spring.factories 文件(springboot 2.x)
在 src/main/resources/META-INF/spring.factories 中添加:
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.example.logaspect.config.LogAspectAutoConfiguration
8.3.9 创建 META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports 文件(springboot 3.x)
在 Spring Boot 3.0 及以上版本中,spring.factories 方式已被弃用,取而代之的是使用 META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports 文件进行自动配置类的注册。
在 src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports 文件中添加:
com.example.logaspect.config.LogAspectAutoConfiguration
这个文件的格式很简单,每行一个配置类的全限定名,不需要键值对形式。这种方式加载更高效,减少了对不必要配置的解析,是 Spring Boot 3.x 推荐的自动配置注册方式。
如果你需要同时兼容 Spring Boot 2.x 和 3.x 版本,可以同时提供这两个文件,不会产生冲突:
META-INF/spring.factories- 用于 Spring Boot 2.xMETA-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports- 用于 Spring Boot 3.x
示例项目结构:
src/main/resources/
├── META-INF/
│ ├── spring.factories # Spring Boot 2.x
│ ├── spring/
│ │ └── org.springframework.boot.autoconfigure.AutoConfiguration.imports # Spring Boot 3.x
│ └── additional-spring-configuration-metadata.json
对于新项目,强烈建议直接使用 Spring Boot 3.x 的新方式,不仅加载效率更高,而且对于模块化和类路径扫描也有更好的支持。
8.4 如何使用日志切面 Starter
8.4.1 添加依赖
在需要使用日志切面的 Spring Boot 项目中添加依赖:
<dependency><groupId>com.example</groupId><artifactId>log-aspect-spring-boot-starter</artifactId><version>1.0.0</version>
</dependency>
8.4.2 注解方式使用
在需要记录日志的方法上添加 @LogRecord 注解:
import com.example.logaspect.annotation.LogRecord;
import org.springframework.web.bind.annotation.*;@RestController
@RequestMapping("/users")
public class UserController {@LogRecord(description = "获取用户信息")@GetMapping("/{id}")public User getUser(@PathVariable Long id) {// 方法实现return userService.getUserById(id);}@LogRecord(description = "创建用户",level = LogRecord.LogLevel.DEBUG,recordResult = false)@PostMappingpublic User createUser(@RequestBody User user) {// 方法实现return userService.createUser(user);}
}
8.4.3 非注解方式使用(全局切面)
在 application.properties 或 application.yml 中启用全局日志切面:
# 启用日志切面
logging.aspect.enabled=true# 启用全局日志切面
logging.aspect.enable-global-aspect=true# 设置全局切入点表达式,这里记录 service 包下所有方法的日志
logging.aspect.global-pointcut=execution(* com.example.service..*.*(..))# 排除不需要记录日志的包
logging.aspect.exclude-packages[0]=com.example.common
logging.aspect.exclude-packages[1]=com.example.util# 配置日志记录选项
logging.aspect.record-params=true
logging.aspect.record-result=true
logging.aspect.record-execution-time=true
8.5 日志输出示例
注解方式日志输出
2023-08-10 10:15:23.456 INFO --- [http-nio-8080-exec-1] com.example.DefaultLogRecordServiceImpl : 开始执行方法: com.example.controller.UserController.getUser | 描述: 获取用户信息 | 入参: [1]
2023-08-10 10:15:23.678 INFO --- [http-nio-8080-exec-1] com.example.DefaultLogRecordServiceImpl : 方法执行完成: com.example.controller.UserController.getUser | 耗时: 222毫秒 | 描述: 获取用户信息 | 返回值: {"id":1,"name":"张三","age":30,"email":"zhangsan@example.com"}
全局方式日志输出
2023-08-10 10:16:45.123 INFO --- [http-nio-8080-exec-2] com.example.DefaultLogRecordServiceImpl : 开始执行方法: com.example.service.impl.UserServiceImpl.getUserById | 入参: [2]
2023-08-10 10:16:45.234 INFO --- [http-nio-8080-exec-2] com.example.DefaultLogRecordServiceImpl : 方法执行完成: com.example.service.impl.UserServiceImpl.getUserById | 耗时: 111毫秒 | 返回值: {"id":2,"name":"李四","age":25,"email":"lisi@example.com"}
九、bug 解决
9.1 日志切面无法生效
如果日志切面无法生效,请检查以下几点:
- 确保日志切面类被正确扫描到
- 注意 springboot 版本,springboot 3.x 和 2.x 的配置方式有所不同(见 8.3)
9.2 log.debug 无输出内容
- springboot 中的 slf4j 框架默认的日志级别为 info,debug 日志级别低于 info
- 需要配置日志级别为 debug
logging.level.root=debug
相关文章:
springBoot_自定义starter
Spring Boot 自定义 Starter 详解 一、Spring Boot Starter 基础概念 1.1 什么是 Spring Boot Starter Spring Boot Starter 是 Spring Boot 的一个核心概念,它是一种特殊的依赖描述符,包含了一组可以集成到应用中的依赖项。简单来说,Star…...
搭建TypeScript单元测试环境
我们在学习TypeScript的时候如果能够搭建一个单元测试的环境,那写些demo会很简单,下面我们使用jest来搭建一个单元测试环境 Jest 是一个由 Facebook 开发并开源的 JavaScript 测试框架,被广泛应用于前端和 Node.js 项目的单元测试。以下是关…...
第十一届机械工程、材料和自动化技术国际会议(MMEAT 2025)
重要信息 官网:www.mmeat.net 时间:2025年06月23-25日 地点:中国-深圳 部分展示 征稿主题 智能制造和工业自动化 复合材料与高性能材料先进制造技术 自动化机器人系统 云制造与物联网集成 精密制造技术 智能生产线优化 实时数据分析与过…...
leetcode 1143. Longest Common Subsequence
目录 题目描述 第一步,明确并理解dp数组及下标的含义 第二步,分析明确并理解递推公式 第三步,理解dp数组如何初始化 第四步,理解遍历顺序 代码 题目描述 这道题和第718题的区别就是,本题求的是最长公共子序列的长…...
SSH 私钥文件权限控制指南
1. 为什么必须严格控制私钥文件权限? 安全机制:SSH 协议强制要求私钥文件必须仅对所有者可读,防止未授权访问。 风险:若权限过松(如其他用户可读),SSH 客户端会拒绝连接,避免私钥泄…...
stack和queue的学习
stack的介绍 stack的文档介绍 stack是一种容器适配器,专门用在具有后进先出操作的上下文环境中,其删除只能从容器的一端进行元素的插入与提取操作。stack是作为容器适配器被实现的,容器适配器即是对特定类封装作为其底层的容器,…...
微服务Nacos组件的介绍、安装、使用
微服务Nacos组件的介绍、安装、使用 在微服务架构日渐普及的今天,服务注册与配置管理成了系统架构中的关键环节。阿里巴巴开源的 Nacos(Naming and Configuration Service)正是解决这一问题的利器。本文将为你全面介绍 Nacos 的概念、安装方…...
SpringBoot_为何需要SpringBoot?
Spring Boot 出现前的开发困境 配置繁琐 大量的 XML 配置文件 Spring 是一个非常优秀的轻量级框架,但其配置却是重量级的需要编写大量的 XML 配置文件或注解配置,使项目配置复杂且难以维护配置文件中容易出现错误,且排查问题困难开发过程中…...
格式工厂 v5.18最新免安装绿色便携版
前言 用它来转视频的时候,还能顺便给那些有点小瑕疵的视频修修补补,保证转出来的视频质量杠杠的。更厉害的是,它不只是转换那么简单,还能帮你把PDF合并成一本小册子,视频也能合并成大片,还能随心所欲地裁剪…...
使用logrotate实现日志轮转
logrotate 是一个强大的 Linux 工具,用于自动化管理日志文件的轮转、压缩、删除和归档。它能有效防止日志文件无限增长,节省磁盘空间,同时保持日志的可追溯性。以下是详细讲解 logrotate 的用法,涵盖安装、配置、测试、自动化、常…...
MQTTX + MCP:MQTT 客户端秒变物联网 Agent
引言:MQTTX 与 MCP 的融合 作为最受欢迎的 MQTT 客户端工具,MQTTX 在 1.12.0 beta 版本中集成了模型上下文协议(MCP)到 Copilot AI 功能中,显著提升了服务能力。这一融合让 MQTTX 转变为 MCP Host(也就是发…...
redis close+连接参数设置+并发情况风险分析
1,如下代码 redis 为什么 client.close,不关闭会出现什么问题 public void confirm(String token, MenuHistoryVO menuHistoryVO) {if (StringUtil.isEmptyOrNull(token) || Objects.isNull(menuHistoryVO)) {return;}String key getKey(token);JedisC…...
[密码学实战]GMT 0048-2016智能密码钥匙密码检测规范技术解析与实战指南
GMT 0048-2016智能密码钥匙密码检测规范技术解析与实战指南 引言 随着信息安全需求的升级,智能密码钥匙(如USB Key、安全芯片等)作为密码运算和密钥管理的核心设备,广泛应用于金融、政务、物联网等领域。中国国家密码管理局发布…...
Android OkHttp 框架的使用与源码、原理解析
一、引言 在如今的移动应用开发领域,网络交互已经成为了绝大多数 Android 应用不可或缺的一部分。无论是获取最新的资讯内容、同步用户数据,还是与后端服务器进行实时通信,高效且稳定的网络请求都是保障应用良好用户体验的基石。而 OkHttp 框…...
Qt C++ 解析和处理 XML 文件示例
使用 Qt C 解析和处理 XML 文件 以下是使用 Qt C 实现 XML 文件处理的几种方法,包括解析、创建和修改 XML 文件。 1. 使用 QXmlStreamReader (推荐方式) #include <QFile> #include <QXmlStreamReader> #include <QDebug>void parseXmlWithStr…...
快手砍掉本地生活的门槛
一场本地商家的效率革命。 作者|景行 编辑|杨舟 “两斤鸡翅根七块九,两盒蓝莓九块钱,两公斤卫生纸十四块九一提。” 这是朝阳佳惠超市,在快手一则普通的短视频内容。 佳惠超市在辽宁省朝阳市有22家分店,打开佳惠超市的相关快手…...
Python基础语法3
目录 1、函数 1.1、语法格式 1.2、函数返回值 1.3、变量作用域 1.4、执行过程 1.5、链式调用 1.6、嵌套调用 1.7、函数递归 1.8、参数默认值 1.9、关键字参数 2、列表 2.1、创建列表 2.2、下标访问 2.3、切片操作 2.4、遍历列表元素 2.5、新增元素 2.6、查找元…...
Vue 3中如何封装API请求:提升开发效率的最佳实践
在现代前端开发中,API请求是不可避免的一部分,尤其是与后端交互时。随着Vue 3的广泛应用,如何高效地封装API请求,既能提升代码的可维护性,又能确保代码的高复用性,成为了很多开发者关注的话题。 在本文中&…...
GitHub 趋势日报 (2025年04月17日)
本日报由 TrendForge 系统生成 https://trendforge.devlive.org/ 📈 今日整体趋势 Top 10 排名项目名称项目描述今日获星总星数语言1Anduin2017/HowToCook程序员在家做饭方法指南。Programmer’s guide about how to cook at home (Simplified Chinese onl…⭐ 224…...
第5章-1 优化服务器设置
上一篇:《第4章-5 linux 网络管理》,接着服务器设置 本章我们将解释如何为MySQL服务器创建合适的配置文件。这是一个迂回的旅程,有许多兴趣点和可以俯瞰风景的短途旅程。这些短途旅程是必要的。确定合适配置的最短路径并不是从研究配置选项并…...
【AI】Windows环境安装SPAR3D单图三维重建心得
效果一览 左图为原始单个图像,右图为通过SPAR3D重建后的三维建模,可以看出效果还是不错的。 本地环境配置 系统:Windows 11 专业版CPU:i5-13400F内存:32GBGPU:RTX3060 12GBcuda:11.8conda&…...
桌面应用UI开发方案
一、基于 Web 技术的跨平台方案 Electron Python/Go 特点: 技术栈:前端使用 HTML/CSS/JS,后端通过 Node.js 集成 Python/Go 模块或服务。 跨平台:支持 Windows、macOS、Linux 桌面端,适合开发桌面应用。 生态成熟&…...
使用docker在manjaro linux系统上运行windows和ubuntu
因为最近项目必须要使用指定版本的solidworks和maxwell(都只能在win系统上使用), 且目前的ubuntu容器是没有桌面的,导致我运行不了一些带图形的ros2功能。无奈之下,决定使用docker-compose写一下配置文件,彻底解决问题…...
Agent智能体ReAct机制深度解读:推理与行动的完美闭环
一、从Chain-of-Thought到ReAct的范式演进 1.1 传统决策机制的局限 #mermaid-svg-Jf3ygvgHcGciJvX8 {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-Jf3ygvgHcGciJvX8 .error-icon{fill:#552222;}#mermaid-svg-Jf3y…...
在 Node.js 中使用原生 `http` 模块,获取请求的各个部分:**请求行、请求头、请求体、请求路径、查询字符串** 等内容
在 Node.js 中使用原生 http 模块,可以通过 req 对象来获取请求的各个部分:请求行、请求头、请求体、请求路径、查询字符串 等内容。 ✅ 一、基础结构 const http require(http); const url require(url);const server http.createServer((req, res)…...
Redis(01)Redis连接报错Redis is running in protected mode……的解决方案
一、引言:从一个典型连接错误说起 在分布式系统开发中,Redis 作为高性能缓存中间件被广泛使用。 然而,当我们首次部署 Redis 并尝试从外部客户端连接时,常常会遇到以下错误: DENIED Redis is running in protected m…...
18487.1-2015-解读笔记之四-交流充电之流程分析
前面简单分析了国标交流充电桩插枪监测逻辑和PWM控制逻辑,下面简单分析一下交流充电流程 附录A 交流充电连接过程和控制时序如下: 由此可以将充电流程大概分为几个阶段: 1.充电连接阶段 充电连接阶段CC(电阻由无穷大到R4RC&…...
海外服务器安装Ubuntu 22.04图形界面并配置VNC远程访问指南
在云计算和远程工作日益普及的今天,如何高效地管理和使用海外服务器成为了一个热门话题。本文将详细介绍如何在海外的Ubuntu 22.04服务器上安装图形界面,并配置VNC服务来实现远程访问。无论您是开发者、系统管理员,还是只是想要更便捷地管理您的海外服务器,这篇指南都能为您…...
Linux 管道理解
一、什么是管道 1.1 unix中最古老的进程间通信 1.2 一个进程链接到另一个进程的数据流称为“管道”: 图解: 二、管道通信的原理 2.1当我们创建一个进程然后打开一个文件的时候 会经过以下步骤: ①首先要描述这个进程,为这个…...
国产RK3568+FPGA以 “实时控制+高精度采集+灵活扩展” 为核心的解决方案
RK3568FPGA方案在工业领域应用的核心优势 一、实时性与低延迟控制 AMP架构与GPIO中断技术 通过非对称多处理架构(AMP)实现Linux与实时操作系统(RTOS/裸机)协同,主核负责调度,从核通过GPIO中断响应紧…...
