技术栈: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 之前 配置文件的加载顺序是按照以下顺序进行的:
application.properties
application.yml
bootstrap.properties
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
配置
传统与新版的主要区别:
传统引导方式(旧版):
spring.cloud.bootstrap.enabled
:需要显式配置为true
否则不会启用bootstrap
配置bootstrap.yml
:需要手动配置spring.cloud.config.uri
和其他属性- 代码流程:
BootstrapApplicationListener
直接在事件监听时检查是否启用bootstrap
并根据条件进行初始化
新版引导方式:
bootstrapEnabled
方法:在新版 Spring Cloud 中PropertyUtils.bootstrapEnabled
方法提供了一种更加灵活的方式来检查是否启用了bootstrap
不仅仅通过spring.cloud.bootstrap.enabled
属性 还通过检查Marker
类的存在来进行判断- Marker 类:新版加入了
org.springframework.cloud.bootstrap.marker.Marker
类 作为配置的标记类来表示是否启用某些功能 - 灵活性提高:新版代码通过
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 在不断优化和简化配置的同时 也保持了对旧版配置的兼容性