Skip to content
This repository was archived by the owner on Feb 23, 2023. It is now read-only.

Commit 3459117

Browse files
committed
Merge pull request #1420 from kazuki43zoo
* pr/1420: Polish "Fix StackOverflowError when processing ConfigurationProperties" Fix StackOverflowError when processing ConfigurationProperties Closes gh-1420
2 parents 79b9851 + b264eae commit 3459117

File tree

2 files changed

+81
-10
lines changed

2 files changed

+81
-10
lines changed

spring-aot/src/main/java/org/springframework/boot/context/properties/ConfigurationPropertiesNativeConfigurationProcessor.java

+26-9
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2019-2021 the original author or authors.
2+
* Copyright 2019-2022 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -26,8 +26,10 @@
2626
import java.lang.reflect.Method;
2727
import java.util.Arrays;
2828
import java.util.Collection;
29+
import java.util.HashSet;
2930
import java.util.List;
3031
import java.util.Map;
32+
import java.util.Set;
3133
import java.util.stream.Collectors;
3234

3335
import org.springframework.aot.context.bootstrap.generator.infrastructure.nativex.BeanFactoryNativeConfigurationProcessor;
@@ -67,7 +69,7 @@ public void process(ConfigurableListableBeanFactory beanFactory, NativeConfigura
6769

6870
private void processConfigurationProperties(NativeConfigurationRegistry registry, BeanDefinition beanDefinition) {
6971
Class<?> type = ClassUtils.getUserClass(beanDefinition.getResolvableType().toClass());
70-
TypeProcessor.process(type, registry);
72+
TypeProcessor.processConfigurationProperties(type, registry);
7173
}
7274

7375
/**
@@ -84,21 +86,36 @@ private static class TypeProcessor {
8486

8587
private final BeanInfo beanInfo;
8688

87-
private TypeProcessor(Class<?> type, boolean constructorBinding) {
89+
private final Set<Class<?>> seen;
90+
91+
private TypeProcessor(Class<?> type, boolean constructorBinding, Set<Class<?>> seen) {
8892
this.type = type;
8993
this.constructorBinding = constructorBinding;
9094
this.beanInfo = getBeanInfo(type);
95+
this.seen = seen;
96+
}
97+
98+
public static void processConfigurationProperties(Class<?> type, NativeConfigurationRegistry registry) {
99+
new TypeProcessor(type, hasConstructorBinding(type), new HashSet<>()).process(registry);
91100
}
92101

93-
public static void process(Class<?> type, NativeConfigurationRegistry registry) {
94-
new TypeProcessor(type, hasConstructorBinding(type)).process(registry);
102+
private void processNestedType(Class<?> type, NativeConfigurationRegistry registry) {
103+
processNestedType(type, hasConstructorBinding(type), registry);
104+
}
105+
106+
private void processNestedType(Class<?> type, boolean constructorBinding, NativeConfigurationRegistry registry) {
107+
new TypeProcessor(type, constructorBinding, this.seen).process(registry);
95108
}
96109

97110
private static boolean hasConstructorBinding(AnnotatedElement element) {
98111
return MergedAnnotations.from(element).isPresent(ConstructorBinding.class);
99112
}
100113

101114
private void process(NativeConfigurationRegistry registry) {
115+
if (this.seen.contains(this.type)) {
116+
return;
117+
}
118+
this.seen.add(this.type);
102119
Builder reflection = registry.reflection().forType(this.type);
103120
if (isClassOnlyReflectionType()) {
104121
return;
@@ -139,10 +156,10 @@ private void handleValueObjectProperties(NativeConfigurationRegistry registry, C
139156
if (propertyClass.equals(beanInfo.getBeanDescriptor().getBeanClass())) {
140157
return; // Prevent infinite recursion
141158
}
142-
new TypeProcessor(propertyType.resolve(), true).process(registry);
159+
processNestedType(propertyType.resolve(), true, registry);
143160
Class<?> nestedType = getNestedType(constructor.getParameters()[i].getName(), propertyType);
144161
if (nestedType != null) {
145-
new TypeProcessor(nestedType, true).process(registry);
162+
processNestedType(nestedType, true, registry);
146163
}
147164
}
148165
}
@@ -156,10 +173,10 @@ private void handleJavaBeanProperties(NativeConfigurationRegistry registry) {
156173
if (propertyClass.equals(beanInfo.getBeanDescriptor().getBeanClass())) {
157174
return; // Prevent infinite recursion
158175
}
159-
TypeProcessor.process(propertyClass, registry);
176+
processNestedType(propertyClass, registry);
160177
Class<?> nestedType = getNestedType(propertyDescriptor.getName(), propertyType);
161178
if (nestedType != null) {
162-
TypeProcessor.process(nestedType, registry);
179+
processNestedType(nestedType, registry);
163180
}
164181
}
165182
}

spring-aot/src/test/java/org/springframework/boot/context/properties/ConfigurationPropertiesNativeConfigurationProcessorTests.java

+55-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2019-2021 the original author or authors.
2+
* Copyright 2019-2022 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -235,6 +235,17 @@ void processConfigurationPropertiesWithWellKnownTypes() {
235235
.anySatisfy(classOnlyBinding(Environment.class)).hasSize(3);
236236
}
237237

238+
@Test
239+
void processConfigurationPropertiesWithCrossReference() {
240+
DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
241+
beanFactory.registerBeanDefinition("beanA", BeanDefinitionBuilder.rootBeanDefinition(SamplePropertiesWithCrossReference.class).getBeanDefinition());
242+
NativeConfigurationRegistry registry = process(beanFactory);
243+
assertThat(registry.reflection().reflectionEntries())
244+
.anySatisfy(javaBeanBinding(SamplePropertiesWithCrossReference.class))
245+
.anySatisfy(javaBeanBinding(CrossReferenceA.class))
246+
.anySatisfy(javaBeanBinding(CrossReferenceB.class)).hasSize(3);
247+
}
248+
238249
private Consumer<DefaultNativeReflectionEntry> classOnlyBinding(Class<?> type) {
239250
return (entry) -> {
240251
assertThat(entry.getType()).isEqualTo(type);
@@ -523,4 +534,47 @@ public ImmutableRecursive(ImmutableRecursive recursive) {
523534

524535
}
525536

537+
@ConfigurationProperties("crossreference")
538+
static class SamplePropertiesWithCrossReference {
539+
540+
@NestedConfigurationProperty
541+
private CrossReferenceA crossReferenceA;
542+
543+
public void setCrossReferenceA(CrossReferenceA crossReferenceA) {
544+
this.crossReferenceA = crossReferenceA;
545+
}
546+
547+
public CrossReferenceA getCrossReferenceA() {
548+
return crossReferenceA;
549+
}
550+
}
551+
552+
static class CrossReferenceA {
553+
554+
private CrossReferenceB crossReferenceB;
555+
556+
public void setCrossReferenceB(CrossReferenceB crossReferenceB) {
557+
this.crossReferenceB = crossReferenceB;
558+
}
559+
560+
public CrossReferenceB getCrossReferenceB() {
561+
return crossReferenceB;
562+
}
563+
564+
}
565+
566+
static class CrossReferenceB {
567+
568+
private CrossReferenceA crossReferenceA;
569+
570+
public void setCrossReferenceA(CrossReferenceA crossReferenceA) {
571+
this.crossReferenceA = crossReferenceA;
572+
}
573+
574+
public CrossReferenceA getCrossReferenceA() {
575+
return crossReferenceA;
576+
}
577+
578+
}
579+
526580
}

0 commit comments

Comments
 (0)