Springが使えない@ProptySource配合@ConfigrationPropties注入対象属性問題解析
22889 ワード
記事の目次、問題背景 、再現問題 2.1作成`datasource.yml`ファイル .作成`DataSourceProperties` .編纂`DynamicDataSourceProperties` 2.4注入不能原因解析 、ソリューション 3.1カスタム`ProptySourceFactory` 3.2使用`@ProptySource`注 一、問題の背景
プロジェクトには多データソースが必要なので、
二、問題を再現する
2.1
2.4注入不能原因解析
内部の具体的な解析属性ファイルの関数は
3.1カスタム
使用時に解析を指定する
プロジェクトには多データソースが必要なので、
datasource.yml
の構成ファイルを自分で書いてデータソースを配置したいです.springの常用解析方法を採用しています.成功していません.二、問題を再現する
2.1
datasource.yml
ファイルを作成するdynamic:
datasource:
druids:
write:
driverClassName: com.p6spy.engine.spy.P6SpyDriver
url: jdbc:p6spy:mysql://118.89.104.67:3306/saas_db?useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai
username: root
password: mysql!@root##123
filters: stat
read:
driverClassName: com.p6spy.engine.spy.P6SpyDriver
url: jdbc:p6spy:mysql://localhost:3306/saas_db?useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai
username: root
password: 1234
filters: stat
2.2作成DataSourceProperties
@Data
public class DataSourceProperties {
private String driverClassName;
private String url;
private String username;
private String password;
}
2.3作成DynamicDataSourceProperties
springを用いて提供された@ConfigurationProperties
は、@PropertySource
に注釈を合わせてdatasource.yml
をDynamicDataSourceProperties
のMapに解析した.@Data
@PropertySource(value = {"classpath:datasource.yml"})
@ConfigurationProperties(prefix = "dynamic.datasource")
public class DynamicDataSourceProperties {
// Map
private Map<String, DataSourceProperties> druids = new LinkedHashMap<>();
}
しかし、結果は理想的ではなく、druids
を起動した後、springboot
は空のセットであることがわかった.2.4注入不能原因解析
driuds
のソースコードを追跡することによって、彼の解析位置は@PropertySource
から始まりました.ConfigurationClassParser
方法で解決します.protected final SourceClass doProcessConfigurationClass(ConfigurationClass configClass, SourceClass sourceClass)
引き続き彼のソースコードを観察します.// Process any @PropertySource annotations
for (AnnotationAttributes propertySource : AnnotationConfigUtils.attributesForRepeatable(
sourceClass.getMetadata(), PropertySources.class,
org.springframework.context.annotation.PropertySource.class)) {
if (this.environment instanceof ConfigurableEnvironment) {
processPropertySource(propertySource);
}
else {
logger.info("Ignoring @PropertySource annotation on [" + sourceClass.getMetadata().getClassName() +
"]. Reason: Environment must implement ConfigurableEnvironment");
}
}
ここではdoProcessConfigurationClass
の方法を使って、引き続き追跡します. /**
* Process the given @PropertySource
annotation metadata.
* @param propertySource metadata for the @PropertySource
annotation found
* @throws IOException if loading a property source failed
*/
private void processPropertySource(AnnotationAttributes propertySource) throws IOException {
String name = propertySource.getString("name");
if (!StringUtils.hasLength(name)) {
name = null;
}
String encoding = propertySource.getString("encoding");
if (!StringUtils.hasLength(encoding)) {
encoding = null;
}
String[] locations = propertySource.getStringArray("value");
Assert.isTrue(locations.length > 0, "At least one @PropertySource(value) location is required");
boolean ignoreResourceNotFound = propertySource.getBoolean("ignoreResourceNotFound");
Class<? extends PropertySourceFactory> factoryClass = propertySource.getClass("factory");
PropertySourceFactory factory = (factoryClass == PropertySourceFactory.class ?
DEFAULT_PROPERTY_SOURCE_FACTORY : BeanUtils.instantiateClass(factoryClass));
for (String location : locations) {
try {
String resolvedLocation = this.environment.resolveRequiredPlaceholders(location);
Resource resource = this.resourceLoader.getResource(resolvedLocation);
addPropertySource(factory.createPropertySource(name, new EncodedResource(resource, encoding)));
}
catch (IllegalArgumentException | FileNotFoundException | UnknownHostException ex) {
// Placeholders not resolvable or resource not found when trying to open it
if (ignoreResourceNotFound) {
if (logger.isInfoEnabled()) {
logger.info("Properties location [" + location + "] not resolvable: " + ex.getMessage());
}
}
else {
throw ex;
}
}
}
}
processPropertySource
サイクルにはこのような関数があると見られます.内部の具体的な解析属性ファイルの関数は
for
によって実行され、追跡していくと、addPropertySource(factory.createPropertySource(name, new EncodedResource(resource, encoding)));
は一つのデフォルトPropertySourceFactory factory
しか提供されていないことが分かります.このデフォルトの工場を見ると、彼が使っているSpring
で属性ファイルをロードしていることが分かります.最後に発見されたのを追跡して、DefaultPropertySourceFactory
のクラスで属性を読み取ります. public synchronized void load(InputStream inStream) throws IOException {
load0(new LineReader(inStream));
}
これにより、PropertiesLoaderUtils.loadProperties(resource)
から提供されたProperties
が、Spring
ファイルの解析に対応していないことが分かり、彼は一行の読み取りができる.city:
name:
area: 200
上のymlは解析されます.city = ""
name = " "
area = "200"
私たちが欲しいのはcity.name = " "
city.area = "200"
三、解決策3.1カスタム
@PropertySource
public class YamlPropertySourceFactory implements PropertySourceFactory {
@Override
public PropertySource<?> createPropertySource(String name, EncodedResource resource) throws IOException {
YamlPropertySourceLoader loader = new YamlPropertySourceLoader();
List<PropertySource<?>> load = loader.load(name, resource.getResource());
if (load!=null && !load.isEmpty()) {
return load.get(0);
}
return null;
}
}
3.2 yml
を使用して注釈を付ける使用時に解析を指定する
PropertySourceFactory
@PropertySource(value = {"classpath:datasource.yml"},
factory = YamlPropertySourceFactory.class)