新版SpringBoot无法主动读取bootstrap.yml的原因和解决方法

 更新时间:2025年01月10日 09:29:15   作者:小白的一叶扁舟  
在使用新版 Springboot 搭建微服务时 发现配置数据源失败,依赖、配置、注解等所示所有均为正确,所以本文给大家介绍了新版SpringBoot无法主动读取bootstrap.yml的原因和解决方案,需要的朋友可以参考下

技术栈:

Springboot 2024.0.0 + MyBatisPlus3 + MySql8 + Hikari连接池

前言:

在使用新版 Springboot 搭建微服务时 发现配置数据源失败(Failed to configure a DataSource: ‘url‘ attribute is not specified and no emb)如下图依赖 配置 注解等所示 所有均为正确

注意:

因追踪代码过长 所以放在最后方 有兴趣的可以看一下

相关依赖如下:

<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>3.4.1</version>
    <relativePath/>
</parent>

<dependencies>
    <!-- MySQL Connector -->
    <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
        <version>8.0.31</version>
    </dependency>
    <!-- Mybatis-Plus -->
    <dependency>
        <groupId>com.baomidou</groupId>
        <artifactId>mybatis-plus-boot-starter</artifactId>
        <version>3.5.9</version>
    </dependency>
</dependencies>

相关配置如下:

# DataSource 配置
spring:
  application:
    name: ai-grading-base # 服务名 将在 Eureka 中注册
  datasource:
    url: jdbc:mysql://localhost:3306/xxx-xxx?useUnicode=true&characterEncoding=UTF-8&useSSL=false
    driver-class-name: com.mysql.cj.jdbc.Driver
    username: xxx
    password: yyy
    hikari:
      maximum-pool-size: 10  # 设置最大连接池大小
      minimum-idle: 5        # 最小空闲连接数
      idle-timeout: 30000    # 空闲连接的最大存活时间 单位:毫秒
      max-lifetime: 600000   # 连接最大生命周期 单位:毫秒
      connection-timeout: 30000  # 连接超时时间 单位:毫秒
      validation-timeout: 5000    # 校验连接的超时时间 单位:毫秒
      leak-detection-threshold: 15000  # 连接泄漏检测阈值 单位:毫秒
      pool-name: HikariCP      # 连接池的名称
      auto-commit: true        # 是否启用自动提交

# MyBatis 配置
mybatis-plus:
  mapper-locations: classpath:mapper/*Mapper.xml
  type-aliases-package: xxx.xxx.xxx.pojo  
  configuration:
    map-underscore-to-camel-case: true
    #开启日志打印
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl

启动类注解:

@MapperScan("xxx.xx.xxx.mapper")
@SpringBootApplication
public class AiGradingBaseApplication {
    public static void main(String[] args) {
        SpringApplication.run(AiGradingBaseApplication.class, args);
    }
}

配置数据源失败(Failed to configure a DataSource: ‘url‘ attribute is not specified and no emb)

开发场景:

采用 Mysql 8 配置 bootstrap.yml 失败

报错原因:

在 Spring Boot 2.4 之前 配置文件的加载顺序是按照以下顺序进行的:

  1. application.properties
  2. application.yml
  3. bootstrap.properties
  4. bootstrap.yml

其中 bootstrap.yml 的加载优先级大于 application.yml 因此如果同一个配置项在两个文件中都有定义 bootstrap.yml 中的值会覆盖 application.yml 中的值

配置文件加载优先级

  • bootstrap.yml: 用于在应用程序启动时加载应用上下文之前的配置 通常用于配置服务发现 配置中心等
  • application.yml: 用于在应用程序上下文加载之后加载配置 通常用于业务相关的配置

关键点:

  • bootstrap.yml 在 Spring Boot 2.4 之前的版本中有更高的优先级 因此如果配置在 bootstrap.yml 和 application.yml 中重复定义 bootstrap.yml 中的配置会生效
  • 从 Spring Boot 2.4 开始 bootstrap.yml 已经被移除 并且将其功能移交给 application.yml 和 application.properties Spring Cloud 配置被集成到 application.yml 中
  • 如果使用的是 Spring Boot 2.4 之后的版本 应该最好禁用 bootstrap.yml 改用 application.yml 来进行配置

主要解决方案一:将 bootstrap.yml 配置移动至 application.yml 既可

主要解决方案二:认真检查MySql(datascoure)配置文件是否有误 特别是Tab缩进等 可参考上方贴出的配置

主要解决方案三:使用 spring.cloud.bootstrap.enabled 属性启用引导配置

bootstrap.yml 文件的引导配置仍然可以通过设置 spring.cloud.bootstrap.enabled 属性来启用 如果该属性设置为 true Spring Cloud 将尝试加载 bootstrap.yml 文件 并将其中的配置应用于应用程序的环境中

关键步骤:

  • 确保 spring.cloud.bootstrap.enabled=true 配置被激活 可以通过系统属性 环境变量或配置文件中设置
  • bootstrap.yml 会被自动加载并包含在 Spring 环境中

例如 可以在 application.properties 或 application.yml 中配置:

spring.cloud.bootstrap.enabled=true

或者通过环境变量或系统属性传递:

-Dspring.cloud.bootstrap.enabled=true

代码层面:

在新版 Spring Cloud 中 PropertyUtils.bootstrapEnabled() 方法会检查 spring.cloud.bootstrap.enabled 属性的值以及 Marker 类的存在 从而决定是否启用 bootstrap 配置

public static boolean bootstrapEnabled(Environment environment) {
    return (Boolean) environment.getProperty("spring.cloud.bootstrap.enabled", Boolean.class, false) || MARKER_CLASS_EXISTS;
}

主要解决方案四:读取 bootstrap.yml 配置文件

在新版 Spring Cloud 中 bootstrap.yml 文件可以直接通过 Spring 的配置机制加载 通常无需手动指定文件路径 Spring Cloud 会根据 spring.cloud.bootstrap.enabled 属性的设置决定是否加载 bootstrap.yml 或 bootstrap.properties 文件

典型的 bootstrap.yml 配置文件:

spring:
  cloud:
    config:
      uri: http://localhost:8888
    bootstrap:
      enabled: true

该配置告诉 Spring Cloud Config Client 从指定的 Config Server 获取配置 并在应用程序启动时加载

主要解决方案五: 使用 ConfigurableEnvironment 和 ApplicationContext 读取引导配置

在 Spring Cloud 中 可以通过 ConfigurableEnvironment 或 ApplicationContext 来访问和读取 bootstrap.yml 配置文件中的属性

ConfigurableEnvironment environment = event.getEnvironment();
String configUri = environment.getProperty("spring.cloud.config.uri", String.class, "http://localhost:8888");

通过这种方式 可以读取 bootstrap.yml 文件中配置的其他属性(如 spring.cloud.config.uri) 并将其用于应用程序的初始化过程

主要解决方案六: 手动加载 bootstrap.yml 文件(如果需要)

在一些特定的应用场景下 可能需要手动加载 bootstrap.yml 文件 Spring 提供了多种方式加载外部配置文件 包括使用 @PropertySource 注解或者 Environment 对象 此方法一般用于需要手动控制文件加载顺序或路径的情况

@Configuration
@PropertySource("classpath:/bootstrap.yml")
public class BootstrapConfig {
    @Value("${spring.cloud.config.uri}")
    private String configUri;
}

主要解决方案七: 通过 @EnableConfigurationProperties 绑定配置

如果希望将 bootstrap.yml 配置文件中的属性绑定到一个配置类中 可以使用 @EnableConfigurationProperties 注解来启用属性绑定 这种方式在新版中也非常常见

@Configuration
@EnableConfigurationProperties(ConfigProperties.class)
public class ConfigBootstrap {
    // 自动注入配置类
    @Autowired
    private ConfigProperties configProperties;
}

其中 ConfigProperties 类可以定义从 bootstrap.yml 文件中读取的配置属性:

@ConfigurationProperties(prefix = "spring.cloud.config")
public class ConfigProperties {
    private String uri;

    // Getter and Setter
}

主要解决方案八: 通过 ApplicationContextInitializer 初始化 bootstrap.yml 配置

Spring Cloud 使用了 ApplicationContextInitializer 来初始化 Spring 应用上下文 在 BootstrapApplicationListener 中 ApplicationContextInitializer 被用来处理 bootstrap.yml 配置的加载和处理

如果你需要定制加载逻辑 可以通过实现自己的 ApplicationContextInitializer 来进一步控制 bootstrap.yml 的加载顺序和方式:

public class CustomBootstrapApplicationContextInitializer implements ApplicationContextInitializer<ConfigurableApplicationContext> {
    @Override
    public void initialize(ConfigurableApplicationContext applicationContext) {
        // 自定义初始化逻辑 处理 bootstrap 配置
    }
}

并在 SpringApplication 启动时注册这个初始化器:

SpringApplication app = new SpringApplication(MyApplication.class);
app.addInitializers(new CustomBootstrapApplicationContextInitializer());
app.run(args);

主要解决方案九: 使用 SpringApplication 配置程序的监听器

Spring Cloud 使用了 BootstrapApplicationListener 来处理 bootstrap.yml 文件的加载和应用配置 它在 ApplicationEnvironmentPreparedEvent 事件中触发 并将配置应用到 Spring 环境

public class BootstrapApplicationListener implements ApplicationListener<ApplicationEnvironmentPreparedEvent>, Ordered {
    public void onApplicationEvent(ApplicationEnvironmentPreparedEvent event) {
        ConfigurableEnvironment environment = event.getEnvironment();
        if (PropertyUtils.bootstrapEnabled(environment)) {
            // 初始化 bootstrap 上下文 加载 bootstrap.yml 配置
        }
    }
}

其余解决方案一:添加节点 保证文件被正常被扫描并成功加载(以 SpringBoot 3.4.1 举例 官方已在依赖中声明 resources 中内容 所以不用单独声明)

<build>
    <finalName>xx-xx-xx</finalName>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    <resources>
        <resource>
            <directory>src/main/java</directory>
            <includes>
            <include>/*.yml</include>
            <include>/*.properties</include>
            <include>/*.xml</include>
            </includes>
            <filtering>false</filtering>
        </resource>
        <resource>
            <directory>src/main/resources</directory>
            <includes>
            <include>/*.yml</include>
            <include>/*.properties</include>
            <include>/*.xml</include>
            </includes>
            <filtering>false</filtering>
        </resource>
    </resources>
</build>

其余方式二(除特殊场景外 其余皆不推荐):在启用类上添加注解声明不需要加载数据源(DataSourceAutoConfiguration)

@MapperScan("xxx.xxx.xxx.mapper")
@SpringBootApplication(exclude= {DataSourceAutoConfiguration.class})
public class AiGradingBaseApplication {
    public static void main(String[] args) {
        SpringApplication.run(AiGradingBaseApplication.class, args);
    }
}

其余方式三(除特殊场景外 其余皆不推荐):在配置文件中声明不需要加载数据源(DataSourceAutoConfiguration)跟方式二同理 只是操作方式不一致

spring:
 autoconfigure:
 	exclude: org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration

源码追踪:

前言:因 Springboot 旧版及新版中针对于 bootstrap.yml 文件加载进行重构

官方:

Config First Bootstrap
To use the legacy bootstrap way of connecting to Config Server, bootstrap must be enabled via a property or the spring-cloud-starter-bootstrap starter. The property is spring.cloud.bootstrap.enabled=true. It must be set as a System Property or environment variable. Once bootstrap has been enabled any application with Spring Cloud Config Client on the classpath will connect to Config Server as follows: When a config client starts, it binds to the Config Server (through the spring.cloud.config.uri bootstrap configuration property) and initializes Spring Environment with remote property sources.

The net result of this behavior is that all client applications that want to consume the Config Server need a bootstrap.yml (or an environment variable) with the server address set in spring.cloud.config.uri (it defaults to "http://localhost:8888").

翻译:

配置引导方式(Config First Bootstrap)
要使用传统的引导方式连接到 Config Server 必须通过属性或 spring-cloud-starter-bootstrap 启动器来启用引导 相关的属性是 spring.cloud.bootstrap.enabled=true 该属性必须作为系统属性或环境变量进行设置 一旦启用了引导 任何包含 Spring Cloud Config Client 的应用程序都会按照以下方式连接到 Config Server:
当一个配置客户端启动时 它会绑定到 Config Server(通过 spring.cloud.config.uri 引导配置属性)并使用远程属性源初始化 Spring 环境 
这种行为的最终结果是 所有希望使用 Config Server 的客户端应用程序都需要一个包含服务器地址配置的 bootstrap.yml 文件(或者通过环境变量设置) 该配置项为 spring.cloud.config.uri(默认为 "http://localhost:8888") 

简述:

在 Spring Cloud 的早期版本中 bootstrap.yml 是配置 Spring Cloud Config Client 的关键文件 要启用传统的引导方式连接到 Config Server 必须通过 spring.cloud.bootstrap.enabled=true 属性或通过 spring-cloud-starter-bootstrap 启动器来启用引导

传统引导方式:

  • 配置属性: spring.cloud.bootstrap.enabled=true 必须作为系统属性或环境变量设置
  • 配置文件: 客户端应用程序需要一个 bootstrap.yml 文件 在其中设置 spring.cloud.config.uri 属性(默认值是 http://localhost:8888) 指向 Config Server 的地址
  • 初始化过程: 在应用程序启动时 bootstrap.yml 会被加载 并通过 spring.cloud.config.uri 属性连接到 Config Server 以获取远程配置

相关依赖:

<!-- 旧版 -->
<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>2.3.12.RELEASE</version>
    <relativePath/>
</parent>
<!-- 新版 -->
<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>3.4.1</version>
    <relativePath/>
</parent>

旧版相关源码:

package org.springframework.cloud.bootstrap;

public class BootstrapApplicationListener implements ApplicationListener<ApplicationEnvironmentPreparedEvent>, Ordered {

    // 定义常量:bootstrap 配置源名称
    public static final String BOOTSTRAP_PROPERTY_SOURCE_NAME = "bootstrap";

    // 定义常量:默认属性名称
    public static final String DEFAULT_PROPERTIES = "springCloudDefaultProperties";
    
    // 省略部分代码...

    // 构造函数
    public BootstrapApplicationListener() {
    }

    // 监听到 ApplicationEnvironmentPreparedEvent 事件时调用的方法
    public void onApplicationEvent(ApplicationEnvironmentPreparedEvent event) {
        // 获取当前环境对象
        ConfigurableEnvironment environment = event.getEnvironment();
        
        // 新版 - 检查是否启用了 bootstrap 或使用了传统的处理方式
	    // if (PropertyUtils.bootstrapEnabled(environment) || PropertyUtils.useLegacyProcessing(environment)) {
        // 旧版 - 检查是否启用了 bootstrap
        if ((Boolean) environment.getProperty("spring.cloud.bootstrap.enabled", Boolean.class, true)) {
        
            // 如果环境中没有包含名为 "bootstrap" 的属性源
            if (!environment.getPropertySources().contains("bootstrap")) {
                ConfigurableApplicationContext context = null;
                // 解析配置的 bootstrap 名称 默认为 "bootstrap"
                String configName = environment.resolvePlaceholders("${spring.cloud.bootstrap.name:bootstrap}");
                // 获取 Spring 应用的初始化器列表
                Iterator var5 = event.getSpringApplication().getInitializers().iterator();
                // 省略部分代码...
            }
        }
    }
}

新版相关源码:

package org.springframework.cloud.util;

import org.springframework.core.env.Environment;
import org.springframework.util.ClassUtils;

public abstract class PropertyUtils {
    
    // 定义常量:表示是否启用了 bootstrap 配置
    public static final String BOOTSTRAP_ENABLED_PROPERTY = "spring.cloud.bootstrap.enabled";
    
    // 定义常量:用于标记类是否存在
    public static final boolean MARKER_CLASS_EXISTS = ClassUtils.isPresent("org.springframework.cloud.bootstrap.marker.Marker", (ClassLoader)null);

    // 判断是否启用了 bootstrap 配置的方法
    public static boolean bootstrapEnabled(Environment environment) {
        // 获取 "spring.cloud.bootstrap.enabled" 配置的值 默认为 false
        return (Boolean) environment.getProperty("spring.cloud.bootstrap.enabled", Boolean.class, false) || MARKER_CLASS_EXISTS;
    }

    // 省略部分代码...
}

相关核心代码解释:

在旧版本的 Spring Cloud 中 BootstrapApplicationListener 类会监听 ApplicationEnvironmentPreparedEvent 事件 在环境配置准备好后 检查是否启用了引导(spring.cloud.bootstrap.enabled)并进行初始化

  • 代码检查 spring.cloud.bootstrap.enabled 属性是否为 true 如果是 则进行 bootstrap 上下文的初始化
  • 如果环境中没有找到 "bootstrap" 配置源 则创建该配置源 并进行相应的配置初始化

在新版 Spring Cloud 中 PropertyUtils 类提供了一个新的方法来判断是否启用了 bootstrap 配置

bootstrapEnabled 方法: 检查是否启用了 spring.cloud.bootstrap.enabled 配置 或者 org.springframework.cloud.bootstrap.marker.Marker 类是否存在 这个类的存在标记了某些特定功能或版本的启用

  • spring.cloud.bootstrap.enabled 配置项通常表示是否启用 Spring Cloud Config Client 的引导功能
  • MARKER_CLASS_EXISTS 用来检查类 org.springframework.cloud.bootstrap.marker.Marker 是否存在 通常在新的 Spring Cloud 版本中 这个类的存在表示是否启用某些特定的配置或功能
  • MARKER_CLASS_EXISTS 常量用于检查 Marker 类是否存在 这是新版 Spring Cloud 引导机制的一部分 表示是否启用 bootstrap 配置

这个方法的核心目的是根据配置和类是否存在来判断是否启用 bootstrap 配置源

关键常量

  • MARKER_CLASS_EXISTS 常量用于检查 Marker 类是否存在 这是新版 Spring Cloud 引导机制的一部分 表示是否启用 bootstrap 配置

传统与新版的主要区别:

传统引导方式(旧版):

  1. spring.cloud.bootstrap.enabled:需要显式配置为 true 否则不会启用 bootstrap 配置
  2. bootstrap.yml:需要手动配置 spring.cloud.config.uri 和其他属性
  3. 代码流程:BootstrapApplicationListener 直接在事件监听时检查是否启用 bootstrap 并根据条件进行初始化

新版引导方式:

  1. bootstrapEnabled 方法:在新版 Spring Cloud 中 PropertyUtils.bootstrapEnabled 方法提供了一种更加灵活的方式来检查是否启用了 bootstrap 不仅仅通过 spring.cloud.bootstrap.enabled 属性 还通过检查 Marker 类的存在来进行判断
  2. Marker 类:新版加入了 org.springframework.cloud.bootstrap.marker.Marker 类 作为配置的标记类来表示是否启用某些功能
  3. 灵活性提高:新版代码通过 PropertyUtils 类提供的逻辑来判断是否启用引导功能 允许更灵活的配置和判断

总结:

  • 旧版 Spring Cloud: bootstrap.yml 是必需的配置文件 spring.cloud.bootstrap.enabled 用于控制是否启用引导配置 启用后 Spring Cloud Config Client 会自动连接到 Config Server
  • 新版 Spring Cloud: 引入了通过检查 Marker 类的存在来动态启用 bootstrap 配置的机制 使得配置加载更加灵活 并支持更多的使用场景 通过检查 spring.cloud.bootstrap.enabled 或 Marker 类的存在 Spring Cloud 可以决定是否加载 bootstrap.yml 文件
  • PropertyUtils 类: 提供了简化的方法来判断是否启用 bootstrap 配置 它通过判断 spring.cloud.bootstrap.enabled 属性的值以及 Marker 类的存在来决定是否启用 bootstrap 配置 从而使新版代码更加简洁、灵活和可扩展
  • 加载 bootstrap.yml 文件: 在新版 Spring Cloud 中 加载 bootstrap.yml 文件依赖于 spring.cloud.bootstrap.enabled 的配置值和 Marker 类的存在 当启用该属性时 Spring Cloud 会自动加载 bootstrap.yml 文件 无需手动指定文件路径 配置可以通过 ConfigurableEnvironment 或 ApplicationContext 来访问 如果需要更复杂的控制 开发者可以通过自定义 ApplicationContextInitializer 或 ApplicationListener 来实现定制化的配置加载过程

这种引导方式的演变表明 Spring Cloud 在不断优化和简化配置的同时 也保持了对旧版配置的兼容性

相关文章

  • Spring Boot加密配置文件特殊内容的示例代码详解

    Spring Boot加密配置文件特殊内容的示例代码详解

    这篇文章主要介绍了Spring Boot加密配置文件特殊内容的相关知识,本文通过图文并茂的形式给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2020-05-05
  • Java11 发布前抓紧掌握这些新特性

    Java11 发布前抓紧掌握这些新特性

    Java 11即将发布,你准备好了? 在这篇文章中,我们讨论下在进入Java 11之前,你需要了解的Java 8、9和10的一些有用功能,若还在用Java 8以前的版本,那就太落伍了,这里首先要明确,Java 8:是一个针对传统传统版本的重大转变
    2018-09-09
  • application作用域实现用户登录挤掉之前登录用户代码

    application作用域实现用户登录挤掉之前登录用户代码

    这篇文章主要介绍了application作用域实现用户登录挤掉之前登录用户代码,具有一定参考价值,需要的朋友可以了解下。
    2017-11-11
  • Springboot之如何统计代码执行耗时时间

    Springboot之如何统计代码执行耗时时间

    这篇文章主要介绍了Springboot之如何统计代码执行耗时时间问题,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2023-03-03
  • Spring Boot Admin微服务应用监控的实现

    Spring Boot Admin微服务应用监控的实现

    这篇文章主要介绍了Spring Boot Admin微服务应用监控,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2019-10-10
  • 浅谈java对象之间相互转化的多种方式

    浅谈java对象之间相互转化的多种方式

    这篇文章主要介绍了浅谈java对象之间相互转化的多种方式,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2020-08-08
  • 深入探究MyBatis插件机制灵活扩展及自定义增强框架能力

    深入探究MyBatis插件机制灵活扩展及自定义增强框架能力

    这篇文章主要介绍了深入探究MyBatis插件机制灵活扩展及自定义增强框架能力
    2024-01-01
  • java网上商城开发之邮件发送功能(全)

    java网上商城开发之邮件发送功能(全)

    这篇文章主要介绍了java网上商城开发之邮件发送功能,第一部分介绍了环境配置,第二部分则介绍了具体实现代码,感兴趣的小伙伴们可以参考一下
    2016-03-03
  • Java使用Graphics2D实现字符串文本自动换行

    Java使用Graphics2D实现字符串文本自动换行

    这篇文章主要为大家详细介绍了Java如何使用Graphics2D实现字符串文本自动换行,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下
    2024-04-04
  • Spring Boot统一异常处理详解

    Spring Boot统一异常处理详解

    我们在做Web应用的时候,请求处理过程中发生错误是非常常见的情况。这个时候就需要统一异常处理了,这篇文章主要给大家介绍了Spring Boot如何进行统一异常处理的相关资料,需要的朋友可以参考借鉴,下面来一起看看吧。
    2017-02-02

最新评论