Skip to content

Propose new spring.autoconfigure.exclusion property binding as Map to support multiple property sources #41669

@tmoschou

Description

@tmoschou

Currently the spring.autoconfigure.exclude property is bound as a List<Class<?>>. This is a problem as collections cannot be merged across different property sources or individual elements removed. Additionally it requires property sources with higher precedence to have global knowledge of all preexisting exclusion from other sources in order to append a new exclusion.

This is a frequent pain-point for us and others. See #27414 - Consider merging spring.autoconfigure.exclude from multiple profiles and #9137

Understandably merging / overriding logic for collections is difficult - refer to #12444 (comment)

I propose a new configuration property (E.g. spring.autoconfigure.exclusions or perhaps some other property if too similarly named) that is bound as a Map<Class<?>, Boolean>. Binding to a map will allow merging from different property sources and profiles. E.g.

---
spring:
  config.activate.on-profile: nosecurity
  autoconfigure:
    exclusions: 
      org.springframework.boot.autoconfigure.security.servlet.SecurityAutoConfiguration: true
      org.springframework.boot.actuate.autoconfigure.security.servlet.ManagementWebSecurityAutoConfiguration: true
 
---
spring:
  config.activate.on-profile: noredis
  autoconfigure:
    exclusions: 
      org.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration: true

Including the class name in the key is not too different from the logging.level.<class> properties and similar to management.health.<name>.enabled.

I have a custom EnvironmentPostProcessor that I think achieves my goal by rebinding the spring.autoconfigure.exclude property from my custom spring.autoconfigure.exclusions property? But it would be nice if spring-boot had first class support for this. For posterity here it is

@Order(Ordered.LOWEST_PRECEDENCE)
public class AutoConfigureExcludeEnvironmentPostProcessor implements EnvironmentPostProcessor {

    private static final String SPRING_AUTOCONFIGURE_EXCLUDE = "spring.autoconfigure.exclude";
    private static final String SPRING_AUTOCONFIGURE_EXCLUDEMAP = "spring.autoconfigure.exclusion";

    @Override
    public void postProcessEnvironment(ConfigurableEnvironment environment, SpringApplication application) {
        Binder binder = Binder.get(environment);
        Set<String> disabled = new HashSet<>();
        binder.bind(SPRING_AUTOCONFIGURE_EXCLUDE, Bindable.listOf(String.class)).ifBound(disabled::addAll);
        Map<String, Boolean> excludeMap =
            binder.bind(SPRING_AUTOCONFIGURE_EXCLUDEMAP, Bindable.mapOf(String.class, Boolean.class))
                .orElseGet(Collections::emptyMap);

        for (Map.Entry<String, Boolean> entry : excludeMap.entrySet()) {
            if (entry.getValue()) {
                disabled.add(entry.getKey());
            } else {
                // override if class is present in 'spring.autoconfigure.exclude' property
                disabled.remove(entry.getKey());
            }
        }

        if (!disabled.isEmpty() || environment.containsProperty(SPRING_AUTOCONFIGURE_EXCLUDE)) {
            environment.getPropertySources().addFirst(new MapPropertySource(
                AutoConfigureExcludeEnvironmentPostProcessor.class.getSimpleName(),
                Map.of(SPRING_AUTOCONFIGURE_EXCLUDE, disabled)
            ));
        }
    }
}

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions