英文:
Using Spring expression language to assert if a property (with comma separated values) loaded from a properties file, contains "value"
问题
我有以下问题,我在Spring Boot中有一个特定的配置类,其中包含一些Bean,我希望只有在某个属性满足一定条件时才创建这些Bean。该属性是一个包含一组值的列表。
现在配置类大致如下所示:
@ConditionalOnExpression("#{'${conditions.values.options}'.contains('ScenarioOne')}")
ConfigurationForScenarioOne {
@Bean
public StudentBean getStudentBean(){
...
}
}
在属性文件中,我会有类似以下的内容:
conditions.values.options=${CONDITIONS_VALUES_OPTIONS}
然后在运行时提供CONDITIONS_VALUES_OPTIONS
的值,例如:
-DCONDITIONS_VALUES_OPTIONS=ScenarioOne,ScenarioTwo,ScenarioFive
我之所以这样做,是为了能够部署应用程序,并根据运行时提供的值使用不同的Bean。我将会有多个这样的配置类,根据传递的属性来确定创建哪些配置类。
为了实现这一点,我试图依赖于Spring表达式语言来执行以下操作:
- 读取属性
conditions.values.options
, - 将其转换为列表,
- 验证列表是否包含所需的配置字符串。
到目前为止,我尝试了许多表达式,包括:
@ConditionalOnExpression("#{'${conditions.values.options}'.contains('ScenarioOne')}")
以及其他类似的表达式,但都没有成功。有人可以帮助我看看我在这里漏掉了什么吗?
英文:
I have the following problem, I have a certain configuration class in spring boot which contains beans, which I would like to be created only if a certain property which has a list of values, contains a certain value.
now the configuration class looks something like this:
@ConditionalOnExpression("#{'${conditions.values.options}'.contains('ScenarioOne')}")
ConfigurationForScenarioOne{
@Bean
public StudentBean getStudentBean(){
...
}
}
In the properties file I would have something like so:
conditions.values.options=${CONDITIONS_VALUES_OPTIONS}
Then provide the value of CONDITIONS_VALUES_OPTIONS
at runtime like so:
-DCONDITIONS_VALUES_OPTIONS=ScenarioOne,ScenarioTwo,ScenarioFive
The reason why I want to do this is to be able to deploy the app and have different beans be in use depending on the value given at runtime.
I will have several of these Configuration classes that will be created based on which ones of these properties are passed.
TO achieve this I was trying to rely on Spring Expression language to do the following:
1-Read the property conditions.values.options
, then convert it to a list, then verify if the list contained the desired configuration string.
So far I have tried several expressions including:
@ConditionalOnExpression("#{'${conditions.values.options}'.contains('ScenarioOne')}")
and other similar expressions, with no luck. Can someone help me see what I am missing here?
答案1
得分: 3
我能够通过以下方式实现所需的行为:
@ConditionalOnExpression("#{T(java.util.Arrays).asList('${conditions.values.options}').contains('ScenarioOne')}")
因此,稍微解析一下,希望能够帮助那些可能遇到类似问题的人:
似乎 Spring 将逗号分隔的属性读取为数组,这是错误的,因为可以使用 Spring Boot 来完成以下操作:
以下两个语句都是有效的:
@Value("${property.with.comma.separated.values}")
private List<String> prop;
或者
@Value("${property.with.comma.separated.values}")
private String[] prop;
现在是表达式:
"${conditions.values.options}".contains("ScenarioOne")
会抛出错误,说 Array 类没有名为 .contains()
的方法,因此我不得不采取使用一些方法调用:
T(java.util.Arrays).asList('${conditions.values.options}')
来读取逗号分隔的值,将字符串数组转换为 List<String>
,然后执行 .contains(..)
方法。
我希望有一种更简单的方法来做这件事,必须在其中进行方法调用似乎有点过度设计。
我愿意听取建议 😊。
英文:
I was able to get the desired behavior using this:
@ConditionalOnExpression("#{T(java.util.Arrays).asList('${conditions.values.options}').contains('ScenarioOne')}")
So dissecting this a little bit in the hopes to help others who may come across similar problems:
It seems that spring reads comma separated properties as an Array, this is bogus because the following can be done using spring boot:
The following two statements are valid:
@Value("${property.with.comma.separated.values}")
private List<String> prop;
OR
@Value("${property.with.comma.separated.values}")
private String [] prop;
now the expression:
"#{'${conditions.values.options}'.contains('ScenarioOne')}"
will throw an error saying that the Array class does not contain a method called '.contains()'
hence this is why I had to resort to use some method execution:
T(java.util.Arrays).asList('${conditions.values.options}')
to read the comma separated values, convert the String array into a List<String> and then perform the '.contains(..)' method invocation.
I wish there was a simpler way to do it, to have to do a method invocation in there seems like overkill.
I am open to sugestions 😀
答案2
得分: 0
一个替代方法是实现一个自定义的条件注解。
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Conditional(OnHavingValueCondition.class)
public @interface ConditionalOnHavingValue {
/**
* 应该包含该值的属性。
*/
String property();
/**
* 必须存在的值。
*/
String value();
}
public class OnHavingValueCondition extends SpringBootCondition {
@Override
public ConditionOutcome getMatchOutcome(final ConditionContext context, final AnnotatedTypeMetadata metadata) {
final MultiValueMap<String, Object> attributes = metadata.getAllAnnotationAttributes(ConditionalOnHavingValue.class.getName());
if (attributes == null) {
return ConditionOutcome.noMatch("无法从 ConditionalOnHavingValue 注解中获取属性");
}
final String property = (String) attributes.getFirst("property");
final String value = (String) attributes.getFirst("value");
//noinspection unchecked,DataFlowIssue
final Set<String> propertyValues = (Set<String>) context.getEnvironment().getProperty(property, Set.class);
//noinspection DataFlowIssue
if (!propertyValues.contains(value)) {
return ConditionOutcome.noMatch("在属性中找不到特定的值");
}
return ConditionOutcome.match();
}
}
英文:
An alternative is to implement a custom conditional annotation.
@ConditionalOnHavingValue(property = "conditions.values.options", value = "ScenarioOne")
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Conditional(OnHavingValueCondition.class)
public @interface ConditionalOnHavingValue {
/**
* The property that should contain the value.
*/
String property();
/**
* The value that must be present.
*/
String value();
}
public class OnHavingValueCondition extends SpringBootCondition {
@Override
public ConditionOutcome getMatchOutcome(final ConditionContext context, final AnnotatedTypeMetadata metadata) {
final MultiValueMap<String, Object> attributes = metadata.getAllAnnotationAttributes(ConditionalOnHavingValue.class.getName());
if (attributes == null) {
return ConditionOutcome.noMatch("Unable to retrieve attributes from ConditionalOnHavingValue annotation");
}
final String property = (String) attributes.getFirst("property");
final String value = (String) attributes.getFirst("value");
//noinspection unchecked,DataFlowIssue
final Set<String> propertyValues = (Set<String>) context.getEnvironment().getProperty(property, Set.class);
//noinspection DataFlowIssue
if (!propertyValues.contains(value)) {
return ConditionOutcome.noMatch("Unable to find the particular value in the property");
}
return ConditionOutcome.match();
}
}
答案3
得分: -1
请尝试将您的注解值以以下方式编写并检查:
@ConditionalOnExpression("#{${conditions.values.options}}.contains('ScenarioOne')")
在大括号后面写入contains
方法。
英文:
Can you try writing your annotation values like this and check :
@ConditionalOnExpression("#{${conditions.values.options}}.contains('ScenarioOne')")
Write the contains method after the curly brackets.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论