From 4cd43dc79308f536b8a937055c0fe7c3106633ce Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Tue, 17 Apr 2018 15:32:03 +0200 Subject: [PATCH] Workaround for generic parameter types on inner class constructors Issue: SPR-16734 --- .../ConfigurationClassPostProcessorTests.java | 53 ++++++++++++++++++- .../springframework/core/MethodParameter.java | 22 +++++--- .../core/MethodParameterTests.java | 25 +++++++-- 3 files changed, 88 insertions(+), 12 deletions(-) diff --git a/spring-context/src/test/java/org/springframework/context/annotation/ConfigurationClassPostProcessorTests.java b/spring-context/src/test/java/org/springframework/context/annotation/ConfigurationClassPostProcessorTests.java index 56373e04eb1..19f96e8caf0 100644 --- a/spring-context/src/test/java/org/springframework/context/annotation/ConfigurationClassPostProcessorTests.java +++ b/spring-context/src/test/java/org/springframework/context/annotation/ConfigurationClassPostProcessorTests.java @@ -37,6 +37,7 @@ import org.springframework.beans.factory.BeanCreationException; import org.springframework.beans.factory.BeanDefinitionStoreException; import org.springframework.beans.factory.FactoryBean; import org.springframework.beans.factory.NoSuchBeanDefinitionException; +import org.springframework.beans.factory.ObjectProvider; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor; import org.springframework.beans.factory.annotation.Lookup; @@ -351,7 +352,7 @@ public class ConfigurationClassPostProcessorTests { } } - @Test + @Test // SPR-15384 public void nestedConfigurationClassesProcessedInCorrectOrder() { beanFactory.registerBeanDefinition("config", new RootBeanDefinition(ConfigWithOrderedNestedClasses.class)); ConfigurationClassPostProcessor pp = new ConfigurationClassPostProcessor(); @@ -363,6 +364,19 @@ public class ConfigurationClassPostProcessorTests { assertSame(foo, bar.foo); } + @Test // SPR-16734 + public void innerConfigurationClassesProcessedInCorrectOrder() { + beanFactory.registerBeanDefinition("config", new RootBeanDefinition(ConfigWithOrderedInnerClasses.class)); + ConfigurationClassPostProcessor pp = new ConfigurationClassPostProcessor(); + pp.postProcessBeanFactory(beanFactory); + beanFactory.addBeanPostProcessor(new AutowiredAnnotationBeanPostProcessor()); + + Foo foo = beanFactory.getBean(Foo.class); + assertTrue(foo instanceof ExtendedFoo); + Bar bar = beanFactory.getBean(Bar.class); + assertSame(foo, bar.foo); + } + @Test public void scopedProxyTargetMarkedAsNonAutowireCandidate() { AutowiredAnnotationBeanPostProcessor bpp = new AutowiredAnnotationBeanPostProcessor(); @@ -890,6 +904,43 @@ public class ConfigurationClassPostProcessorTests { } } + @Configuration + static class ConfigWithOrderedInnerClasses { + + @Configuration + @Order(1) + class SingletonBeanConfig { + + public SingletonBeanConfig(ConfigWithOrderedInnerClasses other) { + } + + public @Bean Foo foo() { + return new Foo(); + } + + public @Bean Bar bar() { + return new Bar(foo()); + } + } + + @Configuration + @Order(2) + class OverridingSingletonBeanConfig { + + public OverridingSingletonBeanConfig(ObjectProvider other) { + other.getObject(); + } + + public @Bean ExtendedFoo foo() { + return new ExtendedFoo(); + } + + public @Bean Bar bar() { + return new Bar(foo()); + } + } + } + static class Foo { } diff --git a/spring-core/src/main/java/org/springframework/core/MethodParameter.java b/spring-core/src/main/java/org/springframework/core/MethodParameter.java index 317aae575ea..ad3b190f629 100644 --- a/spring-core/src/main/java/org/springframework/core/MethodParameter.java +++ b/spring-core/src/main/java/org/springframework/core/MethodParameter.java @@ -419,7 +419,18 @@ public class MethodParameter { paramType = (method != null ? method.getGenericReturnType() : void.class); } else { - paramType = this.executable.getGenericParameterTypes()[this.parameterIndex]; + Type[] genericParameterTypes = this.executable.getGenericParameterTypes(); + int index = this.parameterIndex; + if (this.executable instanceof Constructor && + ClassUtils.isInnerClass(this.executable.getDeclaringClass()) && + genericParameterTypes.length == this.executable.getParameterCount() - 1) { + // Bug in javac: type array excludes enclosing instance parameter + // for inner classes with at least one generic constructor parameter, + // so access it with the actual parameter index lowered by 1 + index = this.parameterIndex - 1; + } + paramType = (index >= 0 && index < genericParameterTypes.length ? + genericParameterTypes[index] : getParameterType()); } this.genericParameterType = paramType; } @@ -525,12 +536,8 @@ public class MethodParameter { // for inner classes, so access it with the actual parameter index lowered by 1 index = this.parameterIndex - 1; } - if (index >= 0 && index < annotationArray.length) { - paramAnns = adaptAnnotationArray(annotationArray[index]); - } - else { - paramAnns = EMPTY_ANNOTATION_ARRAY; - } + paramAnns = (index >= 0 && index < annotationArray.length ? + adaptAnnotationArray(annotationArray[index]) : EMPTY_ANNOTATION_ARRAY); this.parameterAnnotations = paramAnns; } return paramAnns; @@ -770,7 +777,6 @@ public class MethodParameter { } return false; } - } } diff --git a/spring-core/src/test/java/org/springframework/core/MethodParameterTests.java b/spring-core/src/test/java/org/springframework/core/MethodParameterTests.java index ecdb5d57801..75261006615 100644 --- a/spring-core/src/test/java/org/springframework/core/MethodParameterTests.java +++ b/spring-core/src/test/java/org/springframework/core/MethodParameterTests.java @@ -22,6 +22,7 @@ import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; import java.lang.reflect.Constructor; import java.lang.reflect.Method; +import java.util.concurrent.Callable; import org.junit.Before; import org.junit.Test; @@ -114,7 +115,7 @@ public class MethodParameterTests { @Test // SPR-16652 public void annotatedConstructorParameterInInnerClass() throws Exception { - Constructor constructor = InnerClass.class.getConstructor(getClass(), String.class, Integer.class); + Constructor constructor = InnerClass.class.getConstructor(getClass(), String.class, Callable.class); MethodParameter methodParameter = MethodParameter.forExecutable(constructor, 0); assertEquals(getClass(), methodParameter.getParameterType()); @@ -125,10 +126,28 @@ public class MethodParameterTests { assertNotNull("Failed to find @Param annotation", methodParameter.getParameterAnnotation(Param.class)); methodParameter = MethodParameter.forExecutable(constructor, 2); - assertEquals(Integer.class, methodParameter.getParameterType()); + assertEquals(Callable.class, methodParameter.getParameterType()); assertNull(methodParameter.getParameterAnnotation(Param.class)); } + @Test // SPR-16734 + public void genericConstructorParameterInInnerClass() throws Exception { + Constructor constructor = InnerClass.class.getConstructor(getClass(), String.class, Callable.class); + + MethodParameter methodParameter = MethodParameter.forExecutable(constructor, 0); + assertEquals(getClass(), methodParameter.getParameterType()); + assertEquals(getClass(), methodParameter.getGenericParameterType()); + + methodParameter = MethodParameter.forExecutable(constructor, 1); + assertEquals(String.class, methodParameter.getParameterType()); + assertEquals(String.class, methodParameter.getGenericParameterType()); + + methodParameter = MethodParameter.forExecutable(constructor, 2); + assertEquals(Callable.class, methodParameter.getParameterType()); + assertEquals(ResolvableType.forClassWithGenerics(Callable.class, Integer.class).getType(), + methodParameter.getGenericParameterType()); + } + public int method(String p1, long p2) { return 42; @@ -144,7 +163,7 @@ public class MethodParameterTests { @SuppressWarnings("unused") private class InnerClass { - public InnerClass(@Param String s, Integer i) { + public InnerClass(@Param String s, Callable i) { } }