Skip to content

Commit b9c4f1f

Browse files
committed
ResolvableType-based matching respects generic factory method return type
Includes consistent use of ResolvableType.resolve() wherever applicable. Issue: SPR-15011 (cherry picked from commit 4c005e6)
1 parent faab4f9 commit b9c4f1f

File tree

14 files changed

+144
-52
lines changed

14 files changed

+144
-52
lines changed

spring-beans/src/main/java/org/springframework/beans/factory/NoSuchBeanDefinitionException.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -127,7 +127,7 @@ public String getBeanName() {
127127
* that failed.
128128
*/
129129
public Class<?> getBeanType() {
130-
return (this.resolvableType != null ? this.resolvableType.getRawClass() : null);
130+
return (this.resolvableType != null ? this.resolvableType.resolve() : null);
131131
}
132132

133133
/**

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

Lines changed: 19 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,7 @@
7474
import org.springframework.core.MethodParameter;
7575
import org.springframework.core.ParameterNameDiscoverer;
7676
import org.springframework.core.PriorityOrdered;
77+
import org.springframework.core.ResolvableType;
7778
import org.springframework.util.ClassUtils;
7879
import org.springframework.util.ObjectUtils;
7980
import org.springframework.util.ReflectionUtils;
@@ -659,9 +660,9 @@ protected Class<?> determineTargetType(String beanName, RootBeanDefinition mbd,
659660
* @see #createBean
660661
*/
661662
protected Class<?> getTypeForFactoryMethod(String beanName, RootBeanDefinition mbd, Class<?>... typesToMatch) {
662-
Class<?> preResolved = mbd.resolvedFactoryMethodReturnType;
663-
if (preResolved != null) {
664-
return preResolved;
663+
ResolvableType cachedReturnType = mbd.factoryMethodReturnType;
664+
if (cachedReturnType != null) {
665+
return cachedReturnType.resolve();
665666
}
666667

667668
Class<?> factoryClass;
@@ -685,11 +686,12 @@ protected Class<?> getTypeForFactoryMethod(String beanName, RootBeanDefinition m
685686
if (factoryClass == null) {
686687
return null;
687688
}
689+
factoryClass = ClassUtils.getUserClass(factoryClass);
688690

689691
// If all factory methods have the same return type, return that type.
690692
// Can't clearly figure out exact method due to type converting / autowiring!
691693
Class<?> commonType = null;
692-
boolean cache = false;
694+
Method uniqueCandidate = null;
693695
int minNrOfArgs = mbd.getConstructorArgumentValues().getArgumentCount();
694696
Method[] candidates = ReflectionUtils.getUniqueDeclaredMethods(factoryClass);
695697
for (Method factoryMethod : candidates) {
@@ -724,8 +726,12 @@ protected Class<?> getTypeForFactoryMethod(String beanName, RootBeanDefinition m
724726
Class<?> returnType = AutowireUtils.resolveReturnTypeForFactoryMethod(
725727
factoryMethod, args, getBeanClassLoader());
726728
if (returnType != null) {
727-
cache = true;
729+
uniqueCandidate = (commonType == null ? factoryMethod : null);
728730
commonType = ClassUtils.determineCommonAncestor(returnType, commonType);
731+
if (commonType == null) {
732+
// Ambiguous return types found: return null to indicate "not determinable".
733+
return null;
734+
}
729735
}
730736
}
731737
catch (Throwable ex) {
@@ -735,22 +741,22 @@ protected Class<?> getTypeForFactoryMethod(String beanName, RootBeanDefinition m
735741
}
736742
}
737743
else {
744+
uniqueCandidate = (commonType == null ? factoryMethod : null);
738745
commonType = ClassUtils.determineCommonAncestor(factoryMethod.getReturnType(), commonType);
746+
if (commonType == null) {
747+
// Ambiguous return types found: return null to indicate "not determinable".
748+
return null;
749+
}
739750
}
740751
}
741752
}
742753

743754
if (commonType != null) {
744755
// Clear return type found: all factory methods return same type.
745-
if (cache) {
746-
mbd.resolvedFactoryMethodReturnType = commonType;
747-
}
748-
return commonType;
749-
}
750-
else {
751-
// Ambiguous return types found: return null to indicate "not determinable".
752-
return null;
756+
mbd.factoryMethodReturnType = (uniqueCandidate != null ?
757+
ResolvableType.forMethodReturnType(uniqueCandidate) : ResolvableType.forClass(commonType));
753758
}
759+
return commonType;
754760
}
755761

756762
/**

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

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -517,7 +517,10 @@ else if (containsSingleton(beanName) && !containsBeanDefinition(beanName)) {
517517
// Retrieve corresponding bean definition.
518518
RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);
519519

520-
Class<?> classToMatch = typeToMatch.getRawClass();
520+
Class<?> classToMatch = typeToMatch.resolve();
521+
if (classToMatch == null) {
522+
classToMatch = FactoryBean.class;
523+
}
521524
Class<?>[] typesToMatch = (FactoryBean.class == classToMatch ?
522525
new Class<?>[] {classToMatch} : new Class<?>[] {FactoryBean.class, classToMatch});
523526

@@ -557,6 +560,13 @@ else if (BeanFactoryUtils.isFactoryDereference(name)) {
557560
}
558561
}
559562

563+
ResolvableType resolvableType = mbd.targetType;
564+
if (resolvableType == null) {
565+
resolvableType = mbd.factoryMethodReturnType;
566+
}
567+
if (resolvableType != null && resolvableType.resolve() == beanType) {
568+
return typeToMatch.isAssignableFrom(resolvableType);
569+
}
560570
return typeToMatch.isAssignableFrom(beanType);
561571
}
562572
}
@@ -1447,6 +1457,10 @@ protected Object evaluateBeanDefinitionString(String value, BeanDefinition beanD
14471457
* @return the type of the bean, or {@code null} if not predictable
14481458
*/
14491459
protected Class<?> predictBeanType(String beanName, RootBeanDefinition mbd, Class<?>... typesToMatch) {
1460+
Class<?> targetType = mbd.getTargetType();
1461+
if (targetType != null) {
1462+
return targetType;
1463+
}
14501464
if (mbd.getFactoryMethodName() != null) {
14511465
return null;
14521466
}

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

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2014 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.
@@ -162,8 +162,8 @@ public static Object resolveAutowiringValue(Object autowiringValue, Class<?> req
162162
* Determine the target type for the generic return type of the given
163163
* <em>generic factory method</em>, where formal type variables are declared
164164
* on the given method itself.
165-
* <p>For example, given a factory method with the following signature,
166-
* if {@code resolveReturnTypeForFactoryMethod()} is invoked with the reflected
165+
* <p>For example, given a factory method with the following signature, if
166+
* {@code resolveReturnTypeForFactoryMethod()} is invoked with the reflected
167167
* method for {@code creatProxy()} and an {@code Object[]} array containing
168168
* {@code MyService.class}, {@code resolveReturnTypeForFactoryMethod()} will
169169
* infer that the target return type is {@code MyService}.
@@ -184,9 +184,9 @@ public static Object resolveAutowiringValue(Object autowiringValue, Class<?> req
184184
* @param method the method to introspect (never {@code null})
185185
* @param args the arguments that will be supplied to the method when it is
186186
* invoked (never {@code null})
187-
* @param classLoader the ClassLoader to resolve class names against, if necessary
188-
* (never {@code null})
189-
* @return the resolved target return type, the standard return type, or {@code null}
187+
* @param classLoader the ClassLoader to resolve class names against,
188+
* if necessary (never {@code null})
189+
* @return the resolved target return type or the standard method return type
190190
* @since 3.2.5
191191
*/
192192
public static Class<?> resolveReturnTypeForFactoryMethod(Method method, Object[] args, ClassLoader classLoader) {

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

Lines changed: 14 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -147,22 +147,23 @@ protected RootBeanDefinition getResolvedDecoratedDefinition(RootBeanDefinition r
147147
protected ResolvableType getReturnTypeForFactoryMethod(RootBeanDefinition rbd, DependencyDescriptor descriptor) {
148148
// Should typically be set for any kind of factory method, since the BeanFactory
149149
// pre-resolves them before reaching out to the AutowireCandidateResolver...
150-
Class<?> preResolved = rbd.resolvedFactoryMethodReturnType;
151-
if (preResolved != null) {
152-
return ResolvableType.forClass(preResolved);
150+
ResolvableType returnType = rbd.factoryMethodReturnType;
151+
if (returnType == null) {
152+
Method factoryMethod = rbd.getResolvedFactoryMethod();
153+
if (factoryMethod != null) {
154+
returnType = ResolvableType.forMethodReturnType(factoryMethod);
155+
}
153156
}
154-
else {
155-
Method resolvedFactoryMethod = rbd.getResolvedFactoryMethod();
156-
if (resolvedFactoryMethod != null) {
157-
if (descriptor.getDependencyType().isAssignableFrom(resolvedFactoryMethod.getReturnType())) {
158-
// Only use factory method metadata if the return type is actually expressive enough
159-
// for our dependency. Otherwise, the returned instance type may have matched instead
160-
// in case of a singleton instance having been registered with the container already.
161-
return ResolvableType.forMethodReturnType(resolvedFactoryMethod);
162-
}
157+
if (returnType != null) {
158+
Class<?> resolvedClass = returnType.resolve();
159+
if (resolvedClass != null && descriptor.getDependencyType().isAssignableFrom(resolvedClass)) {
160+
// Only use factory method metadata if the return type is actually expressive enough
161+
// for our dependency. Otherwise, the returned instance type may have matched instead
162+
// in case of a singleton instance having been registered with the container already.
163+
return returnType;
163164
}
164-
return null;
165165
}
166+
return null;
166167
}
167168

168169

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@ public class RootBeanDefinition extends AbstractBeanDefinition {
6464
volatile Class<?> resolvedTargetType;
6565

6666
/** Package-visible field for caching the return type of a generically typed factory method */
67-
volatile Class<?> resolvedFactoryMethodReturnType;
67+
volatile ResolvableType factoryMethodReturnType;
6868

6969
/** Common lock for the four constructor fields below */
7070
final Object constructorArgumentLock = new Object();

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

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -248,7 +248,13 @@ public String[] getBeanDefinitionNames() {
248248

249249
@Override
250250
public String[] getBeanNamesForType(ResolvableType type) {
251-
boolean isFactoryType = (type != null && FactoryBean.class.isAssignableFrom(type.getRawClass()));
251+
boolean isFactoryType = false;
252+
if (type != null) {
253+
Class<?> resolved = type.resolve();
254+
if (resolved != null && FactoryBean.class.isAssignableFrom(resolved)) {
255+
isFactoryType = true;
256+
}
257+
}
252258
List<String> matches = new ArrayList<String>();
253259
for (Map.Entry<String, Object> entry : this.beans.entrySet()) {
254260
String name = entry.getKey();

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

Lines changed: 20 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1295,7 +1295,6 @@ public void testCustomAnnotationRequiredFieldResourceInjectionFailsWhenMultipleD
12951295
}
12961296
catch (UnsatisfiedDependencyException ex) {
12971297
// expected
1298-
ex.printStackTrace();
12991298
assertSame(CustomAnnotationRequiredFieldResourceInjectionBean.class,
13001299
ex.getInjectionPoint().getField().getDeclaringClass());
13011300
}
@@ -1681,9 +1680,6 @@ public void testGenericsBasedFieldInjectionWithMocks() {
16811680
assertSame(ir, bean.integerRepositoryMap.get("integerRepository"));
16821681
}
16831682

1684-
@Qualifier("integerRepo")
1685-
private Repository<?> integerRepositoryQualifierProvider;
1686-
16871683
@Test
16881684
public void testGenericsBasedFieldInjectionWithSimpleMatch() {
16891685
DefaultListableBeanFactory bf = new DefaultListableBeanFactory();
@@ -2184,6 +2180,19 @@ public void testSingleConstructorWithProvidedArgument() {
21842180
assertNotNull(bf.getBean(ProvidedArgumentBean.class));
21852181
}
21862182

2183+
@Test
2184+
public void testAnnotatedDefaultConstructor() {
2185+
DefaultListableBeanFactory bf = new DefaultListableBeanFactory();
2186+
bf.addBeanPostProcessor(new AutowiredAnnotationBeanPostProcessor());
2187+
bf.registerBeanDefinition("annotatedBean", new RootBeanDefinition(AnnotatedDefaultConstructorBean.class));
2188+
2189+
assertNotNull(bf.getBean("annotatedBean"));
2190+
}
2191+
2192+
2193+
@Qualifier("integerRepo")
2194+
private Repository<?> integerRepositoryQualifierProvider;
2195+
21872196

21882197
public static class ResourceInjectionBean {
21892198

@@ -3411,7 +3420,14 @@ public static Set<TestBean> testBeanSet() {
34113420
tbs.add(new TestBean("tb2"));
34123421
return tbs;
34133422
}
3423+
}
34143424

3425+
3426+
public static class AnnotatedDefaultConstructorBean {
3427+
3428+
@Autowired
3429+
public AnnotatedDefaultConstructorBean() {
3430+
}
34153431
}
34163432

34173433
}

spring-context/src/main/java/org/springframework/context/event/ApplicationListenerMethodAdapter.java

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -343,17 +343,15 @@ private ResolvableType getResolvableType(ApplicationEvent event) {
343343
ResolvableType payloadType = null;
344344
if (event instanceof PayloadApplicationEvent) {
345345
PayloadApplicationEvent<?> payloadEvent = (PayloadApplicationEvent<?>) event;
346-
payloadType = payloadEvent.getResolvableType().as(
347-
PayloadApplicationEvent.class).getGeneric(0);
346+
payloadType = payloadEvent.getResolvableType().as(PayloadApplicationEvent.class).getGeneric();
348347
}
349348
for (ResolvableType declaredEventType : this.declaredEventTypes) {
350-
if (!ApplicationEvent.class.isAssignableFrom(declaredEventType.getRawClass())
351-
&& payloadType != null) {
349+
if (!ApplicationEvent.class.isAssignableFrom(declaredEventType.getRawClass()) && payloadType != null) {
352350
if (declaredEventType.isAssignableFrom(payloadType)) {
353351
return declaredEventType;
354352
}
355353
}
356-
if (declaredEventType.getRawClass().isAssignableFrom(event.getClass())) {
354+
if (declaredEventType.getRawClass().isInstance(event)) {
357355
return declaredEventType;
358356
}
359357
}

spring-context/src/main/java/org/springframework/context/event/GenericApplicationListenerAdapter.java

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2015 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.
@@ -60,8 +60,8 @@ public void onApplicationEvent(ApplicationEvent event) {
6060
@SuppressWarnings("unchecked")
6161
public boolean supportsEventType(ResolvableType eventType) {
6262
if (this.delegate instanceof SmartApplicationListener) {
63-
Class<? extends ApplicationEvent> eventClass = (Class<? extends ApplicationEvent>) eventType.getRawClass();
64-
return ((SmartApplicationListener) this.delegate).supportsEventType(eventClass);
63+
Class<? extends ApplicationEvent> eventClass = (Class<? extends ApplicationEvent>) eventType.resolve();
64+
return (eventClass != null && ((SmartApplicationListener) this.delegate).supportsEventType(eventClass));
6565
}
6666
else {
6767
return (this.declaredEventType == null || this.declaredEventType.isAssignableFrom(eventType));
@@ -70,7 +70,7 @@ public boolean supportsEventType(ResolvableType eventType) {
7070

7171
@Override
7272
public boolean supportsEventType(Class<? extends ApplicationEvent> eventType) {
73-
return supportsEventType(ResolvableType.forType(eventType));
73+
return supportsEventType(ResolvableType.forClass(eventType));
7474
}
7575

7676
@Override

0 commit comments

Comments
 (0)