Support nested @PropertyMapping annotations
Update `AnnotationsPropertySource` so that nested annotations are supported. Prior to this commit, annotations annotated with `@PropertyMapping` that contained nested annotation attributes would result in instances of `TypeMappedAnnotation` being used as properties. This usually led to errors due to not being able to convert those to Strings. This commit makes it so that nested annotations are recursively mapped to properties. This should allow for more complex configuration to be mapped from annotations. See gh-23146
This commit is contained in:
parent
f60f3cb38e
commit
edf4c833c2
|
@ -90,7 +90,7 @@ public class AnnotationsPropertySource extends EnumerablePropertySource<Class<?>
|
|||
}
|
||||
}
|
||||
String name = getName(prefix, attributeMapping, attribute);
|
||||
putProperties(name, value.get(), properties);
|
||||
putProperties(name, skip, value.get(), properties);
|
||||
}
|
||||
|
||||
private String getName(String prefix, MergedAnnotation<?> attributeMapping, Method attribute) {
|
||||
|
@ -118,11 +118,17 @@ public class AnnotationsPropertySource extends EnumerablePropertySource<Class<?>
|
|||
return postfix;
|
||||
}
|
||||
|
||||
private void putProperties(String name, Object value, Map<String, Object> properties) {
|
||||
private void putProperties(String name, SkipPropertyMapping defaultSkip, Object value,
|
||||
Map<String, Object> properties) {
|
||||
if (ObjectUtils.isArray(value)) {
|
||||
Object[] array = ObjectUtils.toObjectArray(value);
|
||||
for (int i = 0; i < array.length; i++) {
|
||||
properties.put(name + "[" + i + "]", array[i]);
|
||||
putProperties(name + "[" + i + "]", defaultSkip, array[i], properties);
|
||||
}
|
||||
}
|
||||
else if (value instanceof MergedAnnotation<?>) {
|
||||
for (Method attribute : ((MergedAnnotation<?>) value).getType().getDeclaredMethods()) {
|
||||
collectProperties(name, defaultSkip, (MergedAnnotation<?>) value, attribute, properties);
|
||||
}
|
||||
}
|
||||
else {
|
||||
|
|
|
@ -21,6 +21,9 @@ import java.lang.annotation.RetentionPolicy;
|
|||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.boot.test.autoconfigure.properties.AnnotationsPropertySourceTests.DeeplyNestedAnnotations.Level1;
|
||||
import org.springframework.boot.test.autoconfigure.properties.AnnotationsPropertySourceTests.DeeplyNestedAnnotations.Level2;
|
||||
import org.springframework.boot.test.autoconfigure.properties.AnnotationsPropertySourceTests.NestedAnnotations.Entry;
|
||||
import org.springframework.core.annotation.AliasFor;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
@ -172,6 +175,26 @@ class AnnotationsPropertySourceTests {
|
|||
assertThat(source.containsProperty("testenum.value")).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
void nestedAnnotationsMapped() {
|
||||
AnnotationsPropertySource source = new AnnotationsPropertySource(PropertyMappedWithNestedAnnotations.class);
|
||||
assertThat(source.getProperty("testnested")).isNull();
|
||||
assertThat(source.getProperty("testnested.entries[0]")).isNull();
|
||||
assertThat(source.getProperty("testnested.entries[0].value")).isEqualTo("one");
|
||||
assertThat(source.getProperty("testnested.entries[1]")).isNull();
|
||||
assertThat(source.getProperty("testnested.entries[1].value")).isEqualTo("two");
|
||||
}
|
||||
|
||||
@Test
|
||||
void deeplyNestedAnnotationsMapped() {
|
||||
AnnotationsPropertySource source = new AnnotationsPropertySource(
|
||||
PropertyMappedWithDeeplyNestedAnnotations.class);
|
||||
assertThat(source.getProperty("testdeeplynested")).isNull();
|
||||
assertThat(source.getProperty("testdeeplynested.level1")).isNull();
|
||||
assertThat(source.getProperty("testdeeplynested.level1.level2")).isNull();
|
||||
assertThat(source.getProperty("testdeeplynested.level1.level2.value")).isEqualTo("level2");
|
||||
}
|
||||
|
||||
static class NoAnnotation {
|
||||
|
||||
}
|
||||
|
@ -396,4 +419,51 @@ class AnnotationsPropertySourceTests {
|
|||
|
||||
}
|
||||
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@PropertyMapping("testnested")
|
||||
@interface NestedAnnotations {
|
||||
|
||||
Entry[] entries();
|
||||
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@interface Entry {
|
||||
|
||||
String value();
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@NestedAnnotations(entries = { @Entry("one"), @Entry("two") })
|
||||
static class PropertyMappedWithNestedAnnotations {
|
||||
|
||||
}
|
||||
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@PropertyMapping("testdeeplynested")
|
||||
@interface DeeplyNestedAnnotations {
|
||||
|
||||
Level1 level1();
|
||||
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@interface Level1 {
|
||||
|
||||
Level2 level2();
|
||||
|
||||
}
|
||||
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@interface Level2 {
|
||||
|
||||
String value();
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@DeeplyNestedAnnotations(level1 = @Level1(level2 = @Level2("level2")))
|
||||
static class PropertyMappedWithDeeplyNestedAnnotations {
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue