-
Notifications
You must be signed in to change notification settings - Fork 85
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
提一个建议,关于配置文件绑定顺序的问题 #3
Comments
根据给出的配置编写测试用例,没有复现问题。 单数据源测试用例:DevProfilesTests.java |
@drtrang @ConfigurationProperties(DRUID_DATA_SOURCE_PREFIX)
public abstract class DruidParentDataSource extends DruidDataSource { @ConditionalOnMissingBean(DataSource.class)
//如果放在这里,则使用ConfigurationPropertiesBindingPostProcessor来绑定,会出现顺序的问题
@ConfigurationProperties(DRUID_DATA_SOURCE_PREFIX)
@Bean(initMethod = "init", destroyMethod = "close")
public DruidDataSource dataSource() {
log.debug("druid data-source init...");
return new DruidParentDataSource() {};
} datasource:
druid:
min-evictable-idle-time-millis: 100000
max-evictable-idle-time-millis: 200000
one:
name: one
two:
name: two @Bean
@ConfigurationProperties("spring.datasource.druid.one")
public DruidDataSource firstDataSource() {
log.debug("druid first-data-source init...");
return new DruidMultiDataSource();
}
@Bean
@ConfigurationProperties("spring.datasource.druid.two")
public DruidDataSource secondDataSource() {
log.debug("druid second-data-source init...");
return new DruidMultiDataSource();
} 但是,多数据源就不行了,它们只能把 spring.autoconfigure.exclude=- org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration
spring.datasource.druid.one.min-evictable-idle-time-millis=100000
spring.datasource.druid.one.max-evictable-idle-time-millis=200000
spring.datasource.druid.two.min-evictable-idle-time-millis=100001
spring.datasource.druid.two.max-evictable-idle-time-millis=200002 |
直接上结论,导致 alibaba/druid#1796 的原因在于 Spring Boot 在读取 YamlPropertySourceLoader#load(String name, Resource resource, String profile) // 用 LinkedHashMap 解析
public Map<String, Object> process() {
final Map<String, Object> result = new LinkedHashMap<String, Object>();
process(new MatchCallback() {
@Override
public void process(Properties properties, Map<String, Object> map) {
result.putAll(getFlattenedMap(map));
}
});
return result;
} PropertiesPropertySourceLoader#load(String name, Resource resource, String profile) // 用 HashTable 解析
public PropertySource<?> load(String name, Resource resource, String profile) throws IOException {
if (profile == null) {
Properties properties = PropertiesLoaderUtils.loadProperties(resource);
if (!properties.isEmpty()) {
return new PropertiesPropertySource(name, properties);
}
}
return null;
} RelaxedDataBinder 在进行数据绑定的时候,会依据 PropertySource 的顺序赋值,而 Properties 继承自 HashTable,本身是无序的,那么出现问题就不稀奇了。 所以问题不在于单数据源或多数据源,也不在于 |
DruidParentDataSource 类存在的目的是为了注入 也就是说,以下两种方式是等价的: spring:
datasource:
druid:
min-evictable-idle-time-millis: 1800000
max-evictable-idle-time-millis: 25200000
one:
name: one
two:
name: two
---
spring:
datasource:
druid:
one:
name: one
min-evictable-idle-time-millis: 1800000
max-evictable-idle-time-millis: 25200000
two:
name: two
min-evictable-idle-time-millis: 1800000
max-evictable-idle-time-millis: 25200000 若子数据源有相同的配置时,会覆盖掉父数据源的值: spring:
datasource:
druid:
max-active: 20
one:
name: one
max-active: 50
two:
name: two |
@drtrang 我个人还是比较看好 幸运的是,Spring Boot官方也意识到了该问题,在Spring Boot 2.0 移除了 PropertiesPropertySourceLoader#load(String name, Resource resource, String profile) public PropertySource<?> load(String name, Resource resource, String profile)
throws IOException {
if (profile == null) {
Map<String, ?> properties = loadProperties(resource);
if (!properties.isEmpty()) {
return new OriginTrackedMapPropertySource(name, properties);
}
}
return null;
} OriginTrackedPropertiesLoader# load(boolean expandLists) /**
* Load {@code .properties} data and return a map of {@code String} ->
* {@link OriginTrackedValue}.
* @param expandLists if list {@code name[]=a,b,c} shortcuts should be expanded
* @return the loaded properties
* @throws IOException on read error
*/
public Map<String, OriginTrackedValue> load(boolean expandLists) throws IOException {
try (CharacterReader reader = new CharacterReader(this.resource)) {
Map<String, OriginTrackedValue> result = new LinkedHashMap<>();
StringBuilder buffer = new StringBuilder();
while (reader.read()) {
String key = loadKey(buffer, reader).trim();
if (expandLists && key.endsWith("[]")) {
key = key.substring(0, key.length() - 2);
int index = 0;
do {
OriginTrackedValue value = loadValue(buffer, reader, true);
put(result, key + "[" + (index++) + "]", value);
if (!reader.isEndOfLine()) {
reader.read();
}
}
while (!reader.isEndOfLine());
}
else {
OriginTrackedValue value = loadValue(buffer, reader, false);
put(result, key, value);
}
}
return result;
}
} PropertiesPropertySourceLoaderTests 单元测试我运行了下,已经是按照声明的顺序来存储了。 |
期待 2.0 发布 : ) |
: ) 正式版应该快了,这是一次愉快的技术探讨,感谢。 |
表示spring boot2.0 没有修复这个问题,最近遇到这个坑,项目不经常出现启动失败, 重启又好了 |
@itmyhome 使用的 spring boot 版本、starter 版本是多少?堆栈是否方便贴一下? |
|
2.0.9 用的yml 有同样的问题 |
我用的2.1.5 yml读取map也是无序的 |
@zhangyu-yxff @JiancongLee 有可复现的 test case 么?我这边暂时没有复现 |
你好,例如有上面这种配置,会导致单元测试失败,因为使用动态绑定会按ASCII的顺序绑定配置属性,也就是说max 是先于 min执行的,这也是Druid官方Starter放弃动态绑定这种便捷方式的原因之一,如果你有更好的解决方案,可以探讨下。
The text was updated successfully, but these errors were encountered: