Fix nested type discovery in ConfigurationPropertiesReflectionHintsProcessor

The old implementation picked up nested types only if they have been
annotated with NestedConfigurationProperty, which not all nested
properties are. Now the processor takes into account all referenced
types. It ignores only types in the java package and interfaces.

This commit also moves some of the tests from
ConfigurationPropertiesBeanFactoryInitializationAotProcessorTests to
ConfigurationPropertiesReflectionHintsProcessorTests for easier
discoverability

Closes gh-31708
This commit is contained in:
Moritz Halbritter 2022-07-20 16:39:15 +02:00
parent 81fdb76620
commit 785588826e
3 changed files with 458 additions and 347 deletions

View File

@ -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);

View File

@ -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<TypeHint> 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<TypeHint> 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<TypeHint> 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<TypeHint> typeHints = runtimeHints.reflection().typeHints().toList();
assertThat(typeHints).anySatisfy(javaBeanBinding(SamplePropertiesWithSimpleList.class));
assertThat(typeHints).hasSize(2);
}
@Test
void processValueObjectConfigurationProperties() {
RuntimeHints runtimeHints = process(SampleImmutableProperties.class);
List<TypeHint> 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<TypeHint> 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<String, Address> getAddresses() {
return Collections.emptyMap();
}
}
@ConfigurationProperties("test")
public static class SamplePropertiesWithList {
public List<Address> getAllAddresses() {
return Collections.emptyList();
}
}
@ConfigurationProperties("test")
public static class SamplePropertiesWithSimpleList {
public List<String> 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<Person> family;
SampleImmutablePropertiesWithList(List<Person> 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")

View File

@ -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<String> stringList = new ArrayList<>();
public List<String> 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<Nested> nestedList;
public List<Nested> getNestedList() {
return this.nestedList;
}
public void setNestedList(List<Nested> nestedList) {
this.nestedList = nestedList;
}
}
@ConfigurationProperties
public static class NestedMapProperties {
private Map<String, Nested> nestedMap = new HashMap<>();
public Map<String, Nested> 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;
}
}
}