使用外部中间件时,如:金蝶、东方通 在不重启整个中间件的情况下,二次部署或多个服务同时部署在一个虚拟机下(JVM) IdTypeEnvironmentPostProcessor.setIdType 会将一个IdType对象put进SystemPropertiesPropertySource,而SystemPropertiesPropertySource在整个JVM中是共用的,导致两处问题:
报错信息:org.springframework.core.convert.ConverterNotFoundException:
No converter found capable of converting from type [com.baomidou.mybatisplus.annotation.IdType] to type [com.baomidou.mybatisplus.annotation.IdType]
问题一:IdTypeEnvironmentPostProcessor.getIdType 中 environment.getProperty(ID_TYPE_KEY, IdType.class) 获取到了上一次部署应用时的 IdType 对象,而上一次的 IdType 对象,和本次部署时 IdType.class 的类加载器不一致,导致报错;
问题二:org.springframework.boot.context.properties.bind.BindConverter.convert 中,delegate.canConvert 返回的都是false,最终:throw (failure != null) ? failure : new ConverterNotFoundException(sourceType, targetType);
原因分析:
首先 ConfigurableEnvironment
ConfigurableEnvironment.getProperty(...) 的查找顺序是分层次的:
1、命令行参数(CommandLinePropertySource,即 --key=value)
2、Java 系统属性(System.getProperties(),对应 SystemPropertiesPropertySource)
3、操作系统环境变量(System.getenv(),对应 SystemEnvironmentPropertySource)
4、application.yml / application.properties(OriginTrackedMapPropertySource)
5、默认属性(DefaultPropertiesPropertySource)
其次:Spring 的属性绑定用到了 ConfigurationPropertySource
ConfigurationPropertySource:
它是 Spring Boot 2.x 以后引入的抽象,表示配置属性的来源。
比如:
.properties / .yml 文件,
系统属性(System.getProperties()),
环境变量(System.getenv()),
甚至 Nacos、Apollo 这样的远程配置中心。
它统一成 ConfigurationPropertySource 接口,Spring Boot 就能用同一套逻辑去读取配置。
和 ConfigurableEnvironment 的关系
ConfigurableEnvironment 内部持有一系列 PropertySource。
Spring Boot 启动时会把这些 PropertySource 适配成 ConfigurationPropertySource,
这样属性绑定器(Binder)就可以从中读取配置值。
也就是说:
environment.getProperty("my.key") 读出来的值,
和 Binder 里 ConfigurationPropertySource 提供的值,
本质上是同一批配置源,只是走的 API 不一样。
导致的问题:org.springframework.boot.context.properties.bind.BindConverter.convert 的参数:Object source, TypeDescriptor sourceType, TypeDescriptor targetType
source 是上一次部署时的 IdType 对象
sourceType 的类加载器 (sourceType.getType().getClassLoader()) 与 targetType 的类加载器 (targetType.getType().getClassLoader())不一致,抛出:ConverterNotFoundException
org.springframework.boot.context.properties.bind.BindConverter.convert:
private Object convert(Object source, TypeDescriptor sourceType, TypeDescriptor targetType) {
ConversionException failure = null;
for (ConversionService delegate : this.delegates) {
try {
if (delegate.canConvert(sourceType, targetType)) {
return delegate.convert(source, sourceType, targetType);
}
}
catch (ConversionException ex) {
if (failure == null && ex instanceof ConversionFailedException) {
failure = ex;
}
}
}
throw (failure != null) ? failure : new ConverterNotFoundException(sourceType, targetType);
}
pull/208/head
parent
21243b124c
commit
f12162e7ff
|
|
@ -9,7 +9,10 @@ import lombok.extern.slf4j.Slf4j;
|
|||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.env.EnvironmentPostProcessor;
|
||||
import org.springframework.core.env.ConfigurableEnvironment;
|
||||
import org.springframework.core.env.MapPropertySource;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
|
|
@ -56,11 +59,23 @@ public class IdTypeEnvironmentPostProcessor implements EnvironmentPostProcessor
|
|||
}
|
||||
|
||||
public IdType getIdType(ConfigurableEnvironment environment) {
|
||||
return environment.getProperty(ID_TYPE_KEY, IdType.class);
|
||||
// return environment.getProperty(ID_TYPE_KEY, IdType.class);
|
||||
String value = environment.getProperty(ID_TYPE_KEY);
|
||||
try {
|
||||
return StrUtil.isNotBlank(value) ? IdType.valueOf(value) : IdType.NONE;
|
||||
} catch (IllegalArgumentException ex) {
|
||||
log.error("无法解析 id-type 配置值:{}", value, ex);
|
||||
return IdType.NONE;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public void setIdType(ConfigurableEnvironment environment, IdType idType) {
|
||||
environment.getSystemProperties().put(ID_TYPE_KEY, idType);
|
||||
// environment.getSystemProperties().put(ID_TYPE_KEY, idType);
|
||||
// log.info("[setIdType][修改 MyBatis Plus 的 idType 为({})]", idType);
|
||||
Map<String, Object> map = new HashMap<>();
|
||||
map.put(ID_TYPE_KEY, idType);
|
||||
environment.getPropertySources().addFirst(new MapPropertySource("mybatisPlusIdType", map));
|
||||
log.info("[setIdType][修改 MyBatis Plus 的 idType 为({})]", idType);
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue