RootBeanDefinition accepts ResolvableType for target type hint
Issue: SPR-14580
This commit is contained in:
parent
6a0d9d3d97
commit
4b06b60007
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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...
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue