springBoot_自定义starter

news2025/4/24 20:38:06

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 {

    @Autowired
    private 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();

    @Override
    public 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);
    }

    @Override
    public 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);
    }

    @Override
    public 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
    @ConditionalOnMissingBean
    public 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
    )
    @PostMapping
    public 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

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

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

相关文章

搭建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;本题求的是最长公共子序列的长…

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;还能随心所欲地裁剪…

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

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

快手砍掉本地生活的门槛

一场本地商家的效率革命。 作者|景行 编辑|杨舟 “两斤鸡翅根七块九&#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、查找元…

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

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

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

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

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&…

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中断响应紧…

Pycharm(十五)面向对象程序设计基础

目录 一、面向对象基本概述 class 类名: 属性(类似于定义变量) 行为(类似于定义函数,只不过第一个形参要写self) 二、self关键字介绍 三、在类内部调用类中的函数 四、属性的定义和调用 五、魔法方法init方法 六、魔法方法str和del方法 七、案例-减肥 一、…

华三(H3C)与华为(Huawei)设备配置IPsec VPN的详细说明,涵盖配置流程、参数设置及常见问题处理

以下是针对华三&#xff08;H3C&#xff09;与华为&#xff08;Huawei&#xff09;设备配置IPsec VPN的详细说明&#xff0c;涵盖配置流程、参数设置及常见问题处理&#xff1a; 一、华三&#xff08;H3C&#xff09;设备IPsec VPN配置详解 1. 配置流程 华三IPsec VPN配置主要…

店匠科技摘得 36 氪“2025 AI Partner 创新大奖”

全场景 AI 方案驱动跨境电商数智化跃迁 4 月 18 日,36 氪 2025 AI Partner 大会于上海盛大开幕。大会紧扣“Super App 来了”主题,全力探寻 AI 时代的全新变量,探索 AI 领域下一个超级应用的无限可能性。在此次大会上,跨境电商独立站 SaaS 平台店匠科技(Shoplazza)凭借“店匠跨…

Joint communication and state sensing under logarithmic loss

摘要——我们研究一种基本的联合通信与感知设置&#xff0c;其中发射机希望向接收机传输一条消息&#xff0c;并同时通过广义反馈估计其信道状态。我们假设感知目标是获得状态的软估计&#xff08;即概率分布&#xff09;&#xff0c;而非通常假设的点估计&#xff1b;并且我们…