3

We are in the process of defining our micro service infrastructure across our company. We have created our own "parent", which defines the BOM for our services. Additionally, we have several "starter" projects and small libraries that can be used by our services. (Example, a service can include "starter-stream" to include the dependencies for Kafka).

The streaming utilities library provides its own AutoConfiguration for Kafka setup and requires that the default AutoConfiguration for Kafka to be disabled. This is straightforward, we can just require that any micro service that uses the library to add an "exclude".

What I am looking for is a way to do this programmatically, such that we do not have to add an exclude in EACH web service.

I know this is probably a unique situation, and one possible way I was thinking about doing this was adding an EnvironmentPostProcessor to our utility library that will add the exclusion to spring.autoconfigure.exclude. We can make this smart enough to concatenate the exclusion if the property already exists.

Is there a more elegant way of doing this type of thing?

2 Answers 2

5

I think what you suggest could be fine with an EnvironmentPostProcessor to modify the spring.auconfigure.exclude.

Another funky way could be is to subclass the org.springframework.boot.autoconfigure.AutoConfigurationImportSelector and override the org.springframework.boot.autoconfigure.AutoConfigurationImportSelector#getExclusions so that it combines already configured exclusions with the ones you add.

public class MyCustomSelector extends AutoConfigurationImportSelector {
  @Override
  protected Set<String> getExclusions(AnnotationMetadata metadata, AnnotationAttributes attributes) {
    Set<String> exclusions = super.getExclusions(metadata, attributes);
    exclusions.add("some.other.config.Configuration");
    return exclusions;
  }
}

and then you can just use it with @Import(MyCustomSelector.class).

1
  • To follow up on this, does this mean that if I want to override the default selector, that I have to have a custom variant of @SpringBootApplication that imports my selector rather than the default? Commented Sep 14, 2017 at 18:09
1

The solution we ended up landing on:

This class will create or concatenate entries to spring.autoconfigure.exclude

public class AutoConfigurationExclusion implements EnvironmentPostProcessor{

    private static final String SPRING_EXCLUDE_PROPERTY = "spring.autoconfigure.exclude";
    
    String exclusionList;
    
    public AutoConfigurationExclusion(Class<?> ... classes) {

        StringBuilder builder = new StringBuilder();
        for (Class<?> clazz : classes) {
            if (builder.length() > 0) {
                builder.append(",");
            }
            builder.append(clazz.getCanonicalName());
        }
        exclusionList = builder.toString();
    }
    
    public AutoConfigurationExclusion(String ... classes) {

        StringBuilder builder = new StringBuilder();
        for (String className : classes) {
            if (builder.length() > 0) {
                builder.append(",");
            }
            builder.append(className);
        }
        exclusionList = builder.toString();

    }
    
    @Override
    public void postProcessEnvironment(ConfigurableEnvironment environment, SpringApplication application) {
        String excludes = environment.getProperty(SPRING_EXCLUDE_PROPERTY);
        
        if (StringUtils.isBlank(excludes)) {
            excludes = exclusionList;
        } else {
            excludes = excludes + "," + exclusionList;
        }
        Map<String, Object> map = new HashMap<>();
        map.put(SPRING_EXCLUDE_PROPERTY, excludes);
        
        SpringPropertyOverrideHelper.overrideValues(environment.getPropertySources(), map);
    }

}

And the static helper to get/create a new property source to hold the "overridden" value(s) and that source is added as the first property source.

public class SpringPropertyOverrideHelper {

    private static final String PROPERTY_SOURCE_NAME = "overrideProperties";

    public static void overrideValues(MutablePropertySources propertySources, Map<String, Object> propertyMap) {
        MapPropertySource target = null;
        if (propertySources.contains(PROPERTY_SOURCE_NAME)) {
            PropertySource<?> source = propertySources.get(PROPERTY_SOURCE_NAME);
            if (source instanceof MapPropertySource) {
                target = (MapPropertySource) source;
                for (Map.Entry<String, Object> entry : propertyMap.entrySet()) {
                    target.getSource().put(entry.getKey(), entry.getValue());
                }
            }
        } else {
            target = new MapPropertySource(PROPERTY_SOURCE_NAME, propertyMap);
        }
        if (!propertySources.contains(PROPERTY_SOURCE_NAME)) {
            propertySources.addFirst(target);
        }
    }

    private SpringPropertyOverrideHelper() {
    }

}
1
  • 1
    This class should be registered in spring.factories somehow? Commented Feb 6, 2021 at 7:23

Not the answer you're looking for? Browse other questions tagged or ask your own question.