这样做的目的是使用 @Value 动态获取属性。
我的申请
application.yml
app:
name: my-app-scv
my-app-svc: # This value will be dynamic acoording to each other app implementing Auth-Library
oauth:
tokenUrl: https://anOAuthImpl.com
jwksUrl: https://anOAuthImpl.com/jwks
validation:
url: https://anOAuthImpl.com/validate/
client:
id: client_id
secret: secret
授权库
AuthConfigurationProperties java记录
@Component
@Validated
public record AuthConfigurationProperties(
@Value("${#{authAppConfiguration.appName}.oauth.tokenUrl}}")
@NotEmpty
String tokenUrl,
@Value("${#{authAppConfiguration.appName}.oauth.jwksUrl}")
String jwksUrl,
@Valid
OauthClient client,
@Valid
ValidationProperties validation
) {
}
AuthAppConfiguration java记录
@Component
public record AuthAppConfiguration(
@Value("${app.name}")
String appName
) {
}
这些更改之前的解决方案有
@ConfigurationProperties
,但不支持 SpEL。
这是正确的方法还是有更简单的方法?另外,我的豆子也有问题
AuthAppConfiguration
; Spring无法正确解析bean。
到目前为止,我正在尝试在另一个 bean 中设置应用程序之外的值,以便 SpEL 首先计算表达式,然后 @Value 选取预期的属性。
Caused by: java.lang.IllegalArgumentException: Could not resolve placeholder '#{authAppConfiguration.appName}.oauth.tokenUrl' in value "${#{authAppConfiguration.appName}.oauth.tokenUrl}"
您有多种选择来动态获取环境变量。
Environment
bean 或 PostConstruct
来执行自定义逻辑。这可能是最简单的,因此也是适合您情况的最佳解决方案
Environment
豆。@Component
@Validated
public class AuthConfigurationProperties {
@NotEmpty
private String tokenUrl;
private String jwksUrl;
@Valid
private OauthClient client;
@Valid
private ValidationProperties validation;
public AuthConfigurationProperties(OauthClient client, ValidationProperties validation, Environment environment) {
this.client = client;
this.validation = validation;
String appName = environment.getProperty("authAppConfiguration.appName");
this.tokenUrl = environment.getProperty("%s.oauth.tokenUrl".formatted(appName));
this.jwksUrl = environment.getProperty("%s.oauth.jwksUrl".formatted(appName));
}
}
通过为环境注册 PostProcess,您可以添加自己的动态值:
public class CustomPropertySource extends PropertySource<String> {
private Environment environment;
public CustomPropertySource(String name, Environment environment) {
super(name); //name of the PropertySource, doesn't matter in our implementation
this.environment = environment;
}
@Override
public Object getProperty(String name) {
if (name.equals("customUsername")) {
// do similar magic with environment
return "MY CUSTOM RUNTIME VALUE";
}
return null; // when no such value we return null
}
}
class EnvironmentConfig implements EnvironmentPostProcessor {
@Override
public void postProcessEnvironment(ConfigurableEnvironment environment,
SpringApplication application) {
environment.getPropertySources()
.addFirst(new CustomPropertySource("customPropertySource", environment));
}
}
最后一步是你必须注册EnvironmentPostProcess,通过在src/main/resources/META-INF/spring.factories路径下创建spring.factories文件,并在其中添加以下行:
org.springframework.boot.env.EnvironmentPostProcessor=package.to.environment.config.EnvironmentConfig
PropertySourcesPlaceholderConfigurer
负责解析这些占位符的类是一个名为 PropertySourcesPlaceholderConfigurer 的 BeanPostProcessor(请参阅此处)。
因此您可以覆盖它并提供我们的自定义 PropertySource 来解析该属性,如下所示:
@Component
public class CustomConfigurer extends PropertySourcesPlaceholderConfigurer {
@Override
protected void processProperties(ConfigurableListableBeanFactory beanFactoryToProcess, ConfigurablePropertyResolver propertyResolver) throws BeansException {
// again you would need to do some logic with environment bean to get your values and then set them as you wish
((ConfigurableEnvironment) beanFactoryToProcess.getBean("environment"))
.getPropertySources()
.addFirst(new CustomPropertySource("customPropertySource"));
super.processProperties(beanFactoryToProcess, propertyResolver);
}
}
最后一个选项可能需要设置
@Order
以确保它在设置其他值后运行。
总的来说,我推荐第一个选项,其他选项似乎不必要地复杂,但我只是想提供我所知道的所有选项。