Avoid stack overflow in case of circular type reference

Issue: SPR-11522
This commit is contained in:
Juergen Hoeller 2014-03-07 00:08:15 +01:00
parent 93c8b7ab04
commit 3dfa7e9ac0
2 changed files with 61 additions and 5 deletions

View File

@ -1,5 +1,5 @@
/*
* Copyright 2002-2013 the original author or authors.
* Copyright 2002-2014 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.
@ -1700,6 +1700,22 @@ public class AutowiredAnnotationBeanPostProcessorTests {
assertSame(bean2, bean1.gi2);
}
@Test
public void testCircularTypeReference() {
DefaultListableBeanFactory bf = new DefaultListableBeanFactory();
bf.setAutowireCandidateResolver(new QualifierAnnotationAutowireCandidateResolver());
AutowiredAnnotationBeanPostProcessor bpp = new AutowiredAnnotationBeanPostProcessor();
bpp.setBeanFactory(bf);
bf.addBeanPostProcessor(bpp);
bf.registerBeanDefinition("bean1", new RootBeanDefinition(StockServiceImpl.class));
bf.registerBeanDefinition("bean2", new RootBeanDefinition(StockMovementDaoImpl.class));
bf.registerBeanDefinition("bean3", new RootBeanDefinition(StockMovementImpl.class));
bf.registerBeanDefinition("bean4", new RootBeanDefinition(StockMovementInstructionImpl.class));
StockServiceImpl service = bf.getBean(StockServiceImpl.class);
assertSame(bf.getBean(StockMovementDaoImpl.class), service.stockMovementDao);
}
public static class ResourceInjectionBean {
@ -2650,4 +2666,35 @@ public class AutowiredAnnotationBeanPostProcessorTests {
}
}
public interface StockMovement<P extends StockMovementInstruction> {
}
public interface StockMovementInstruction<C extends StockMovement> {
}
public interface StockMovementDao<S extends StockMovement> {
}
public static class StockMovementImpl<P extends StockMovementInstruction> implements StockMovement<P> {
}
public static class StockMovementInstructionImpl<C extends StockMovement> implements StockMovementInstruction<C> {
}
public static class StockMovementDaoImpl<E extends StockMovement> implements StockMovementDao<E> {
}
public static class StockServiceImpl {
@Autowired
private StockMovementDao<StockMovement> stockMovementDao;
}
}

View File

@ -28,6 +28,7 @@ import java.lang.reflect.Type;
import java.lang.reflect.TypeVariable;
import java.lang.reflect.WildcardType;
import java.util.Collection;
import java.util.IdentityHashMap;
import java.util.Map;
import org.springframework.core.SerializableTypeWrapper.FieldTypeProvider;
@ -188,10 +189,10 @@ public final class ResolvableType implements Serializable {
* @return {@code true} if the specified {@code type} can be assigned to this {@code type}
*/
public boolean isAssignableFrom(ResolvableType type) {
return isAssignableFrom(type, false);
return isAssignableFrom(type, null);
}
private boolean isAssignableFrom(ResolvableType type, boolean checkingGeneric) {
private boolean isAssignableFrom(ResolvableType type, Map<ResolvableType, ResolvableType> matchedBefore) {
Assert.notNull(type, "Type must not be null");
// If we cannot resolve types, we are not assignable
@ -204,6 +205,10 @@ public final class ResolvableType implements Serializable {
return (type.isArray() && getComponentType().isAssignableFrom(type.getComponentType()));
}
if (matchedBefore != null && matchedBefore.get(this) == type) {
return true;
}
// Deal with wildcard bounds
WildcardBounds ourBounds = WildcardBounds.get(this);
WildcardBounds typeBounds = WildcardBounds.get(type);
@ -220,7 +225,7 @@ public final class ResolvableType implements Serializable {
}
// Main assignability check about to follow
boolean exactMatch = checkingGeneric;
boolean exactMatch = (matchedBefore != null); // We're checking nested generic variables now...
boolean checkGenerics = true;
Class<?> ourResolved = null;
if (this.type instanceof TypeVariable) {
@ -265,8 +270,12 @@ public final class ResolvableType implements Serializable {
if (ourGenerics.length != typeGenerics.length) {
return false;
}
if (matchedBefore == null) {
matchedBefore = new IdentityHashMap<ResolvableType, ResolvableType>(1);
}
matchedBefore.put(this, type);
for (int i = 0; i < ourGenerics.length; i++) {
if (!ourGenerics[i].isAssignableFrom(typeGenerics[i], true)) {
if (!ourGenerics[i].isAssignableFrom(typeGenerics[i], matchedBefore)) {
return false;
}
}