@ConditionalOnProperty for lists or arrays?

做~自己de王妃 提交于 2020-06-23 07:06:19

问题


I'm using Spring Boot 1.4.3 @AutoConfiguration where I create beans automatically based on properties user specifies. User can specify an array of services, where name and version are required fields:

service[0].name=myServiceA
service[0].version=1.0

service[1].name=myServiceB
service[1].version=1.2

...

If the user forgets to specify a required field on even just one service, I want to back-off and not create any beans. Can I accomplish this with @ConditionalOnProperty? I want something like:

@Configuration
@ConditionalOnProperty({"service[i].name", "service[i].version"})
class AutoConfigureServices {
....
} 

回答1:


This is the custom Condition I created. It needs some polishing to be more generic (ie not hardcoding strings), but worked great for me.

To use, I annotated my Configuration class with @Conditional(RequiredRepeatablePropertiesCondition.class)

public class RequiredRepeatablePropertiesCondition extends SpringBootCondition {

    private static final Logger LOGGER = LoggerFactory.getLogger(RequiredRepeatablePropertiesCondition.class.getName());

    public static final String[] REQUIRED_KEYS = {
            "my.services[i].version",
            "my.services[i].name"
    };

    @Override
    public ConditionOutcome getMatchOutcome(ConditionContext context, AnnotatedTypeMetadata metadata) {
        List<String> missingProperties = new ArrayList<>();
        RelaxedPropertyResolver resolver = new RelaxedPropertyResolver(context.getEnvironment());
        Map<String, Object> services = resolver.getSubProperties("my.services");
        if (services.size() == 0) {
            missingProperties.addAll(Arrays.asList(REQUIRED_KEYS));
            return getConditionOutcome(missingProperties);
        }
        //gather indexes to check: [0], [1], [3], etc
        Pattern p = Pattern.compile("\\[(\\d+)\\]");
        Set<String> uniqueIndexes = new HashSet<String>();
        for (String key : services.keySet()) {
            Matcher m = p.matcher(key);
            if (m.find()) {
                uniqueIndexes.add(m.group(1));
            }
        }
        //loop each index and check required props
        uniqueIndexes.forEach(index -> {
            for (String genericKey : REQUIRED_KEYS) {
                String multiServiceKey = genericKey.replace("[i]", "[" + index + "]");
                if (!resolver.containsProperty(multiServiceKey)) {
                    missingProperties.add(multiServiceKey);
                }
            }
        });
        return getConditionOutcome(missingProperties);
    }

    private ConditionOutcome getConditionOutcome(List<String> missingProperties) {
        if (missingProperties.isEmpty()) {
            return ConditionOutcome.match(ConditionMessage.forCondition(RequiredRepeatablePropertiesCondition.class.getCanonicalName())
                    .found("property", "properties")
                    .items(Arrays.asList(REQUIRED_KEYS)));
        }
        return ConditionOutcome.noMatch(
                ConditionMessage.forCondition(RequiredRepeatablePropertiesCondition.class.getCanonicalName())
            .didNotFind("property", "properties")
            .items(missingProperties)
        );
    }
}



回答2:


Old question, but I hope my answer will help for Spring2.x: Thanks to @Brian, I checked migration guide, where I was inspired by example code. This code works for me:

final List<String> services = Binder.get(context.getEnvironment()).bind("my.services", List.class).orElse(null);

I did try to get List of POJO (as AutoConfigureService) but my class differs from AutoConfigureServices. For that purpose, I used:

final Services services = Binder.get(context.getEnvironment()).bind("my.services", Services.class).orElse(null);

Well, keep playing :-D



来源:https://stackoverflow.com/questions/41916042/conditionalonproperty-for-lists-or-arrays

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!