Skip to content

Commit e6cefdc

Browse files
committed
RootBeanDefinition accepts ResolvableType for target type hint
Issue: SPR-14580 (cherry picked from commit 4b06b60)
1 parent fbeff47 commit e6cefdc

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
@@ -25,6 +25,7 @@
2525
import org.springframework.beans.factory.config.BeanDefinition;
2626
import org.springframework.beans.factory.config.BeanDefinitionHolder;
2727
import org.springframework.beans.factory.config.ConstructorArgumentValues;
28+
import org.springframework.core.ResolvableType;
2829
import org.springframework.util.Assert;
2930

3031
/**
@@ -48,22 +49,26 @@
4849
@SuppressWarnings("serial")
4950
public class RootBeanDefinition extends AbstractBeanDefinition {
5051

51-
boolean allowCaching = true;
52-
5352
private BeanDefinitionHolder decoratedDefinition;
5453

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

5758
boolean isFactoryMethodUnique = false;
5859

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

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

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

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

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

7884
/** Package-visible field that indicates MergedBeanDefinitionPostProcessor having been applied */
@@ -171,8 +177,8 @@ public RootBeanDefinition(String beanClassName, ConstructorArgumentValues cargs,
171177
*/
172178
public RootBeanDefinition(RootBeanDefinition original) {
173179
super(original);
174-
this.allowCaching = original.allowCaching;
175180
this.decoratedDefinition = original.decoratedDefinition;
181+
this.allowCaching = original.allowCaching;
176182
this.targetType = original.targetType;
177183
this.isFactoryMethodUnique = original.isFactoryMethodUnique;
178184
}
@@ -213,19 +219,32 @@ public BeanDefinitionHolder getDecoratedDefinition() {
213219
return this.decoratedDefinition;
214220
}
215221

222+
/**
223+
* Specify a generics-containing target type of this bean definition, if known in advance.
224+
* @since 4.3.3
225+
*/
226+
public void setTargetType(ResolvableType targetType) {
227+
this.targetType = targetType;
228+
}
229+
216230
/**
217231
* Specify the target type of this bean definition, if known in advance.
232+
* @since 3.2.2
218233
*/
219234
public void setTargetType(Class<?> targetType) {
220-
this.targetType = targetType;
235+
this.targetType = (targetType != null ? ResolvableType.forClass(targetType) : null);
221236
}
222237

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

231250
/**

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)