我有一个基于Spring Boot的Java库。就我而言,我需要以自己的方式解析以something.*
开头的占位符,然后才能由某些 Spring 属性解析器解析此属性。例如:
应用程序属性:
spring.datasource.url="${something.url}"
所以这个占位符与我的something.*
模式相匹配,我想在春天尝试解决它之前用specific
词替换它。我在哪里可以做到这一点,以便我可以避免使用 System.setProperty
创建系统属性?
以下是我实现它的尝试:
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.config.PropertyPlaceholderConfigurer;
import org.springframework.util.PropertyPlaceholderHelper;
import org.springframework.util.StringValueResolver;
import java.util.Properties;
public class MyPropertyPlaceholderConfigurer extends PropertyPlaceholderConfigurer {
@Override
protected void processProperties(ConfigurableListableBeanFactory beanFactoryToProcess, Properties props)
throws BeansException {
StringValueResolver valueResolver = new ReloadablePlaceholderResolvingStringValueResolver(props);
this.doProcessProperties(beanFactoryToProcess, valueResolver);
}
private class ReloadablePlaceholderResolvingStringValueResolver
implements StringValueResolver {
private final PropertyPlaceholderHelper helper;
private final ReloadablePropertyPlaceholderConfigurerResolver resolver;
public ReloadablePlaceholderResolvingStringValueResolver(Properties props) {
this.helper = new MyPropertyPlaceholderHelper(placeholderPrefix, placeholderSuffix, valueSeparator, ignoreUnresolvablePlaceholders);
this.resolver = new ReloadablePropertyPlaceholderConfigurerResolver(props);
}
@Override
public String resolveStringValue(String strVal) throws BeansException {
String value = this.helper.replacePlaceholders(strVal, this.resolver);
return (value.equals(nullValue) ? null : value);
}
}
private class ReloadablePropertyPlaceholderConfigurerResolver
implements PropertyPlaceholderHelper.PlaceholderResolver {
private Properties props;
private ReloadablePropertyPlaceholderConfigurerResolver(Properties props) {
this.props = props;
}
@Override
public String resolvePlaceholder(String placeholderName) {
return MyPropertyPlaceholderConfigurer.this.resolvePlaceholder(placeholderName, props, SYSTEM_PROPERTIES_MODE_FALLBACK);
}
}
}
初始化 bean:
@Bean
public PropertyPlaceholderConfigurer propertyPlaceholderConfigurer() {
return new MyPropertyPlaceholderConfigurer();
}
正在解析属性:
@RestController
public class MainController {
@Autowired
private Environment environment;
@GetMapping("/getEnvProeprty")
public String getEnvironmentName() {
return environment.getProperty("spring.datasource.url");
}
}
我有类似的要求并解决了这个用例。
总之:
由于 spring 尝试按照 porperty 源列表中的顺序解析属性,因此基本思想是在当前Environment
中 PropertySources 的顶部 [0th 索引] 添加我们的自定义属性源。
为此,请重写类PropertySourcesPlaceholderConfigurer.java
中的 getAppliedPropertySources()
方法,并在当前 Spring Evnironment
中addFirst
自定义属性源。因此,Spring 将首先尝试从我们的自定义源中解析占位符,如果未找到,则遍历其余的属性源。
@Configuration
public class CustomPropertySourcesPlaceholderConfigurer extends PropertySourcesPlaceholderConfigurer {
private static final String[] IGNORABLE_SECRETS = new String[] {"secrets.property1", "secrets.property2"};
@Override
public PropertySources getAppliedPropertySources() throws IllegalStateException {
// gets the applied property sources
MutablePropertySources appliedPropertySources = (MutablePropertySources) super.getAppliedPropertySources();
// fetches the property source of the current evnironment
PropertySource<?> environmentPropertySource = appliedPropertySources.get("environmentProperties");
if (Objects.nonNull(environmentPropertySource)) {
addCustomPropertySourceToIgnoreRefresh(environmentPropertySource);
}
return appliedPropertySources;
}
private void addCustomPropertySourceToIgnoreRefresh(PropertySource<?> environmentPropertySource) {
Map<String, Object> customPropertyMap = new HashMap<>();
// Iterating over properties listed in IGNORABLE_SECRETS to override them
for (String ignorableSecretProperty: IGNORABLE_SECRETS) {
customPropertyMap.put(ignorableSecretProperty, "customValueForThatPorperty");
// For my use case, i'm trying to fetch the secrets from the property sources that are loaded
// during the bootstrap phase and using them as sort of cache so that spring boot doesn't
// need to make grpc calls to google to resolve the secrets during the consul refresh
// customPropertyMap.put(ignorableSecretProperty,
// environmentPropertySource.getProperty(ignorableSecretProperty));
}
ConfigurableEnvironment configurableEnvironment =
(ConfigurableEnvironment) environmentPropertySource.getSource();
// post fetching the configurable environment, adding my hash map as custom map property source
configurableEnvironment.getPropertySources()
.addFirst(new MapPropertySource("customPropertySource", customPropertyMap));
}
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
super.postProcessBeanFactory(beanFactory);
}
}
有关更多详细信息,您可以参考我在媒体上发表的文章。