Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Support binding of default properties when an empty property is defined #48920

@philwebb

Description

@philwebb

A comment on issue #43437 (comment) along with #24133 shows that we're currently lacking some functionality in our binder support.

Sample use-case

The sample in #43437 (comment) has the following use-case.

Imagine you have existing properties that allow you to configure throttling. There's a simple type enum supporting fixed, none and doubling:

my-application:
  throttling:
    type: fixed
    duration: 2s

After a while, you realize you want to offer more properties to configure various aspects of the throttling strategy. The properties are different for each strategy you support. To do this you expand the properties, and programmatically enforce that only one is specified.

For example:

my-application:
  throttling:
    fixed:
      duration: 2s

or

my-application:
  throttling:
    doubling:
      min: 2s
      max: 2s

The following would be an invalid configuration and throw an exception because you can't have both fixed and doubling:

my-application:
  throttling:
    fixed:
      duration: 2s
    doubling:
      min: 2s
      max: 2s

The problem is, you also need properties to represent none. You want the following to work:

my-application:
  throttling:
    none: {}

This means you need to know the difference between my-application.throttling.none being specified as an empty object or missing entirely.

Initial Ideal (using @DefaultValue as an indicator)

The @DefaultValue annotation could be used to replace both null values (as it currently does) as well as empty values (empty strings, or empty collections). It would also be nice if the annotation could indicate if it always applies, or if it should only apply when the underlying PropertySource.containsProperty method returns true.

One possible option is to add replace and mustContainProperty attributes to @DefaultValue. The replace attribute would be an enum of NULL_VALUES or NULL_AND_EMPTY_VALUES. The mustContainProperty would be a boolean to indicate if the property must be contained in the underlying source.

In order to be completely useful, some changes would also be need to OriginTrackedYamlLoader (and probably the underlying Spring Framework class) so the empty objects are inserted into the Map with null values.

With the suggested annotations, this would be possible using:

public record ThrottlingProperties(
		@DefaultValue(replace = NULL_OR_EMPTY_VALUES, mustContainProperty = true) Fixed fixed,
		@DefaultValue(replace = NULL_OR_EMPTY_VALUES, mustContainProperty = true) Doubling doubling,
		@DefaultValue(replace = NULL_OR_EMPTY_VALUES, mustContainProperty = true) None none) {

	public ThrottlingProperties {
		MutuallyExclusiveConfigurationPropertiesException.throwIfMultipleNonNullValuesIn((values) -> {
			values.put("fixed", fixed);
			values.put("doubling", doubling);
			values.put("none", none);
		});
	}

	public record Fixed(Duration duration) {

	}

	public record Doubling(Duration min, Duration max) {

	}

	public record None() {

	}

}

Using NULL_OR_EMPTY_VALUES means that .properties files could also work (since they don't support null values):

my-application.throttling.none

Metadata

Metadata

Assignees

Labels

status: blockedAn issue that's blocked on an external project changestatus: team-onlyAn issue that's best handled directly by the team rather than a community contribution.type: enhancementA general enhancement

Type

No type

Projects

No projects

Relationships

None yet

Development

No branches or pull requests

Issue actions