@ConditionalOnProperty 用于多值属性

10

有没有一种方法可以基于多值属性使用@ConditionalOnProperty注解?

Spring配置:

@Bean
@ConditionalOnProperty(name = "prop", havingValue = "a")
public SomeBean bean1() {
    return new SomeBean1();
}

@Bean
@ConditionalOnProperty(name = "prop", havingValue = "b")
public SomeBean bean2() {
    return new SomeBean2();
}

以及application.yaml文件

prop: 
 - a
 - b

我期望两个bean:bean1和bean2都将被注册到Spring上下文中,但它们中没有一个被注册。有什么方法可以做到这一点吗?

你是说以后当你执行context.getBean("bean1");时它不起作用了吗? - Mudassar
@Mudassar 是的,完全正确。 - Maksim Prabarshchuk
4个回答

4

您可以使用Spring Boot @ConditionalOnExpression注解,不仅仅是在@bean注释的方法中,还可以直接用在类上:

@ConditionalOnExpression("${my.prop}")
@Component
class SomeBean1 implements SomeBean {}

@ConditionalOnExpression("!${my.prop}")
@Component
class SomeBean2 implements SomeBean {}

话虽如此,我个人更喜欢使用纯Java:

@Value("${my.prop}") String myProp;
...
@Bean SomeBean someBean() {
    if ("abc".equals(myProp)) {return new SomeBean1();} else {return new SomeBean2();}
}

你能否给我一个使用@ConditionalOnExpression和多值属性的例子?我的意思是类似于@ConditionalOnExpression("${my.prop}.contains('a')")这样的东西? - Maksim Prabarshchuk
@Value方法对我来说不太好,因为我想要在上下文中注册一组bean,而不仅仅是一个。而且我事先不知道bean的数量(这取决于属性“prop”)。 - Maksim Prabarshchuk
1
我认为应该是@ConditionalOnExpression("'${my.prop}'.contains('a')")或者例如@ConditionalOnExpression("'${my.prop}'=='abc'")。您可以使用SpEL支持的任何表达式。我理解您想要依赖组件扫描,否则您可以在@Configuration类中完成所有逻辑。 - Alexander
我个人仍然更喜欢在纯Java中实现这种逻辑,例如:class SomeBeanManager {@Autowired void setAllOfThem(List<SomeBean> allOfThem) {...} } 并根据您的属性选择其中之一。 - Alexander
1
@ConditionalOnExpression("'${vault.environment.type}'.contains('MAP')") 对我很有效。 - Thiagarajan Ramanathan
显示剩余6条评论

3

看起来@ConditionalOnProperty没有多值属性。

在Spring环境中,它们呈现为:

prop[0]=a
prop[1]=b

我的解决方案是创建一个自己的 @Conditional 扩展,可以处理多个属性值。以下是示例。
注释:
@Retention(RetentionPolicy.RUNTIME)
@Target({ ElementType.TYPE, ElementType.METHOD })
@Documented
@Conditional(OnPropertyCondition.class)
public @interface ConditionalOnProperty2 {
    String name();
    String value();
}

条件:

class OnPropertyCondition implements Condition {
    @Override
    public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
        String name = attribute(metadata, "name");
        String value = attribute(metadata, "value");

        String searchName = name;
        String property = property(context, searchName);
        int i = 0;
        do {
            if (value.equals(property)) return true;
            searchName = name + '[' + i++ + ']';
        } while ((property = property(context, searchName)) != null);
        return false;
    }

    private String attribute(AnnotatedTypeMetadata metadata, String name) {
        return (String) metadata.getAnnotationAttributes(ConditionalOnProperty2.class.getName()).get(name);
    }

    private String property(ConditionContext context, String name) {
        return context.getEnvironment().getProperty(name);
    }
}

使用方法:

 @Bean
 @ConditionalOnProperty2(name = "prop", havingValue = "a")
 public SomeBean bean1() {
     return new SomeBean1();
 }

 @Bean
 @ConditionalOnProperty2(name = "prop", havingValue = "b")
 public SomeBean bean2() {
     return new SomeBean2();
 }

如果使用Spring Boot,可以考虑扩展SpringBootCondition,因为它提供了更多的日志信息,以便在出现问题时进行排查。 - bric3

0

havingValue 并不意味着一个对象拥有这个值,它表示我们从这个键所获取的值是否等于 havingValue

因此,一个列表对象 prop 既不等于 a 也不等于 b,它等于 [a, b]。两者都不会被加载。


0
我认为Thiagarajan Ramanathan的评论非常有帮助,回答了这个问题。
 @ConditionalOnExpression("'${vault.environment.type}'.contains('MAP')") 

对我来说起了作用。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接