Revert "Fix nested type discovery in ConfigurationPropertiesReflectionHintsProcessor"
This reverts commit 785588826e.
This commit is contained in:
@@ -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,13 +148,27 @@ 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) {
|
||||
Class<?> propertyClass = propertyType.toClass();
|
||||
if (propertyType.isArray()) {
|
||||
@@ -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);
|
||||
|
||||
@@ -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<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);
|
||||
@@ -91,6 +156,17 @@ 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);
|
||||
@@ -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<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 {
|
||||
|
||||
@@ -194,6 +355,18 @@ 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 {
|
||||
|
||||
@@ -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")
|
||||
|
||||
@@ -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<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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user