From 3dfa7e9ac0ec070b52f580e11a279a7b7f64804f Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Fri, 7 Mar 2014 00:08:15 +0100 Subject: [PATCH] Avoid stack overflow in case of circular type reference Issue: SPR-11522 --- ...wiredAnnotationBeanPostProcessorTests.java | 49 ++++++++++++++++++- .../springframework/core/ResolvableType.java | 17 +++++-- 2 files changed, 61 insertions(+), 5 deletions(-) diff --git a/spring-beans/src/test/java/org/springframework/beans/factory/annotation/AutowiredAnnotationBeanPostProcessorTests.java b/spring-beans/src/test/java/org/springframework/beans/factory/annotation/AutowiredAnnotationBeanPostProcessorTests.java index 1996f3ba831..8953633dff9 100644 --- a/spring-beans/src/test/java/org/springframework/beans/factory/annotation/AutowiredAnnotationBeanPostProcessorTests.java +++ b/spring-beans/src/test/java/org/springframework/beans/factory/annotation/AutowiredAnnotationBeanPostProcessorTests.java @@ -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

{ + } + + + public interface StockMovementInstruction { + } + + + public interface StockMovementDao { + } + + + public static class StockMovementImpl

implements StockMovement

{ + } + + + public static class StockMovementInstructionImpl implements StockMovementInstruction { + } + + + public static class StockMovementDaoImpl implements StockMovementDao { + } + + + public static class StockServiceImpl { + + @Autowired + private StockMovementDao stockMovementDao; + } + } diff --git a/spring-core/src/main/java/org/springframework/core/ResolvableType.java b/spring-core/src/main/java/org/springframework/core/ResolvableType.java index 241de0ddcff..b002c1ba879 100644 --- a/spring-core/src/main/java/org/springframework/core/ResolvableType.java +++ b/spring-core/src/main/java/org/springframework/core/ResolvableType.java @@ -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 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(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; } }