diff --git a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/properties/ConfigurationPropertiesReflectionHintsProcessor.java b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/properties/ConfigurationPropertiesReflectionHintsProcessor.java index fae939d638..797456c028 100644 --- a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/properties/ConfigurationPropertiesReflectionHintsProcessor.java +++ b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/properties/ConfigurationPropertiesReflectionHintsProcessor.java @@ -21,6 +21,7 @@ import java.beans.IntrospectionException; import java.beans.Introspector; import java.beans.PropertyDescriptor; import java.lang.reflect.Constructor; +import java.lang.reflect.Field; import java.lang.reflect.Method; import java.util.Arrays; import java.util.Collection; @@ -34,10 +35,12 @@ import org.springframework.beans.BeanInfoFactory; import org.springframework.beans.ExtendedBeanInfoFactory; import org.springframework.boot.context.properties.bind.Bindable; import org.springframework.core.ResolvableType; +import org.springframework.core.annotation.MergedAnnotations; +import org.springframework.util.ReflectionUtils; /** * Registers a given type on {@link ReflectionHints} for binding purposes, discovering any - * referenced types it may expose via a property. + * nested type it may expose via a property. * * @author Andy Wilkinson * @author Moritz Halbritter @@ -74,22 +77,12 @@ public final class ConfigurationPropertiesReflectionHintsProcessor { .process(reflectionHints); } - private void processType(Class type, ReflectionHints reflectionHints) { - if (isTypeIgnored(type)) { - return; - } - new ConfigurationPropertiesReflectionHintsProcessor(type, getBindConstructor(type, true), this.seen) - .process(reflectionHints); + private void processNestedType(Class type, ReflectionHints reflectionHints) { + processNestedType(type, getBindConstructor(type, true), reflectionHints); } - private boolean isTypeIgnored(Class type) { - if (type.getPackageName().startsWith("java.")) { - return true; - } - if (type.isInterface()) { - return true; - } - return false; + private void processNestedType(Class type, Constructor bindConstructor, ReflectionHints reflectionHints) { + new ConfigurationPropertiesReflectionHintsProcessor(type, bindConstructor, this.seen).process(reflectionHints); } private static Constructor getBindConstructor(Class type, boolean nestedType) { @@ -125,8 +118,9 @@ public final class ConfigurationPropertiesReflectionHintsProcessor { private void handleValueObjectProperties(ReflectionHints reflectionHints) { for (int i = 0; i < this.bindConstructor.getParameterCount(); i++) { + String propertyName = this.bindConstructor.getParameters()[i].getName(); ResolvableType propertyType = ResolvableType.forConstructorParameter(this.bindConstructor, i); - registerType(reflectionHints, propertyType); + handleProperty(reflectionHints, propertyName, propertyType); } } @@ -135,12 +129,16 @@ public final class ConfigurationPropertiesReflectionHintsProcessor { Method readMethod = propertyDescriptor.getReadMethod(); if (readMethod != null) { ResolvableType propertyType = ResolvableType.forMethodReturnType(readMethod, this.type); - registerType(reflectionHints, propertyType); + String propertyName = propertyDescriptor.getName(); + if (isSetterMandatory(propertyName, propertyType) && propertyDescriptor.getWriteMethod() == null) { + continue; + } + handleProperty(reflectionHints, propertyName, propertyType); } } } - private void registerType(ReflectionHints reflectionHints, ResolvableType propertyType) { + private void handleProperty(ReflectionHints reflectionHints, String propertyName, ResolvableType propertyType) { Class propertyClass = propertyType.resolve(); if (propertyClass == null) { return; @@ -150,11 +148,25 @@ public final class ConfigurationPropertiesReflectionHintsProcessor { } Class componentType = getComponentType(propertyType); if (componentType != null) { - processType(componentType, reflectionHints); + // Can be a list of simple types + if (!isJavaType(componentType)) { + processNestedType(componentType, reflectionHints); + } } - else { - processType(propertyClass, reflectionHints); + else if (isNestedType(propertyName, propertyClass)) { + processNestedType(propertyClass, reflectionHints); + } + } + + private boolean isSetterMandatory(String propertyName, ResolvableType propertyType) { + Class propertyClass = propertyType.resolve(); + if (propertyClass == null) { + return true; } + if (getComponentType(propertyType) != null) { + return false; + } + return !isNestedType(propertyName, propertyClass); } private Class getComponentType(ResolvableType propertyType) { @@ -171,6 +183,27 @@ public final class ConfigurationPropertiesReflectionHintsProcessor { return null; } + /** + * Specify whether the specified property refer to a nested type. A nested type + * represents a sub-namespace that need to be fully resolved. + * @param propertyName the name of the property + * @param propertyType the type of the property + * @return whether the specified {@code propertyType} is a nested type + */ + private boolean isNestedType(String propertyName, Class propertyType) { + if (this.type.equals(propertyType.getDeclaringClass())) { + return true; + } + else { + Field field = ReflectionUtils.findField(this.type, propertyName); + return field != null && MergedAnnotations.from(field).isPresent(NestedConfigurationProperty.class); + } + } + + private boolean isJavaType(Class candidate) { + return candidate.getPackageName().startsWith("java."); + } + private static BeanInfo getBeanInfo(Class beanType) { try { BeanInfo beanInfo = beanInfoFactory.getBeanInfo(beanType); diff --git a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/context/properties/ConfigurationPropertiesBeanFactoryInitializationAotProcessorTests.java b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/context/properties/ConfigurationPropertiesBeanFactoryInitializationAotProcessorTests.java index cb1bb4ce15..5bbabde9ec 100644 --- a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/context/properties/ConfigurationPropertiesBeanFactoryInitializationAotProcessorTests.java +++ b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/context/properties/ConfigurationPropertiesBeanFactoryInitializationAotProcessorTests.java @@ -18,7 +18,9 @@ package org.springframework.boot.context.properties; import java.lang.reflect.Constructor; import java.util.Arrays; +import java.util.Collections; import java.util.List; +import java.util.Map; import java.util.function.Consumer; import org.junit.jupiter.api.Test; @@ -39,6 +41,10 @@ import org.springframework.beans.factory.aot.BeanFactoryInitializationCode; import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; import org.springframework.beans.factory.support.DefaultListableBeanFactory; import org.springframework.beans.factory.support.RootBeanDefinition; +import org.springframework.context.ApplicationContext; +import org.springframework.context.ApplicationContextAware; +import org.springframework.context.EnvironmentAware; +import org.springframework.core.env.Environment; import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.Mockito.mock; @@ -82,6 +88,65 @@ class ConfigurationPropertiesBeanFactoryInitializationAotProcessorTests { (hint) -> assertThat(hint.getMemberCategories()).contains(MemberCategory.INVOKE_DECLARED_METHODS)); } + @Test + void processJavaBeanConfigurationProperties() { + RuntimeHints runtimeHints = process(SampleProperties.class); + assertThat(runtimeHints.reflection().getTypeHint(SampleProperties.class)) + .satisfies(javaBeanBinding(SampleProperties.class)); + } + + @Test + void processJavaBeanConfigurationPropertiesWithSeveralConstructors() throws NoSuchMethodException { + RuntimeHints runtimeHints = process(SamplePropertiesWithSeveralConstructors.class); + assertThat(runtimeHints.reflection().getTypeHint(SamplePropertiesWithSeveralConstructors.class)) + .satisfies(javaBeanBinding(SamplePropertiesWithSeveralConstructors.class, + SamplePropertiesWithSeveralConstructors.class.getDeclaredConstructor())); + } + + @Test + void processJavaBeanConfigurationPropertiesWithMapOfPojo() { + RuntimeHints runtimeHints = process(SamplePropertiesWithMap.class); + List typeHints = runtimeHints.reflection().typeHints().toList(); + assertThat(typeHints).anySatisfy(javaBeanBinding(SamplePropertiesWithMap.class)); + assertThat(typeHints).anySatisfy(javaBeanBinding(Address.class)); + assertThat(typeHints).hasSize(3); + } + + @Test + void processJavaBeanConfigurationPropertiesWithListOfPojo() { + RuntimeHints runtimeHints = process(SamplePropertiesWithList.class); + List typeHints = runtimeHints.reflection().typeHints().toList(); + assertThat(typeHints).anySatisfy(javaBeanBinding(SamplePropertiesWithList.class)); + assertThat(typeHints).anySatisfy(javaBeanBinding(Address.class)); + assertThat(typeHints).hasSize(3); + } + + @Test + void processJavaBeanConfigurationPropertiesWitArrayOfPojo() { + RuntimeHints runtimeHints = process(SamplePropertiesWithArray.class); + List typeHints = runtimeHints.reflection().typeHints().toList(); + assertThat(typeHints).anySatisfy(javaBeanBinding(SamplePropertiesWithArray.class)); + assertThat(typeHints).anySatisfy(javaBeanBinding(Address.class)); + assertThat(typeHints).hasSize(3); + } + + @Test + void processJavaBeanConfigurationPropertiesWithListOfJavaType() { + RuntimeHints runtimeHints = process(SamplePropertiesWithSimpleList.class); + List typeHints = runtimeHints.reflection().typeHints().toList(); + assertThat(typeHints).anySatisfy(javaBeanBinding(SamplePropertiesWithSimpleList.class)); + assertThat(typeHints).hasSize(2); + } + + @Test + void processValueObjectConfigurationProperties() { + RuntimeHints runtimeHints = process(SampleImmutableProperties.class); + List typeHints = runtimeHints.reflection().typeHints().toList(); + assertThat(typeHints).anySatisfy(valueObjectBinding(SampleImmutableProperties.class, + SampleImmutableProperties.class.getDeclaredConstructors()[0])); + assertThat(typeHints).hasSize(2); + } + @Test void processValueObjectConfigurationPropertiesWithSpecificConstructor() throws NoSuchMethodException { RuntimeHints runtimeHints = process(SampleImmutablePropertiesWithSeveralConstructors.class); @@ -91,6 +156,17 @@ class ConfigurationPropertiesBeanFactoryInitializationAotProcessorTests { assertThat(typeHints).hasSize(2); } + @Test + void processValueObjectConfigurationPropertiesWithSeveralLayersOfPojo() { + RuntimeHints runtimeHints = process(SampleImmutablePropertiesWithList.class); + List typeHints = runtimeHints.reflection().typeHints().toList(); + assertThat(typeHints).anySatisfy(valueObjectBinding(SampleImmutablePropertiesWithList.class, + SampleImmutablePropertiesWithList.class.getDeclaredConstructors()[0])); + assertThat(typeHints).anySatisfy(valueObjectBinding(Person.class, Person.class.getDeclaredConstructors()[0])); + assertThat(typeHints).anySatisfy(valueObjectBinding(Address.class, Address.class.getDeclaredConstructors()[0])); + assertThat(typeHints).hasSize(4); + } + @Test void processConfigurationPropertiesWithNestedTypeNotUsedIsIgnored() { RuntimeHints runtimeHints = process(SamplePropertiesWithNested.class); @@ -98,6 +174,23 @@ class ConfigurationPropertiesBeanFactoryInitializationAotProcessorTests { .satisfies(javaBeanBinding(SamplePropertiesWithNested.class)); } + @Test + void processConfigurationPropertiesWithNestedExternalType() { + RuntimeHints runtimeHints = process(SamplePropertiesWithExternalNested.class); + assertThat(runtimeHints.reflection().typeHints()) + .anySatisfy(javaBeanBinding(SamplePropertiesWithExternalNested.class)) + .anySatisfy(javaBeanBinding(SampleType.class)).anySatisfy(javaBeanBinding(SampleType.Nested.class)) + .hasSize(4); + } + + @Test + void processConfigurationPropertiesWithRecursiveType() { + RuntimeHints runtimeHints = process(SamplePropertiesWithRecursive.class); + assertThat(runtimeHints.reflection().typeHints()) + .anySatisfy(javaBeanBinding(SamplePropertiesWithRecursive.class)) + .anySatisfy(javaBeanBinding(Recursive.class)).hasSize(3); + } + @Test void processValueObjectConfigurationPropertiesWithRecursiveType() { RuntimeHints runtimeHints = process(SampleImmutablePropertiesWithRecursive.class); @@ -109,6 +202,15 @@ class ConfigurationPropertiesBeanFactoryInitializationAotProcessorTests { .hasSize(3); } + @Test + void processConfigurationPropertiesWithWellKnownTypes() { + RuntimeHints runtimeHints = process(SamplePropertiesWithWellKnownTypes.class); + assertThat(runtimeHints.reflection().typeHints()) + .anySatisfy(javaBeanBinding(SamplePropertiesWithWellKnownTypes.class)) + // TODO + .hasSize(2); + } + @Test void processConfigurationPropertiesWithCrossReference() { RuntimeHints runtimeHints = process(SamplePropertiesWithCrossReference.class); @@ -177,6 +279,65 @@ class ConfigurationPropertiesBeanFactoryInitializationAotProcessorTests { } + @ConfigurationProperties("test") + public static class SamplePropertiesWithSeveralConstructors { + + SamplePropertiesWithSeveralConstructors() { + } + + SamplePropertiesWithSeveralConstructors(String ignored) { + } + + } + + @ConfigurationProperties("test") + public static class SamplePropertiesWithMap { + + public Map getAddresses() { + return Collections.emptyMap(); + } + + } + + @ConfigurationProperties("test") + public static class SamplePropertiesWithList { + + public List
getAllAddresses() { + return Collections.emptyList(); + } + + } + + @ConfigurationProperties("test") + public static class SamplePropertiesWithSimpleList { + + public List getNames() { + return Collections.emptyList(); + } + + } + + @ConfigurationProperties("test") + public static class SamplePropertiesWithArray { + + public Address[] getAllAddresses() { + return new Address[0]; + } + + } + + @ConfigurationProperties + public static class SampleImmutableProperties { + + @SuppressWarnings("unused") + private final String name; + + SampleImmutableProperties(String name) { + this.name = name; + } + + } + @ConfigurationProperties public static class SampleImmutablePropertiesWithSeveralConstructors { @@ -194,6 +355,18 @@ class ConfigurationPropertiesBeanFactoryInitializationAotProcessorTests { } + @ConfigurationProperties + public static class SampleImmutablePropertiesWithList { + + @SuppressWarnings("unused") + private final List family; + + SampleImmutablePropertiesWithList(List family) { + this.family = family; + } + + } + @ConfigurationProperties("nested") public static class SamplePropertiesWithNested { @@ -203,6 +376,48 @@ class ConfigurationPropertiesBeanFactoryInitializationAotProcessorTests { } + @ConfigurationProperties("nested") + public static class SamplePropertiesWithExternalNested { + + private String name; + + @NestedConfigurationProperty + private SampleType sampleType; + + public String getName() { + return this.name; + } + + public void setName(String name) { + this.name = name; + } + + public SampleType getSampleType() { + return this.sampleType; + } + + public void setSampleType(SampleType sampleType) { + this.sampleType = sampleType; + } + + } + + @ConfigurationProperties("recursive") + public static class SamplePropertiesWithRecursive { + + @NestedConfigurationProperty + private Recursive recursive; + + public Recursive getRecursive() { + return this.recursive; + } + + public void setRecursive(Recursive recursive) { + this.recursive = recursive; + } + + } + @ConfigurationProperties public static class SampleImmutablePropertiesWithRecursive { @@ -215,6 +430,84 @@ class ConfigurationPropertiesBeanFactoryInitializationAotProcessorTests { } + @ConfigurationProperties("wellKnownTypes") + public static class SamplePropertiesWithWellKnownTypes implements ApplicationContextAware, EnvironmentAware { + + private ApplicationContext applicationContext; + + private Environment environment; + + public ApplicationContext getApplicationContext() { + return this.applicationContext; + } + + @Override + public void setApplicationContext(ApplicationContext applicationContext) { + this.applicationContext = applicationContext; + } + + public Environment getEnvironment() { + return this.environment; + } + + @Override + public void setEnvironment(Environment environment) { + this.environment = environment; + } + + } + + public static class SampleType { + + private final Nested nested = new Nested(); + + public Nested getNested() { + return this.nested; + } + + static class Nested { + + } + + } + + public static class Address { + + } + + public static class Person { + + @SuppressWarnings("unused") + private final String firstName; + + @SuppressWarnings("unused") + private final String lastName; + + @NestedConfigurationProperty + private final Address address; + + Person(String firstName, String lastName, Address address) { + this.firstName = firstName; + this.lastName = lastName; + this.address = address; + } + + } + + public static class Recursive { + + private Recursive recursive; + + public Recursive getRecursive() { + return this.recursive; + } + + public void setRecursive(Recursive recursive) { + this.recursive = recursive; + } + + } + public static class ImmutableRecursive { @SuppressWarnings("unused") diff --git a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/context/properties/ConfigurationPropertiesReflectionHintsProcessorTests.java b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/context/properties/ConfigurationPropertiesReflectionHintsProcessorTests.java deleted file mode 100644 index 099a49f16c..0000000000 --- a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/context/properties/ConfigurationPropertiesReflectionHintsProcessorTests.java +++ /dev/null @@ -1,437 +0,0 @@ -/* - * Copyright 2012-2022 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.boot.context.properties; - -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -import org.junit.jupiter.api.Test; - -import org.springframework.aot.hint.MemberCategory; -import org.springframework.aot.hint.RuntimeHints; -import org.springframework.aot.hint.predicate.RuntimeHintsPredicates; -import org.springframework.beans.BeansException; -import org.springframework.beans.factory.BeanFactory; -import org.springframework.beans.factory.BeanFactoryAware; -import org.springframework.context.ApplicationContext; -import org.springframework.context.ApplicationContextAware; -import org.springframework.context.EnvironmentAware; -import org.springframework.core.env.Environment; -import org.springframework.util.ReflectionUtils; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.assertThatCode; - -/** - * Tests for {@link ConfigurationPropertiesReflectionHintsProcessor}. - * - * @author Moritz Halbritter - */ -class ConfigurationPropertiesReflectionHintsProcessorTests { - - @Test - void shouldRegisterNoArgConstructor() throws NoSuchMethodException { - RuntimeHints hints = runProcessor(NoArgsCtorProperties.class); - assertThat(RuntimeHintsPredicates.reflection() - .onConstructor(ReflectionUtils.accessibleConstructor(NoArgsCtorProperties.class))).accepts(hints); - } - - @Test - void shouldRegisterMemberCategories() { - RuntimeHints hints = runProcessor(NoArgsCtorProperties.class); - hasReflectionHintsForType(hints, NoArgsCtorProperties.class); - } - - @Test - void shouldNotRegisterJavaTypes() { - RuntimeHints hints = runProcessor(NoArgsCtorProperties.class); - assertThat(RuntimeHintsPredicates.reflection().onType(String.class)).rejects(hints); - } - - @Test - void shouldNotRegisterJavaTypesInLists() { - RuntimeHints hints = runProcessor(StringListProperties.class); - assertThat(RuntimeHintsPredicates.reflection().onType(String.class)).rejects(hints); - } - - @Test - void shouldRegisterConstructorBinding() throws NoSuchMethodException { - RuntimeHints hints = runProcessor(ConstructorBindingProperties.class); - assertThat(RuntimeHintsPredicates.reflection() - .onConstructor(ReflectionUtils.accessibleConstructor(ConstructorBindingProperties.class, String.class))) - .accepts(hints); - } - - @Test - void shouldRegisterConstructorBindingWithRecords() throws NoSuchMethodException { - RuntimeHints hints = runProcessor(RecordProperties.class); - assertThat(RuntimeHintsPredicates.reflection() - .onConstructor(ReflectionUtils.accessibleConstructor(RecordProperties.class, String.class))) - .accepts(hints); - } - - @Test - void shouldRegisterNestedTypes() { - RuntimeHints hints = runProcessor(NestedProperties.class); - hasReflectionHintsForType(hints, Nested.class); - } - - @Test - void shouldRegisterNestedTypesWhenNoSetterIsPresent() { - RuntimeHints hints = runProcessor(NestedNoSetterProperties.class); - hasReflectionHintsForType(hints, Nested.class); - } - - @Test - void shouldRegisterNestedTypesInLists() { - RuntimeHints hints = runProcessor(NestedListProperties.class); - hasReflectionHintsForType(hints, Nested.class); - } - - @Test - void shouldRegisterNestedTypesInMaps() { - RuntimeHints hints = runProcessor(NestedMapProperties.class); - hasReflectionHintsForType(hints, Nested.class); - } - - @Test - void shouldRegisterNestedTypesInArrays() { - RuntimeHints hints = runProcessor(NestedArrayProperties.class); - hasReflectionHintsForType(hints, Nested.class); - } - - @Test - void shouldRegisterNestedWithRecords() { - RuntimeHints hints = runProcessor(NestedRecordProperties.class); - hasReflectionHintsForType(hints, Nested.class); - } - - @Test - void shouldRegisterMultipleLevelsOfNested() { - RuntimeHints hints = runProcessor(NestedMultipleLevelsProperties.class); - hasReflectionHintsForType(hints, NestedLevel1.class); - hasReflectionHintsForType(hints, NestedLevel2.class); - hasReflectionHintsForType(hints, NestedLevel3.class); - } - - @Test - void shouldNotCrashOnCycles() { - assertThatCode(() -> runProcessor(CycleProperties.class)).doesNotThrowAnyException(); - } - - @Test - void shouldNotRegisterInterfaces() { - RuntimeHints hints = runProcessor(InterfaceProperties.class); - assertThat(RuntimeHintsPredicates.reflection().onType(SomeInterface.class)).rejects(hints); - } - - @Test - void shouldNotRegisterKnownTypes() { - RuntimeHints hints = runProcessor(AwareProperties.class); - assertThat(RuntimeHintsPredicates.reflection().onType(Environment.class)).rejects(hints); - assertThat(RuntimeHintsPredicates.reflection().onType(ApplicationContext.class)).rejects(hints); - assertThat(RuntimeHintsPredicates.reflection().onType(BeanFactory.class)).rejects(hints); - } - - private RuntimeHints runProcessor(Class type) { - RuntimeHints hints = new RuntimeHints(); - ConfigurationPropertiesReflectionHintsProcessor.processConfigurationProperties(type, hints.reflection()); - return hints; - } - - private void hasReflectionHintsForType(RuntimeHints hints, Class type) { - assertThat(RuntimeHintsPredicates.reflection().onType(type) - .withMemberCategories(MemberCategory.INVOKE_DECLARED_METHODS, MemberCategory.INVOKE_PUBLIC_METHODS)) - .accepts(hints); - } - - @ConfigurationProperties - public static class NoArgsCtorProperties { - - private String field; - - NoArgsCtorProperties() { - } - - NoArgsCtorProperties(String field) { - this.field = field; - } - - public String getField() { - return this.field; - } - - public void setField(String field) { - this.field = field; - } - - } - - @ConfigurationProperties - public static class StringListProperties { - - private final List stringList = new ArrayList<>(); - - public List getStringList() { - return this.stringList; - } - - } - - @ConfigurationProperties - public static class ConstructorBindingProperties { - - private final String field; - - ConstructorBindingProperties(String field) { - this.field = field; - } - - public String getField() { - return this.field; - } - - } - - @ConfigurationProperties - public record RecordProperties(String field) { - } - - @ConfigurationProperties - public static class NestedProperties { - - private Nested nested; - - public Nested getNested() { - return this.nested; - } - - public void setNested(Nested nested) { - this.nested = nested; - } - - } - - @ConfigurationProperties - public static class NestedNoSetterProperties { - - private final Nested nested = new Nested(); - - public Nested getNested() { - return this.nested; - } - - } - - @ConfigurationProperties - public static class NestedListProperties { - - private List nestedList; - - public List getNestedList() { - return this.nestedList; - } - - public void setNestedList(List nestedList) { - this.nestedList = nestedList; - } - - } - - @ConfigurationProperties - public static class NestedMapProperties { - - private Map nestedMap = new HashMap<>(); - - public Map getNestedMap() { - return this.nestedMap; - } - - } - - @ConfigurationProperties - public static class NestedArrayProperties { - - private Nested[] nestedArray; - - public Nested[] getNestedArray() { - return this.nestedArray; - } - - public void setNestedArray(Nested[] nestedArray) { - this.nestedArray = nestedArray; - } - - } - - @ConfigurationProperties - public record NestedRecordProperties(Nested nested) { - - } - - @ConfigurationProperties - public static class CycleProperties { - - private CycleProperties cycleProperties; - - public CycleProperties getCycleProperties() { - return this.cycleProperties; - } - - public void setCycleProperties(CycleProperties cycleProperties) { - this.cycleProperties = cycleProperties; - } - - } - - @ConfigurationProperties - public static class NestedMultipleLevelsProperties { - - private NestedLevel1 nestedLevel1; - - public NestedLevel1 getNestedLevel1() { - return this.nestedLevel1; - } - - public void setNestedLevel1(NestedLevel1 nestedLevel1) { - this.nestedLevel1 = nestedLevel1; - } - - } - - @ConfigurationProperties - public static class InterfaceProperties { - - private SomeInterface someInterface; - - public SomeInterface getSomeInterface() { - return this.someInterface; - } - - public void setSomeInterface(SomeInterface someInterface) { - this.someInterface = someInterface; - } - - } - - @ConfigurationProperties - public static class AwareProperties implements ApplicationContextAware, BeanFactoryAware, EnvironmentAware { - - private String field; - - private BeanFactory beanFactory; - - private ApplicationContext applicationContext; - - private Environment environment; - - @Override - public void setBeanFactory(BeanFactory beanFactory) throws BeansException { - this.beanFactory = beanFactory; - } - - @Override - public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { - this.applicationContext = applicationContext; - } - - @Override - public void setEnvironment(Environment environment) { - this.environment = environment; - } - - public String getField() { - return this.field; - } - - public BeanFactory getBeanFactory() { - return this.beanFactory; - } - - public ApplicationContext getApplicationContext() { - return this.applicationContext; - } - - public Environment getEnvironment() { - return this.environment; - } - - } - - interface SomeInterface { - - } - - public static class Nested { - - private String field; - - public String getField() { - return this.field; - } - - public void setField(String field) { - this.field = field; - } - - } - - public static class NestedLevel1 { - - private NestedLevel2 nestedLevel2; - - public NestedLevel2 getNestedLevel2() { - return this.nestedLevel2; - } - - public void setNestedLevel2(NestedLevel2 nestedLevel2) { - this.nestedLevel2 = nestedLevel2; - } - - } - - public static class NestedLevel2 { - - private NestedLevel3 nestedLevel3; - - public NestedLevel3 getNestedLevel3() { - return this.nestedLevel3; - } - - public void setNestedLevel3(NestedLevel3 nestedLevel3) { - this.nestedLevel3 = nestedLevel3; - } - - } - - public class NestedLevel3 { - - private String field; - - public String getField() { - return this.field; - } - - public void setField(String field) { - this.field = field; - } - - } - -}