Skip to content

Commit 4cd43dc

Browse files
committed
Workaround for generic parameter types on inner class constructors
Issue: SPR-16734
1 parent 91c8b62 commit 4cd43dc

File tree

3 files changed

+88
-12
lines changed

3 files changed

+88
-12
lines changed

spring-context/src/test/java/org/springframework/context/annotation/ConfigurationClassPostProcessorTests.java

Lines changed: 52 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@
3737
import org.springframework.beans.factory.BeanDefinitionStoreException;
3838
import org.springframework.beans.factory.FactoryBean;
3939
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
40+
import org.springframework.beans.factory.ObjectProvider;
4041
import org.springframework.beans.factory.annotation.Autowired;
4142
import org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor;
4243
import org.springframework.beans.factory.annotation.Lookup;
@@ -351,7 +352,7 @@ public void configurationClassesWithInvalidOverridingForProgrammaticCall() {
351352
}
352353
}
353354

354-
@Test
355+
@Test // SPR-15384
355356
public void nestedConfigurationClassesProcessedInCorrectOrder() {
356357
beanFactory.registerBeanDefinition("config", new RootBeanDefinition(ConfigWithOrderedNestedClasses.class));
357358
ConfigurationClassPostProcessor pp = new ConfigurationClassPostProcessor();
@@ -363,6 +364,19 @@ public void nestedConfigurationClassesProcessedInCorrectOrder() {
363364
assertSame(foo, bar.foo);
364365
}
365366

367+
@Test // SPR-16734
368+
public void innerConfigurationClassesProcessedInCorrectOrder() {
369+
beanFactory.registerBeanDefinition("config", new RootBeanDefinition(ConfigWithOrderedInnerClasses.class));
370+
ConfigurationClassPostProcessor pp = new ConfigurationClassPostProcessor();
371+
pp.postProcessBeanFactory(beanFactory);
372+
beanFactory.addBeanPostProcessor(new AutowiredAnnotationBeanPostProcessor());
373+
374+
Foo foo = beanFactory.getBean(Foo.class);
375+
assertTrue(foo instanceof ExtendedFoo);
376+
Bar bar = beanFactory.getBean(Bar.class);
377+
assertSame(foo, bar.foo);
378+
}
379+
366380
@Test
367381
public void scopedProxyTargetMarkedAsNonAutowireCandidate() {
368382
AutowiredAnnotationBeanPostProcessor bpp = new AutowiredAnnotationBeanPostProcessor();
@@ -890,6 +904,43 @@ static class OverridingSingletonBeanConfig {
890904
}
891905
}
892906

907+
@Configuration
908+
static class ConfigWithOrderedInnerClasses {
909+
910+
@Configuration
911+
@Order(1)
912+
class SingletonBeanConfig {
913+
914+
public SingletonBeanConfig(ConfigWithOrderedInnerClasses other) {
915+
}
916+
917+
public @Bean Foo foo() {
918+
return new Foo();
919+
}
920+
921+
public @Bean Bar bar() {
922+
return new Bar(foo());
923+
}
924+
}
925+
926+
@Configuration
927+
@Order(2)
928+
class OverridingSingletonBeanConfig {
929+
930+
public OverridingSingletonBeanConfig(ObjectProvider<SingletonBeanConfig> other) {
931+
other.getObject();
932+
}
933+
934+
public @Bean ExtendedFoo foo() {
935+
return new ExtendedFoo();
936+
}
937+
938+
public @Bean Bar bar() {
939+
return new Bar(foo());
940+
}
941+
}
942+
}
943+
893944
static class Foo {
894945
}
895946

spring-core/src/main/java/org/springframework/core/MethodParameter.java

Lines changed: 14 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -419,7 +419,18 @@ public Type getGenericParameterType() {
419419
paramType = (method != null ? method.getGenericReturnType() : void.class);
420420
}
421421
else {
422-
paramType = this.executable.getGenericParameterTypes()[this.parameterIndex];
422+
Type[] genericParameterTypes = this.executable.getGenericParameterTypes();
423+
int index = this.parameterIndex;
424+
if (this.executable instanceof Constructor &&
425+
ClassUtils.isInnerClass(this.executable.getDeclaringClass()) &&
426+
genericParameterTypes.length == this.executable.getParameterCount() - 1) {
427+
// Bug in javac: type array excludes enclosing instance parameter
428+
// for inner classes with at least one generic constructor parameter,
429+
// so access it with the actual parameter index lowered by 1
430+
index = this.parameterIndex - 1;
431+
}
432+
paramType = (index >= 0 && index < genericParameterTypes.length ?
433+
genericParameterTypes[index] : getParameterType());
423434
}
424435
this.genericParameterType = paramType;
425436
}
@@ -525,12 +536,8 @@ public Annotation[] getParameterAnnotations() {
525536
// for inner classes, so access it with the actual parameter index lowered by 1
526537
index = this.parameterIndex - 1;
527538
}
528-
if (index >= 0 && index < annotationArray.length) {
529-
paramAnns = adaptAnnotationArray(annotationArray[index]);
530-
}
531-
else {
532-
paramAnns = EMPTY_ANNOTATION_ARRAY;
533-
}
539+
paramAnns = (index >= 0 && index < annotationArray.length ?
540+
adaptAnnotationArray(annotationArray[index]) : EMPTY_ANNOTATION_ARRAY);
534541
this.parameterAnnotations = paramAnns;
535542
}
536543
return paramAnns;
@@ -770,7 +777,6 @@ else if (ctor != null) {
770777
}
771778
return false;
772779
}
773-
774780
}
775781

776782
}

spring-core/src/test/java/org/springframework/core/MethodParameterTests.java

Lines changed: 22 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
import java.lang.annotation.Target;
2323
import java.lang.reflect.Constructor;
2424
import java.lang.reflect.Method;
25+
import java.util.concurrent.Callable;
2526

2627
import org.junit.Before;
2728
import org.junit.Test;
@@ -114,7 +115,7 @@ public void annotatedConstructorParameterInStaticNestedClass() throws Exception
114115

115116
@Test // SPR-16652
116117
public void annotatedConstructorParameterInInnerClass() throws Exception {
117-
Constructor<?> constructor = InnerClass.class.getConstructor(getClass(), String.class, Integer.class);
118+
Constructor<?> constructor = InnerClass.class.getConstructor(getClass(), String.class, Callable.class);
118119

119120
MethodParameter methodParameter = MethodParameter.forExecutable(constructor, 0);
120121
assertEquals(getClass(), methodParameter.getParameterType());
@@ -125,10 +126,28 @@ public void annotatedConstructorParameterInInnerClass() throws Exception {
125126
assertNotNull("Failed to find @Param annotation", methodParameter.getParameterAnnotation(Param.class));
126127

127128
methodParameter = MethodParameter.forExecutable(constructor, 2);
128-
assertEquals(Integer.class, methodParameter.getParameterType());
129+
assertEquals(Callable.class, methodParameter.getParameterType());
129130
assertNull(methodParameter.getParameterAnnotation(Param.class));
130131
}
131132

133+
@Test // SPR-16734
134+
public void genericConstructorParameterInInnerClass() throws Exception {
135+
Constructor<?> constructor = InnerClass.class.getConstructor(getClass(), String.class, Callable.class);
136+
137+
MethodParameter methodParameter = MethodParameter.forExecutable(constructor, 0);
138+
assertEquals(getClass(), methodParameter.getParameterType());
139+
assertEquals(getClass(), methodParameter.getGenericParameterType());
140+
141+
methodParameter = MethodParameter.forExecutable(constructor, 1);
142+
assertEquals(String.class, methodParameter.getParameterType());
143+
assertEquals(String.class, methodParameter.getGenericParameterType());
144+
145+
methodParameter = MethodParameter.forExecutable(constructor, 2);
146+
assertEquals(Callable.class, methodParameter.getParameterType());
147+
assertEquals(ResolvableType.forClassWithGenerics(Callable.class, Integer.class).getType(),
148+
methodParameter.getGenericParameterType());
149+
}
150+
132151

133152
public int method(String p1, long p2) {
134153
return 42;
@@ -144,7 +163,7 @@ private static class NestedClass {
144163
@SuppressWarnings("unused")
145164
private class InnerClass {
146165

147-
public InnerClass(@Param String s, Integer i) {
166+
public InnerClass(@Param String s, Callable<Integer> i) {
148167
}
149168
}
150169

0 commit comments

Comments
 (0)