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 797456c028..fae939d638 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,7 +21,6 @@ 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; @@ -35,12 +34,10 @@ 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 - * nested type it may expose via a property. + * referenced types it may expose via a property. * * @author Andy Wilkinson * @author Moritz Halbritter @@ -77,12 +74,22 @@ public final class ConfigurationPropertiesReflectionHintsProcessor { .process(reflectionHints); } - private void processNestedType(Class type, ReflectionHints reflectionHints) { - processNestedType(type, getBindConstructor(type, true), 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, Constructor bindConstructor, ReflectionHints reflectionHints) { - new ConfigurationPropertiesReflectionHintsProcessor(type, bindConstructor, this.seen).process(reflectionHints); + private boolean isTypeIgnored(Class type) { + if (type.getPackageName().startsWith("java.")) { + return true; + } + if (type.isInterface()) { + return true; + } + return false; } private static Constructor getBindConstructor(Class type, boolean nestedType) { @@ -118,9 +125,8 @@ 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); - handleProperty(reflectionHints, propertyName, propertyType); + registerType(reflectionHints, propertyType); } } @@ -129,16 +135,12 @@ public final class ConfigurationPropertiesReflectionHintsProcessor { Method readMethod = propertyDescriptor.getReadMethod(); if (readMethod != null) { ResolvableType propertyType = ResolvableType.forMethodReturnType(readMethod, this.type); - String propertyName = propertyDescriptor.getName(); - if (isSetterMandatory(propertyName, propertyType) && propertyDescriptor.getWriteMethod() == null) { - continue; - } - handleProperty(reflectionHints, propertyName, propertyType); + registerType(reflectionHints, propertyType); } } } - private void handleProperty(ReflectionHints reflectionHints, String propertyName, ResolvableType propertyType) { + private void registerType(ReflectionHints reflectionHints, ResolvableType propertyType) { Class propertyClass = propertyType.resolve(); if (propertyClass == null) { return; @@ -148,27 +150,13 @@ public final class ConfigurationPropertiesReflectionHintsProcessor { } Class componentType = getComponentType(propertyType); if (componentType != null) { - // Can be a list of simple types - if (!isJavaType(componentType)) { - processNestedType(componentType, reflectionHints); - } + processType(componentType, reflectionHints); } - else if (isNestedType(propertyName, propertyClass)) { - processNestedType(propertyClass, reflectionHints); + else { + processType(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) { Class propertyClass = propertyType.toClass(); if (propertyType.isArray()) { @@ -183,27 +171,6 @@ 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 5bbabde9ec..cb1bb4ce15 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,9 +18,7 @@ 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; @@ -41,10 +39,6 @@ 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; @@ -88,65 +82,6 @@ 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); @@ -156,17 +91,6 @@ 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); @@ -174,23 +98,6 @@ 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); @@ -202,15 +109,6 @@ 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); @@ -279,65 +177,6 @@ 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 { @@ -355,18 +194,6 @@ 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 { @@ -376,48 +203,6 @@ 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 { @@ -430,84 +215,6 @@ 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 new file mode 100644 index 0000000000..099a49f16c --- /dev/null +++ b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/context/properties/ConfigurationPropertiesReflectionHintsProcessorTests.java @@ -0,0 +1,437 @@ +/* + * 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; + } + + } + +}