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

springBoot_自定义starter

Spring Boot 自定义 Starter 详解

一、Spring Boot Starter 基础概念

1.1 什么是 Spring Boot Starter

Spring Boot Starter 是 Spring Boot 的一个核心概念,它是一种特殊的依赖描述符,包含了一组可以集成到应用中的依赖项。简单来说,Starter 就是一组预配置的依赖项,使开发者能够快速地将特定功能添加到 Spring Boot 应用中,而无需手动管理每个单独的依赖及其版本。

1.2 Starter 的核心价值

  1. 简化依赖管理

    • 一个 Starter 通常会引入多个必要的依赖,这些依赖已经过版本兼容性测试
    • 开发者只需引入一个 Starter,而不是手动管理多个独立依赖及其版本关系
  2. 自动配置

    • Starter 不仅仅提供依赖,还通常包含自动配置类
    • 能根据应用环境自动配置 Bean,减少手动配置的工作量
  3. 约定大于配置

    • 遵循"约定大于配置"的设计理念,提供合理的默认配置
    • 开发者只需关注与默认行为不同的部分

二、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 的强大之处在于它们的自动配置能力,主要通过以下方式实现:

  1. @EnableAutoConfiguration 注解

    • Spring Boot 应用通过 @SpringBootApplication(其中包含 @EnableAutoConfiguration)启用自动配置
    • 该注解触发自动配置的加载过程
  2. spring.factories 文件

    • 每个 Starter 通常在 META-INF/spring.factories 文件中声明其自动配置类
    • Spring Boot 在启动时会扫描 classpath 下所有的 spring.factories 文件
    • 加载其中声明的自动配置类
  3. 条件化配置

    • 自动配置类使用 @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 遵循特定的命名模式:

  • 官方 Starterspring-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 项目分为两个模块:

  1. 自动配置模块:包含自动配置代码
  2. 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 最佳实践

  1. 提供明确的文档

    • 详细说明 Starter 的功能
    • 列出所有可配置的属性及其默认值
    • 提供使用示例
  2. 遵循命名约定

    • 第三方 Starter 使用 xxx-spring-boot-starter 命名
  3. 设计合理的默认值

    • 默认配置应适合大多数使用场景
    • 同时允许灵活定制
  4. 合理使用条件注解

    • 避免与其他配置冲突
    • 允许用户覆盖默认行为

七、Starter 的优势总结

  1. 大幅减少配置工作:通过自动配置减少样板代码
  2. 提供经过验证的依赖组合:解决依赖冲突和版本不兼容问题
  3. 简化技术集成:使不同技术栈的整合变得更加容易
  4. 增强可维护性:依赖更新时只需更新 Starter 版本
  5. 加速开发过程:开发者可以专注于业务逻辑而非基础设施配置

八、自定义日志切面 Starter 实战案例

本部分将介绍如何创建一个自定义的日志切面 Starter,用于在 Spring Boot 应用中自动记录方法调用日志,包括注解方式和非注解方式两种实现。
代码地址

8.1 整体方案设计

我们将创建一个名为 log-aspect-spring-boot-starter 的自定义 Starter,它能够:

  1. 自动记录方法的调用信息(入参、返回值、执行时间等)
  2. 支持通过注解指定需要记录日志的方法
  3. 支持全局配置,无需注解也能记录日志
  4. 允许自定义日志格式和输出方式

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 版本,可以同时提供这两个文件,不会产生冲突:

  1. META-INF/spring.factories - 用于 Spring Boot 2.x
  2. META-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.propertiesapplication.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 日志切面无法生效

如果日志切面无法生效,请检查以下几点:

  1. 确保日志切面类被正确扫描到
  2. 注意 springboot 版本,springboot 3.x 和 2.x 的配置方式有所不同(见 8.3)

9.2 log.debug 无输出内容

  1. springboot 中的 slf4j 框架默认的日志级别为 info,debug 日志级别低于 info
  2. 需要配置日志级别为 debug
logging.level.root=debug

相关文章:

springBoot_自定义starter

Spring Boot 自定义 Starter 详解 一、Spring Boot Starter 基础概念 1.1 什么是 Spring Boot Starter Spring Boot Starter 是 Spring Boot 的一个核心概念&#xff0c;它是一种特殊的依赖描述符&#xff0c;包含了一组可以集成到应用中的依赖项。简单来说&#xff0c;Star…...

搭建TypeScript单元测试环境

我们在学习TypeScript的时候如果能够搭建一个单元测试的环境&#xff0c;那写些demo会很简单&#xff0c;下面我们使用jest来搭建一个单元测试环境 Jest 是一个由 Facebook 开发并开源的 JavaScript 测试框架&#xff0c;被广泛应用于前端和 Node.js 项目的单元测试。以下是关…...

第十一届机械工程、材料和自动化技术国际会议(MMEAT 2025)

重要信息 官网&#xff1a;www.mmeat.net 时间&#xff1a;2025年06月23-25日 地点&#xff1a;中国-深圳 部分展示 征稿主题 智能制造和工业自动化 复合材料与高性能材料先进制造技术 自动化机器人系统 云制造与物联网集成 精密制造技术 智能生产线优化 实时数据分析与过…...

leetcode 1143. Longest Common Subsequence

目录 题目描述 第一步&#xff0c;明确并理解dp数组及下标的含义 第二步&#xff0c;分析明确并理解递推公式 第三步&#xff0c;理解dp数组如何初始化 第四步&#xff0c;理解遍历顺序 代码 题目描述 这道题和第718题的区别就是&#xff0c;本题求的是最长公共子序列的长…...

SSH 私钥文件权限控制指南

1. 为什么必须严格控制私钥文件权限&#xff1f; 安全机制&#xff1a;SSH 协议强制要求私钥文件必须仅对所有者可读&#xff0c;防止未授权访问。 风险&#xff1a;若权限过松&#xff08;如其他用户可读&#xff09;&#xff0c;SSH 客户端会拒绝连接&#xff0c;避免私钥泄…...

stack和queue的学习

stack的介绍 stack的文档介绍 stack是一种容器适配器&#xff0c;专门用在具有后进先出操作的上下文环境中&#xff0c;其删除只能从容器的一端进行元素的插入与提取操作。stack是作为容器适配器被实现的&#xff0c;容器适配器即是对特定类封装作为其底层的容器&#xff0c;…...

微服务Nacos组件的介绍、安装、使用

微服务Nacos组件的介绍、安装、使用 在微服务架构日渐普及的今天&#xff0c;服务注册与配置管理成了系统架构中的关键环节。阿里巴巴开源的 Nacos&#xff08;Naming and Configuration Service&#xff09;正是解决这一问题的利器。本文将为你全面介绍 Nacos 的概念、安装方…...

SpringBoot_为何需要SpringBoot?

Spring Boot 出现前的开发困境 配置繁琐 大量的 XML 配置文件 Spring 是一个非常优秀的轻量级框架&#xff0c;但其配置却是重量级的需要编写大量的 XML 配置文件或注解配置&#xff0c;使项目配置复杂且难以维护配置文件中容易出现错误&#xff0c;且排查问题困难开发过程中…...

格式工厂 v5.18最新免安装绿色便携版

前言 用它来转视频的时候&#xff0c;还能顺便给那些有点小瑕疵的视频修修补补&#xff0c;保证转出来的视频质量杠杠的。更厉害的是&#xff0c;它不只是转换那么简单&#xff0c;还能帮你把PDF合并成一本小册子&#xff0c;视频也能合并成大片&#xff0c;还能随心所欲地裁剪…...

使用logrotate实现日志轮转

logrotate 是一个强大的 Linux 工具&#xff0c;用于自动化管理日志文件的轮转、压缩、删除和归档。它能有效防止日志文件无限增长&#xff0c;节省磁盘空间&#xff0c;同时保持日志的可追溯性。以下是详细讲解 logrotate 的用法&#xff0c;涵盖安装、配置、测试、自动化、常…...

MQTTX + MCP:MQTT 客户端秒变物联网 Agent

引言&#xff1a;MQTTX 与 MCP 的融合 作为最受欢迎的 MQTT 客户端工具&#xff0c;MQTTX 在 1.12.0 beta 版本中集成了模型上下文协议&#xff08;MCP&#xff09;到 Copilot AI 功能中&#xff0c;显著提升了服务能力。这一融合让 MQTTX 转变为 MCP Host&#xff08;也就是发…...

redis close+连接参数设置+并发情况风险分析

1&#xff0c;如下代码 redis 为什么 client.close&#xff0c;不关闭会出现什么问题 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智能密码钥匙密码检测规范技术解析与实战指南 引言 随着信息安全需求的升级&#xff0c;智能密码钥匙&#xff08;如USB Key、安全芯片等&#xff09;作为密码运算和密钥管理的核心设备&#xff0c;广泛应用于金融、政务、物联网等领域。中国国家密码管理局发布…...

Android OkHttp 框架的使用与源码、原理解析

一、引言 在如今的移动应用开发领域&#xff0c;网络交互已经成为了绝大多数 Android 应用不可或缺的一部分。无论是获取最新的资讯内容、同步用户数据&#xff0c;还是与后端服务器进行实时通信&#xff0c;高效且稳定的网络请求都是保障应用良好用户体验的基石。而 OkHttp 框…...

Qt C++ 解析和处理 XML 文件示例

使用 Qt C 解析和处理 XML 文件 以下是使用 Qt C 实现 XML 文件处理的几种方法&#xff0c;包括解析、创建和修改 XML 文件。 1. 使用 QXmlStreamReader (推荐方式) #include <QFile> #include <QXmlStreamReader> #include <QDebug>void parseXmlWithStr…...

快手砍掉本地生活的门槛

一场本地商家的效率革命。 作者|景行 编辑|杨舟 “两斤鸡翅根七块九&#xff0c;两盒蓝莓九块钱&#xff0c;两公斤卫生纸十四块九一提。” 这是朝阳佳惠超市&#xff0c;在快手一则普通的短视频内容。 佳惠超市在辽宁省朝阳市有22家分店&#xff0c;打开佳惠超市的相关快手…...

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请求:提升开发效率的最佳实践

在现代前端开发中&#xff0c;API请求是不可避免的一部分&#xff0c;尤其是与后端交互时。随着Vue 3的广泛应用&#xff0c;如何高效地封装API请求&#xff0c;既能提升代码的可维护性&#xff0c;又能确保代码的高复用性&#xff0c;成为了很多开发者关注的话题。 在本文中&…...

GitHub 趋势日报 (2025年04月17日)

本日报由 TrendForge 系统生成 https://trendforge.devlive.org/ &#x1f4c8; 今日整体趋势 Top 10 排名项目名称项目描述今日获星总星数语言1Anduin2017/HowToCook程序员在家做饭方法指南。Programmer’s guide about how to cook at home (Simplified Chinese onl…⭐ 224…...

第5章-1 优化服务器设置

上一篇&#xff1a;《第4章-5 linux 网络管理》&#xff0c;接着服务器设置 本章我们将解释如何为MySQL服务器创建合适的配置文件。这是一个迂回的旅程&#xff0c;有许多兴趣点和可以俯瞰风景的短途旅程。这些短途旅程是必要的。确定合适配置的最短路径并不是从研究配置选项并…...

【AI】Windows环境安装SPAR3D单图三维重建心得

效果一览 左图为原始单个图像&#xff0c;右图为通过SPAR3D重建后的三维建模&#xff0c;可以看出效果还是不错的。 本地环境配置 系统&#xff1a;Windows 11 专业版CPU&#xff1a;i5-13400F内存&#xff1a;32GBGPU&#xff1a;RTX3060 12GBcuda&#xff1a;11.8conda&…...

桌面应用UI开发方案

一、基于 Web 技术的跨平台方案 Electron Python/Go 特点&#xff1a; 技术栈&#xff1a;前端使用 HTML/CSS/JS&#xff0c;后端通过 Node.js 集成 Python/Go 模块或服务。 跨平台&#xff1a;支持 Windows、macOS、Linux 桌面端&#xff0c;适合开发桌面应用。 生态成熟&…...

使用docker在manjaro linux系统上运行windows和ubuntu

因为最近项目必须要使用指定版本的solidworks和maxwell&#xff08;都只能在win系统上使用&#xff09;, 且目前的ubuntu容器是没有桌面的&#xff0c;导致我运行不了一些带图形的ros2功能。无奈之下&#xff0c;决定使用docker-compose写一下配置文件&#xff0c;彻底解决问题…...

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 模块&#xff0c;可以通过 req 对象来获取请求的各个部分&#xff1a;请求行、请求头、请求体、请求路径、查询字符串 等内容。 ✅ 一、基础结构 const http require(http); const url require(url);const server http.createServer((req, res)…...

Redis(01)Redis连接报错Redis is running in protected mode……的解决方案

一、引言&#xff1a;从一个典型连接错误说起 在分布式系统开发中&#xff0c;Redis 作为高性能缓存中间件被广泛使用。 然而&#xff0c;当我们首次部署 Redis 并尝试从外部客户端连接时&#xff0c;常常会遇到以下错误&#xff1a; DENIED Redis is running in protected m…...

18487.1-2015-解读笔记之四-交流充电之流程分析

前面简单分析了国标交流充电桩插枪监测逻辑和PWM控制逻辑&#xff0c;下面简单分析一下交流充电流程 附录A 交流充电连接过程和控制时序如下&#xff1a; 由此可以将充电流程大概分为几个阶段&#xff1a; 1.充电连接阶段 充电连接阶段CC&#xff08;电阻由无穷大到R4RC&…...

海外服务器安装Ubuntu 22.04图形界面并配置VNC远程访问指南

在云计算和远程工作日益普及的今天,如何高效地管理和使用海外服务器成为了一个热门话题。本文将详细介绍如何在海外的Ubuntu 22.04服务器上安装图形界面,并配置VNC服务来实现远程访问。无论您是开发者、系统管理员,还是只是想要更便捷地管理您的海外服务器,这篇指南都能为您…...

Linux 管道理解

一、什么是管道 1.1 unix中最古老的进程间通信 1.2 一个进程链接到另一个进程的数据流称为“管道”&#xff1a; 图解&#xff1a; 二、管道通信的原理 2.1当我们创建一个进程然后打开一个文件的时候 会经过以下步骤&#xff1a; ①首先要描述这个进程&#xff0c;为这个…...

国产RK3568+FPGA以 ‌“实时控制+高精度采集+灵活扩展”‌ 为核心的解决方案

RK3568FPGA方案在工业领域应用的核心优势 一、‌实时性与低延迟控制‌ ‌AMP架构与GPIO中断技术‌ 通过非对称多处理架构&#xff08;AMP&#xff09;实现Linux与实时操作系统&#xff08;RTOS/裸机&#xff09;协同&#xff0c;主核负责调度&#xff0c;从核通过GPIO中断响应紧…...