mirror of
https://github.com/spring-projects/spring-boot.git
synced 2024-08-29 03:06:45 +08:00
Avoid infinite cycle resolving generic type that refers itself
This commit improves type resolution for a unresolved generic type that uses itself in its upper bound declaration. Closes gh-16451
This commit is contained in:
parent
8a04e2cc86
commit
fbb5ffe0a4
@ -248,15 +248,28 @@ class TypeUtils {
|
|||||||
TypeMirror typeMirror = descriptor.resolveGeneric(t);
|
TypeMirror typeMirror = descriptor.resolveGeneric(t);
|
||||||
if (typeMirror != null) {
|
if (typeMirror != null) {
|
||||||
if (typeMirror instanceof TypeVariable) {
|
if (typeMirror instanceof TypeVariable) {
|
||||||
// Still unresolved, let's use upper bound
|
TypeVariable typeVariable = (TypeVariable) typeMirror;
|
||||||
return visit(((TypeVariable) typeMirror).getUpperBound(), descriptor);
|
// Still unresolved, let's use the upper bound, checking first if
|
||||||
|
// a cycle may exist
|
||||||
|
if (!hasCycle(typeVariable)) {
|
||||||
|
return visit(typeVariable.getUpperBound(), descriptor);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
return visit(typeMirror, descriptor);
|
return visit(typeMirror, descriptor);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Unresolved generics, use upper bound
|
// Fallback to simple representation of the upper bound
|
||||||
return visit(t.getUpperBound(), descriptor);
|
return defaultAction(t.getUpperBound(), descriptor);
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean hasCycle(TypeVariable variable) {
|
||||||
|
TypeMirror upperBound = variable.getUpperBound();
|
||||||
|
if (upperBound instanceof DeclaredType) {
|
||||||
|
return ((DeclaredType) upperBound).getTypeArguments().stream()
|
||||||
|
.anyMatch((candidate) -> candidate.equals(variable));
|
||||||
|
}
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -46,8 +46,10 @@ import org.springframework.boot.configurationsample.endpoint.SimpleEndpoint;
|
|||||||
import org.springframework.boot.configurationsample.endpoint.SpecificEndpoint;
|
import org.springframework.boot.configurationsample.endpoint.SpecificEndpoint;
|
||||||
import org.springframework.boot.configurationsample.endpoint.incremental.IncrementalEndpoint;
|
import org.springframework.boot.configurationsample.endpoint.incremental.IncrementalEndpoint;
|
||||||
import org.springframework.boot.configurationsample.generic.AbstractGenericProperties;
|
import org.springframework.boot.configurationsample.generic.AbstractGenericProperties;
|
||||||
|
import org.springframework.boot.configurationsample.generic.ComplexGenericProperties;
|
||||||
import org.springframework.boot.configurationsample.generic.SimpleGenericProperties;
|
import org.springframework.boot.configurationsample.generic.SimpleGenericProperties;
|
||||||
import org.springframework.boot.configurationsample.generic.UnresolvedGenericProperties;
|
import org.springframework.boot.configurationsample.generic.UnresolvedGenericProperties;
|
||||||
|
import org.springframework.boot.configurationsample.generic.UpperBoundGenericPojo;
|
||||||
import org.springframework.boot.configurationsample.incremental.BarProperties;
|
import org.springframework.boot.configurationsample.incremental.BarProperties;
|
||||||
import org.springframework.boot.configurationsample.incremental.FooProperties;
|
import org.springframework.boot.configurationsample.incremental.FooProperties;
|
||||||
import org.springframework.boot.configurationsample.incremental.RenamedBarProperties;
|
import org.springframework.boot.configurationsample.incremental.RenamedBarProperties;
|
||||||
@ -524,6 +526,21 @@ public class ConfigurationMetadataAnnotationProcessorTests {
|
|||||||
assertThat(metadata.getItems()).hasSize(3);
|
assertThat(metadata.getItems()).hasSize(3);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void complexGenericProperties() {
|
||||||
|
ConfigurationMetadata metadata = compile(ComplexGenericProperties.class);
|
||||||
|
assertThat(metadata).has(
|
||||||
|
Metadata.withGroup("generic").fromSource(ComplexGenericProperties.class));
|
||||||
|
assertThat(metadata).has(
|
||||||
|
Metadata.withGroup("generic.test").ofType(UpperBoundGenericPojo.class)
|
||||||
|
.fromSource(ComplexGenericProperties.class));
|
||||||
|
assertThat(metadata).has(Metadata
|
||||||
|
.withProperty("generic.test.mappings",
|
||||||
|
"java.util.Map<java.lang.Enum<T>,java.lang.String>")
|
||||||
|
.fromSource(UpperBoundGenericPojo.class));
|
||||||
|
assertThat(metadata.getItems()).hasSize(3);
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void unresolvedGenericProperties() {
|
public void unresolvedGenericProperties() {
|
||||||
ConfigurationMetadata metadata = compile(AbstractGenericProperties.class,
|
ConfigurationMetadata metadata = compile(AbstractGenericProperties.class,
|
||||||
|
@ -0,0 +1,43 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2012-2019 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.configurationsample.generic;
|
||||||
|
|
||||||
|
import org.springframework.boot.configurationsample.ConfigurationProperties;
|
||||||
|
import org.springframework.boot.configurationsample.NestedConfigurationProperty;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* More advanced generic setup.
|
||||||
|
*
|
||||||
|
* @author Stephane Nicoll
|
||||||
|
*/
|
||||||
|
@ConfigurationProperties("generic")
|
||||||
|
public class ComplexGenericProperties {
|
||||||
|
|
||||||
|
@NestedConfigurationProperty
|
||||||
|
private UpperBoundGenericPojo<Test> test = new UpperBoundGenericPojo<>();
|
||||||
|
|
||||||
|
public UpperBoundGenericPojo<Test> getTest() {
|
||||||
|
return this.test;
|
||||||
|
}
|
||||||
|
|
||||||
|
public enum Test {
|
||||||
|
|
||||||
|
ONE, TWO, THREE
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,35 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2012-2019 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.configurationsample.generic;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A pojo with a complex generic signature.
|
||||||
|
*
|
||||||
|
* @author Stephane Nicoll
|
||||||
|
*/
|
||||||
|
public class UpperBoundGenericPojo<T extends Enum<T>> {
|
||||||
|
|
||||||
|
private final Map<T, String> mappings = new HashMap<>();
|
||||||
|
|
||||||
|
public Map<T, String> getMappings() {
|
||||||
|
return this.mappings;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user