RootBeanDefinition accepts ResolvableType for target type hint

Issue: SPR-14580
This commit is contained in:
Juergen Hoeller 2016-08-17 16:59:16 +02:00
parent 6a0d9d3d97
commit 4b06b60007
4 changed files with 105 additions and 18 deletions

View File

@ -624,10 +624,11 @@ public abstract class AbstractAutowireCapableBeanFactory extends AbstractBeanFac
protected Class<?> determineTargetType(String beanName, RootBeanDefinition mbd, Class<?>... typesToMatch) {
Class<?> targetType = mbd.getTargetType();
if (targetType == null) {
targetType = (mbd.getFactoryMethodName() != null ? getTypeForFactoryMethod(beanName, mbd, typesToMatch) :
targetType = (mbd.getFactoryMethodName() != null ?
getTypeForFactoryMethod(beanName, mbd, typesToMatch) :
resolveBeanClass(mbd, beanName, typesToMatch));
if (ObjectUtils.isEmpty(typesToMatch) || getTempClassLoader() == null) {
mbd.setTargetType(targetType);
mbd.resolvedTargetType = targetType;
}
}
return targetType;

View File

@ -1,5 +1,5 @@
/*
* Copyright 2002-2013 the original author or authors.
* Copyright 2002-2016 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -74,21 +74,31 @@ public class GenericTypeAwareAutowireCandidateResolver implements AutowireCandid
// No generic type -> we know it's a Class type-match, so no need to check again.
return true;
}
ResolvableType targetType = null;
boolean cacheType = false;
RootBeanDefinition rbd = null;
if (bdHolder.getBeanDefinition() instanceof RootBeanDefinition) {
rbd = (RootBeanDefinition) bdHolder.getBeanDefinition();
}
if (rbd != null) {
// First, check factory method return type, if applicable
targetType = getReturnTypeForFactoryMethod(rbd, descriptor);
targetType = rbd.targetType;
if (targetType == null) {
RootBeanDefinition dbd = getResolvedDecoratedDefinition(rbd);
if (dbd != null) {
targetType = getReturnTypeForFactoryMethod(dbd, descriptor);
cacheType = true;
// First, check factory method return type, if applicable
targetType = getReturnTypeForFactoryMethod(rbd, descriptor);
if (targetType == null) {
RootBeanDefinition dbd = getResolvedDecoratedDefinition(rbd);
if (dbd != null) {
targetType = dbd.targetType;
if (targetType == null) {
targetType = getReturnTypeForFactoryMethod(dbd, descriptor);
}
}
}
}
}
if (targetType == null) {
// Regular case: straight bean instance, with BeanFactory available.
if (this.beanFactory != null) {
@ -106,7 +116,14 @@ public class GenericTypeAwareAutowireCandidateResolver implements AutowireCandid
}
}
}
if (targetType == null || (descriptor.fallbackMatchAllowed() && targetType.hasUnresolvableGenerics())) {
if (targetType == null) {
return true;
}
if (cacheType) {
rbd.targetType = targetType;
}
if (descriptor.fallbackMatchAllowed() && targetType.hasUnresolvableGenerics()) {
return true;
}
// Full check for complex generic type match...

View File

@ -26,6 +26,7 @@ import org.springframework.beans.MutablePropertyValues;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.BeanDefinitionHolder;
import org.springframework.beans.factory.config.ConstructorArgumentValues;
import org.springframework.core.ResolvableType;
import org.springframework.util.Assert;
/**
@ -49,22 +50,26 @@ import org.springframework.util.Assert;
@SuppressWarnings("serial")
public class RootBeanDefinition extends AbstractBeanDefinition {
boolean allowCaching = true;
private BeanDefinitionHolder decoratedDefinition;
private volatile Class<?> targetType;
boolean allowCaching = true;
volatile ResolvableType targetType;
boolean isFactoryMethodUnique = false;
/** Package-visible field for caching the determined Class of a given bean definition */
volatile Class<?> resolvedTargetType;
/** Package-visible field for caching the return type of a generically typed factory method */
volatile Class<?> resolvedFactoryMethodReturnType;
/** Common lock for the four constructor fields below */
final Object constructorArgumentLock = new Object();
/** Package-visible field for caching the resolved constructor or factory method */
Executable resolvedConstructorOrFactoryMethod;
/** Package-visible field for caching the return type of a generically typed factory method */
volatile Class<?> resolvedFactoryMethodReturnType;
/** Package-visible field that marks the constructor arguments as resolved */
boolean constructorArgumentsResolved = false;
@ -74,6 +79,7 @@ public class RootBeanDefinition extends AbstractBeanDefinition {
/** Package-visible field for caching partly prepared constructor arguments */
Object[] preparedConstructorArguments;
/** Common lock for the two post-processing fields below */
final Object postProcessingLock = new Object();
/** Package-visible field that indicates MergedBeanDefinitionPostProcessor having been applied */
@ -172,8 +178,8 @@ public class RootBeanDefinition extends AbstractBeanDefinition {
*/
public RootBeanDefinition(RootBeanDefinition original) {
super(original);
this.allowCaching = original.allowCaching;
this.decoratedDefinition = original.decoratedDefinition;
this.allowCaching = original.allowCaching;
this.targetType = original.targetType;
this.isFactoryMethodUnique = original.isFactoryMethodUnique;
}
@ -214,19 +220,32 @@ public class RootBeanDefinition extends AbstractBeanDefinition {
return this.decoratedDefinition;
}
/**
* Specify a generics-containing target type of this bean definition, if known in advance.
* @since 4.3.3
*/
public void setTargetType(ResolvableType targetType) {
this.targetType = targetType;
}
/**
* Specify the target type of this bean definition, if known in advance.
* @since 3.2.2
*/
public void setTargetType(Class<?> targetType) {
this.targetType = targetType;
this.targetType = (targetType != null ? ResolvableType.forClass(targetType) : null);
}
/**
* Return the target type of this bean definition, if known
* (either specified in advance or resolved on first instantiation).
* @since 3.2.2
*/
public Class<?> getTargetType() {
return this.targetType;
if (this.resolvedTargetType != null) {
return this.resolvedTargetType;
}
return (this.targetType != null ? this.targetType.resolve() : null);
}
/**

View File

@ -41,6 +41,7 @@ import org.mockito.Mockito;
import org.springframework.beans.factory.BeanCreationException;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanNameAware;
import org.springframework.beans.factory.FactoryBean;
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
import org.springframework.beans.factory.NoUniqueBeanDefinitionException;
@ -2047,6 +2048,24 @@ public class AutowiredAnnotationBeanPostProcessorTests {
assertSame(bean2, bean1.gi2);
}
@Test
public void testGenericsBasedInjectionWithBeanDefinitionTargetResolvableType() throws Exception {
DefaultListableBeanFactory bf = new DefaultListableBeanFactory();
bf.setAutowireCandidateResolver(new QualifierAnnotationAutowireCandidateResolver());
AutowiredAnnotationBeanPostProcessor bpp = new AutowiredAnnotationBeanPostProcessor();
bpp.setBeanFactory(bf);
bf.addBeanPostProcessor(bpp);
RootBeanDefinition bd1 = new RootBeanDefinition(GenericInterface2Bean.class);
bd1.setTargetType(ResolvableType.forClassWithGenerics(GenericInterface2Bean.class, String.class));
bf.registerBeanDefinition("bean1", bd1);
RootBeanDefinition bd2 = new RootBeanDefinition(GenericInterface2Bean.class);
bd2.setTargetType(ResolvableType.forClassWithGenerics(GenericInterface2Bean.class, Integer.class));
bf.registerBeanDefinition("bean2", bd2);
bf.registerBeanDefinition("bean3", new RootBeanDefinition(MultiGenericFieldInjection.class));
assertEquals("bean1 a bean2 123", bf.getBean("bean3").toString());
}
@Test
public void testCircularTypeReference() {
DefaultListableBeanFactory bf = new DefaultListableBeanFactory();
@ -3139,6 +3158,37 @@ public class AutowiredAnnotationBeanPostProcessorTests {
}
public static class GenericInterface2Bean<K> implements GenericInterface2<K>, BeanNameAware {
private String name;
@Override
public void setBeanName(String name) {
this.name = name;
}
@Override
public String doSomethingMoreGeneric(K o) {
return this.name + " " + o;
}
}
public static class MultiGenericFieldInjection {
@Autowired
private GenericInterface2<String> stringBean;
@Autowired
private GenericInterface2<Integer> integerBean;
@Override
public String toString() {
return this.stringBean.doSomethingMoreGeneric("a") + " " + this.integerBean.doSomethingMoreGeneric(123);
}
}
@SuppressWarnings("rawtypes")
public static class PlainGenericInterface2Impl implements GenericInterface2 {