Workaround for generic parameter types on inner class constructors
Issue: SPR-16734
This commit is contained in:
@@ -37,6 +37,7 @@ import org.springframework.beans.factory.BeanCreationException;
|
|||||||
import org.springframework.beans.factory.BeanDefinitionStoreException;
|
import org.springframework.beans.factory.BeanDefinitionStoreException;
|
||||||
import org.springframework.beans.factory.FactoryBean;
|
import org.springframework.beans.factory.FactoryBean;
|
||||||
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
|
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.Autowired;
|
||||||
import org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor;
|
import org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor;
|
||||||
import org.springframework.beans.factory.annotation.Lookup;
|
import org.springframework.beans.factory.annotation.Lookup;
|
||||||
@@ -351,7 +352,7 @@ public class ConfigurationClassPostProcessorTests {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test // SPR-15384
|
||||||
public void nestedConfigurationClassesProcessedInCorrectOrder() {
|
public void nestedConfigurationClassesProcessedInCorrectOrder() {
|
||||||
beanFactory.registerBeanDefinition("config", new RootBeanDefinition(ConfigWithOrderedNestedClasses.class));
|
beanFactory.registerBeanDefinition("config", new RootBeanDefinition(ConfigWithOrderedNestedClasses.class));
|
||||||
ConfigurationClassPostProcessor pp = new ConfigurationClassPostProcessor();
|
ConfigurationClassPostProcessor pp = new ConfigurationClassPostProcessor();
|
||||||
@@ -363,6 +364,19 @@ public class ConfigurationClassPostProcessorTests {
|
|||||||
assertSame(foo, bar.foo);
|
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
|
@Test
|
||||||
public void scopedProxyTargetMarkedAsNonAutowireCandidate() {
|
public void scopedProxyTargetMarkedAsNonAutowireCandidate() {
|
||||||
AutowiredAnnotationBeanPostProcessor bpp = new AutowiredAnnotationBeanPostProcessor();
|
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<SingletonBeanConfig> other) {
|
||||||
|
other.getObject();
|
||||||
|
}
|
||||||
|
|
||||||
|
public @Bean ExtendedFoo foo() {
|
||||||
|
return new ExtendedFoo();
|
||||||
|
}
|
||||||
|
|
||||||
|
public @Bean Bar bar() {
|
||||||
|
return new Bar(foo());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static class Foo {
|
static class Foo {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -419,7 +419,18 @@ public class MethodParameter {
|
|||||||
paramType = (method != null ? method.getGenericReturnType() : void.class);
|
paramType = (method != null ? method.getGenericReturnType() : void.class);
|
||||||
}
|
}
|
||||||
else {
|
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;
|
this.genericParameterType = paramType;
|
||||||
}
|
}
|
||||||
@@ -525,12 +536,8 @@ public class MethodParameter {
|
|||||||
// for inner classes, so access it with the actual parameter index lowered by 1
|
// for inner classes, so access it with the actual parameter index lowered by 1
|
||||||
index = this.parameterIndex - 1;
|
index = this.parameterIndex - 1;
|
||||||
}
|
}
|
||||||
if (index >= 0 && index < annotationArray.length) {
|
paramAnns = (index >= 0 && index < annotationArray.length ?
|
||||||
paramAnns = adaptAnnotationArray(annotationArray[index]);
|
adaptAnnotationArray(annotationArray[index]) : EMPTY_ANNOTATION_ARRAY);
|
||||||
}
|
|
||||||
else {
|
|
||||||
paramAnns = EMPTY_ANNOTATION_ARRAY;
|
|
||||||
}
|
|
||||||
this.parameterAnnotations = paramAnns;
|
this.parameterAnnotations = paramAnns;
|
||||||
}
|
}
|
||||||
return paramAnns;
|
return paramAnns;
|
||||||
@@ -770,7 +777,6 @@ public class MethodParameter {
|
|||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,6 +22,7 @@ import java.lang.annotation.RetentionPolicy;
|
|||||||
import java.lang.annotation.Target;
|
import java.lang.annotation.Target;
|
||||||
import java.lang.reflect.Constructor;
|
import java.lang.reflect.Constructor;
|
||||||
import java.lang.reflect.Method;
|
import java.lang.reflect.Method;
|
||||||
|
import java.util.concurrent.Callable;
|
||||||
|
|
||||||
import org.junit.Before;
|
import org.junit.Before;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
@@ -114,7 +115,7 @@ public class MethodParameterTests {
|
|||||||
|
|
||||||
@Test // SPR-16652
|
@Test // SPR-16652
|
||||||
public void annotatedConstructorParameterInInnerClass() throws Exception {
|
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);
|
MethodParameter methodParameter = MethodParameter.forExecutable(constructor, 0);
|
||||||
assertEquals(getClass(), methodParameter.getParameterType());
|
assertEquals(getClass(), methodParameter.getParameterType());
|
||||||
@@ -125,10 +126,28 @@ public class MethodParameterTests {
|
|||||||
assertNotNull("Failed to find @Param annotation", methodParameter.getParameterAnnotation(Param.class));
|
assertNotNull("Failed to find @Param annotation", methodParameter.getParameterAnnotation(Param.class));
|
||||||
|
|
||||||
methodParameter = MethodParameter.forExecutable(constructor, 2);
|
methodParameter = MethodParameter.forExecutable(constructor, 2);
|
||||||
assertEquals(Integer.class, methodParameter.getParameterType());
|
assertEquals(Callable.class, methodParameter.getParameterType());
|
||||||
assertNull(methodParameter.getParameterAnnotation(Param.class));
|
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) {
|
public int method(String p1, long p2) {
|
||||||
return 42;
|
return 42;
|
||||||
@@ -144,7 +163,7 @@ public class MethodParameterTests {
|
|||||||
@SuppressWarnings("unused")
|
@SuppressWarnings("unused")
|
||||||
private class InnerClass {
|
private class InnerClass {
|
||||||
|
|
||||||
public InnerClass(@Param String s, Integer i) {
|
public InnerClass(@Param String s, Callable<Integer> i) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user