Skip to content

Commit 4b06b60

Browse files
committed
RootBeanDefinition accepts ResolvableType for target type hint
Issue: SPR-14580
1 parent 6a0d9d3 commit 4b06b60

File tree

4 files changed

+105
-18
lines changed

4 files changed

+105
-18
lines changed

spring-beans/src/main/java/org/springframework/beans/factory/support/AbstractAutowireCapableBeanFactory.java

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -624,10 +624,11 @@ protected Class<?> predictBeanType(String beanName, RootBeanDefinition mbd, Clas
624624
protected Class<?> determineTargetType(String beanName, RootBeanDefinition mbd, Class<?>... typesToMatch) {
625625
Class<?> targetType = mbd.getTargetType();
626626
if (targetType == null) {
627-
targetType = (mbd.getFactoryMethodName() != null ? getTypeForFactoryMethod(beanName, mbd, typesToMatch) :
627+
targetType = (mbd.getFactoryMethodName() != null ?
628+
getTypeForFactoryMethod(beanName, mbd, typesToMatch) :
628629
resolveBeanClass(mbd, beanName, typesToMatch));
629630
if (ObjectUtils.isEmpty(typesToMatch) || getTempClassLoader() == null) {
630-
mbd.setTargetType(targetType);
631+
mbd.resolvedTargetType = targetType;
631632
}
632633
}
633634
return targetType;

spring-beans/src/main/java/org/springframework/beans/factory/support/GenericTypeAwareAutowireCandidateResolver.java

Lines changed: 24 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2013 the original author or authors.
2+
* Copyright 2002-2016 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -74,21 +74,31 @@ protected boolean checkGenericTypeMatch(BeanDefinitionHolder bdHolder, Dependenc
7474
// No generic type -> we know it's a Class type-match, so no need to check again.
7575
return true;
7676
}
77+
7778
ResolvableType targetType = null;
79+
boolean cacheType = false;
7880
RootBeanDefinition rbd = null;
7981
if (bdHolder.getBeanDefinition() instanceof RootBeanDefinition) {
8082
rbd = (RootBeanDefinition) bdHolder.getBeanDefinition();
8183
}
8284
if (rbd != null) {
83-
// First, check factory method return type, if applicable
84-
targetType = getReturnTypeForFactoryMethod(rbd, descriptor);
85+
targetType = rbd.targetType;
8586
if (targetType == null) {
86-
RootBeanDefinition dbd = getResolvedDecoratedDefinition(rbd);
87-
if (dbd != null) {
88-
targetType = getReturnTypeForFactoryMethod(dbd, descriptor);
87+
cacheType = true;
88+
// First, check factory method return type, if applicable
89+
targetType = getReturnTypeForFactoryMethod(rbd, descriptor);
90+
if (targetType == null) {
91+
RootBeanDefinition dbd = getResolvedDecoratedDefinition(rbd);
92+
if (dbd != null) {
93+
targetType = dbd.targetType;
94+
if (targetType == null) {
95+
targetType = getReturnTypeForFactoryMethod(dbd, descriptor);
96+
}
97+
}
8998
}
9099
}
91100
}
101+
92102
if (targetType == null) {
93103
// Regular case: straight bean instance, with BeanFactory available.
94104
if (this.beanFactory != null) {
@@ -106,7 +116,14 @@ protected boolean checkGenericTypeMatch(BeanDefinitionHolder bdHolder, Dependenc
106116
}
107117
}
108118
}
109-
if (targetType == null || (descriptor.fallbackMatchAllowed() && targetType.hasUnresolvableGenerics())) {
119+
120+
if (targetType == null) {
121+
return true;
122+
}
123+
if (cacheType) {
124+
rbd.targetType = targetType;
125+
}
126+
if (descriptor.fallbackMatchAllowed() && targetType.hasUnresolvableGenerics()) {
110127
return true;
111128
}
112129
// Full check for complex generic type match...

spring-beans/src/main/java/org/springframework/beans/factory/support/RootBeanDefinition.java

Lines changed: 28 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
import org.springframework.beans.factory.config.BeanDefinition;
2727
import org.springframework.beans.factory.config.BeanDefinitionHolder;
2828
import org.springframework.beans.factory.config.ConstructorArgumentValues;
29+
import org.springframework.core.ResolvableType;
2930
import org.springframework.util.Assert;
3031

3132
/**
@@ -49,22 +50,26 @@
4950
@SuppressWarnings("serial")
5051
public class RootBeanDefinition extends AbstractBeanDefinition {
5152

52-
boolean allowCaching = true;
53-
5453
private BeanDefinitionHolder decoratedDefinition;
5554

56-
private volatile Class<?> targetType;
55+
boolean allowCaching = true;
56+
57+
volatile ResolvableType targetType;
5758

5859
boolean isFactoryMethodUnique = false;
5960

61+
/** Package-visible field for caching the determined Class of a given bean definition */
62+
volatile Class<?> resolvedTargetType;
63+
64+
/** Package-visible field for caching the return type of a generically typed factory method */
65+
volatile Class<?> resolvedFactoryMethodReturnType;
66+
67+
/** Common lock for the four constructor fields below */
6068
final Object constructorArgumentLock = new Object();
6169

6270
/** Package-visible field for caching the resolved constructor or factory method */
6371
Executable resolvedConstructorOrFactoryMethod;
6472

65-
/** Package-visible field for caching the return type of a generically typed factory method */
66-
volatile Class<?> resolvedFactoryMethodReturnType;
67-
6873
/** Package-visible field that marks the constructor arguments as resolved */
6974
boolean constructorArgumentsResolved = false;
7075

@@ -74,6 +79,7 @@ public class RootBeanDefinition extends AbstractBeanDefinition {
7479
/** Package-visible field for caching partly prepared constructor arguments */
7580
Object[] preparedConstructorArguments;
7681

82+
/** Common lock for the two post-processing fields below */
7783
final Object postProcessingLock = new Object();
7884

7985
/** Package-visible field that indicates MergedBeanDefinitionPostProcessor having been applied */
@@ -172,8 +178,8 @@ public RootBeanDefinition(String beanClassName, ConstructorArgumentValues cargs,
172178
*/
173179
public RootBeanDefinition(RootBeanDefinition original) {
174180
super(original);
175-
this.allowCaching = original.allowCaching;
176181
this.decoratedDefinition = original.decoratedDefinition;
182+
this.allowCaching = original.allowCaching;
177183
this.targetType = original.targetType;
178184
this.isFactoryMethodUnique = original.isFactoryMethodUnique;
179185
}
@@ -214,19 +220,32 @@ public BeanDefinitionHolder getDecoratedDefinition() {
214220
return this.decoratedDefinition;
215221
}
216222

223+
/**
224+
* Specify a generics-containing target type of this bean definition, if known in advance.
225+
* @since 4.3.3
226+
*/
227+
public void setTargetType(ResolvableType targetType) {
228+
this.targetType = targetType;
229+
}
230+
217231
/**
218232
* Specify the target type of this bean definition, if known in advance.
233+
* @since 3.2.2
219234
*/
220235
public void setTargetType(Class<?> targetType) {
221-
this.targetType = targetType;
236+
this.targetType = (targetType != null ? ResolvableType.forClass(targetType) : null);
222237
}
223238

224239
/**
225240
* Return the target type of this bean definition, if known
226241
* (either specified in advance or resolved on first instantiation).
242+
* @since 3.2.2
227243
*/
228244
public Class<?> getTargetType() {
229-
return this.targetType;
245+
if (this.resolvedTargetType != null) {
246+
return this.resolvedTargetType;
247+
}
248+
return (this.targetType != null ? this.targetType.resolve() : null);
230249
}
231250

232251
/**

spring-beans/src/test/java/org/springframework/beans/factory/annotation/AutowiredAnnotationBeanPostProcessorTests.java

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@
4141

4242
import org.springframework.beans.factory.BeanCreationException;
4343
import org.springframework.beans.factory.BeanFactory;
44+
import org.springframework.beans.factory.BeanNameAware;
4445
import org.springframework.beans.factory.FactoryBean;
4546
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
4647
import org.springframework.beans.factory.NoUniqueBeanDefinitionException;
@@ -2047,6 +2048,24 @@ public void testGenericsBasedInjectionIntoTypeVariableSelectingBestMatchAgainstF
20472048
assertSame(bean2, bean1.gi2);
20482049
}
20492050

2051+
@Test
2052+
public void testGenericsBasedInjectionWithBeanDefinitionTargetResolvableType() throws Exception {
2053+
DefaultListableBeanFactory bf = new DefaultListableBeanFactory();
2054+
bf.setAutowireCandidateResolver(new QualifierAnnotationAutowireCandidateResolver());
2055+
AutowiredAnnotationBeanPostProcessor bpp = new AutowiredAnnotationBeanPostProcessor();
2056+
bpp.setBeanFactory(bf);
2057+
bf.addBeanPostProcessor(bpp);
2058+
RootBeanDefinition bd1 = new RootBeanDefinition(GenericInterface2Bean.class);
2059+
bd1.setTargetType(ResolvableType.forClassWithGenerics(GenericInterface2Bean.class, String.class));
2060+
bf.registerBeanDefinition("bean1", bd1);
2061+
RootBeanDefinition bd2 = new RootBeanDefinition(GenericInterface2Bean.class);
2062+
bd2.setTargetType(ResolvableType.forClassWithGenerics(GenericInterface2Bean.class, Integer.class));
2063+
bf.registerBeanDefinition("bean2", bd2);
2064+
bf.registerBeanDefinition("bean3", new RootBeanDefinition(MultiGenericFieldInjection.class));
2065+
2066+
assertEquals("bean1 a bean2 123", bf.getBean("bean3").toString());
2067+
}
2068+
20502069
@Test
20512070
public void testCircularTypeReference() {
20522071
DefaultListableBeanFactory bf = new DefaultListableBeanFactory();
@@ -3139,6 +3158,37 @@ public String doSomethingMoreGeneric(Object o) {
31393158
}
31403159

31413160

3161+
public static class GenericInterface2Bean<K> implements GenericInterface2<K>, BeanNameAware {
3162+
3163+
private String name;
3164+
3165+
@Override
3166+
public void setBeanName(String name) {
3167+
this.name = name;
3168+
}
3169+
3170+
@Override
3171+
public String doSomethingMoreGeneric(K o) {
3172+
return this.name + " " + o;
3173+
}
3174+
}
3175+
3176+
3177+
public static class MultiGenericFieldInjection {
3178+
3179+
@Autowired
3180+
private GenericInterface2<String> stringBean;
3181+
3182+
@Autowired
3183+
private GenericInterface2<Integer> integerBean;
3184+
3185+
@Override
3186+
public String toString() {
3187+
return this.stringBean.doSomethingMoreGeneric("a") + " " + this.integerBean.doSomethingMoreGeneric(123);
3188+
}
3189+
}
3190+
3191+
31423192
@SuppressWarnings("rawtypes")
31433193
public static class PlainGenericInterface2Impl implements GenericInterface2 {
31443194

0 commit comments

Comments
 (0)