RootBeanDefinition accepts ResolvableType for target type hint
Issue: SPR-14580
This commit is contained in:
@@ -624,10 +624,11 @@ public abstract class AbstractAutowireCapableBeanFactory extends AbstractBeanFac
|
|||||||
protected Class<?> determineTargetType(String beanName, RootBeanDefinition mbd, Class<?>... typesToMatch) {
|
protected Class<?> determineTargetType(String beanName, RootBeanDefinition mbd, Class<?>... typesToMatch) {
|
||||||
Class<?> targetType = mbd.getTargetType();
|
Class<?> targetType = mbd.getTargetType();
|
||||||
if (targetType == null) {
|
if (targetType == null) {
|
||||||
targetType = (mbd.getFactoryMethodName() != null ? getTypeForFactoryMethod(beanName, mbd, typesToMatch) :
|
targetType = (mbd.getFactoryMethodName() != null ?
|
||||||
|
getTypeForFactoryMethod(beanName, mbd, typesToMatch) :
|
||||||
resolveBeanClass(mbd, beanName, typesToMatch));
|
resolveBeanClass(mbd, beanName, typesToMatch));
|
||||||
if (ObjectUtils.isEmpty(typesToMatch) || getTempClassLoader() == null) {
|
if (ObjectUtils.isEmpty(typesToMatch) || getTempClassLoader() == null) {
|
||||||
mbd.setTargetType(targetType);
|
mbd.resolvedTargetType = targetType;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return 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");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with 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.
|
// No generic type -> we know it's a Class type-match, so no need to check again.
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
ResolvableType targetType = null;
|
ResolvableType targetType = null;
|
||||||
|
boolean cacheType = false;
|
||||||
RootBeanDefinition rbd = null;
|
RootBeanDefinition rbd = null;
|
||||||
if (bdHolder.getBeanDefinition() instanceof RootBeanDefinition) {
|
if (bdHolder.getBeanDefinition() instanceof RootBeanDefinition) {
|
||||||
rbd = (RootBeanDefinition) bdHolder.getBeanDefinition();
|
rbd = (RootBeanDefinition) bdHolder.getBeanDefinition();
|
||||||
}
|
}
|
||||||
if (rbd != null) {
|
if (rbd != null) {
|
||||||
// First, check factory method return type, if applicable
|
targetType = rbd.targetType;
|
||||||
targetType = getReturnTypeForFactoryMethod(rbd, descriptor);
|
|
||||||
if (targetType == null) {
|
if (targetType == null) {
|
||||||
RootBeanDefinition dbd = getResolvedDecoratedDefinition(rbd);
|
cacheType = true;
|
||||||
if (dbd != null) {
|
// First, check factory method return type, if applicable
|
||||||
targetType = getReturnTypeForFactoryMethod(dbd, descriptor);
|
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) {
|
if (targetType == null) {
|
||||||
// Regular case: straight bean instance, with BeanFactory available.
|
// Regular case: straight bean instance, with BeanFactory available.
|
||||||
if (this.beanFactory != null) {
|
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;
|
return true;
|
||||||
}
|
}
|
||||||
// Full check for complex generic type match...
|
// 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.BeanDefinition;
|
||||||
import org.springframework.beans.factory.config.BeanDefinitionHolder;
|
import org.springframework.beans.factory.config.BeanDefinitionHolder;
|
||||||
import org.springframework.beans.factory.config.ConstructorArgumentValues;
|
import org.springframework.beans.factory.config.ConstructorArgumentValues;
|
||||||
|
import org.springframework.core.ResolvableType;
|
||||||
import org.springframework.util.Assert;
|
import org.springframework.util.Assert;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -49,22 +50,26 @@ import org.springframework.util.Assert;
|
|||||||
@SuppressWarnings("serial")
|
@SuppressWarnings("serial")
|
||||||
public class RootBeanDefinition extends AbstractBeanDefinition {
|
public class RootBeanDefinition extends AbstractBeanDefinition {
|
||||||
|
|
||||||
boolean allowCaching = true;
|
|
||||||
|
|
||||||
private BeanDefinitionHolder decoratedDefinition;
|
private BeanDefinitionHolder decoratedDefinition;
|
||||||
|
|
||||||
private volatile Class<?> targetType;
|
boolean allowCaching = true;
|
||||||
|
|
||||||
|
volatile ResolvableType targetType;
|
||||||
|
|
||||||
boolean isFactoryMethodUnique = false;
|
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();
|
final Object constructorArgumentLock = new Object();
|
||||||
|
|
||||||
/** Package-visible field for caching the resolved constructor or factory method */
|
/** Package-visible field for caching the resolved constructor or factory method */
|
||||||
Executable resolvedConstructorOrFactoryMethod;
|
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 */
|
/** Package-visible field that marks the constructor arguments as resolved */
|
||||||
boolean constructorArgumentsResolved = false;
|
boolean constructorArgumentsResolved = false;
|
||||||
|
|
||||||
@@ -74,6 +79,7 @@ public class RootBeanDefinition extends AbstractBeanDefinition {
|
|||||||
/** Package-visible field for caching partly prepared constructor arguments */
|
/** Package-visible field for caching partly prepared constructor arguments */
|
||||||
Object[] preparedConstructorArguments;
|
Object[] preparedConstructorArguments;
|
||||||
|
|
||||||
|
/** Common lock for the two post-processing fields below */
|
||||||
final Object postProcessingLock = new Object();
|
final Object postProcessingLock = new Object();
|
||||||
|
|
||||||
/** Package-visible field that indicates MergedBeanDefinitionPostProcessor having been applied */
|
/** Package-visible field that indicates MergedBeanDefinitionPostProcessor having been applied */
|
||||||
@@ -172,8 +178,8 @@ public class RootBeanDefinition extends AbstractBeanDefinition {
|
|||||||
*/
|
*/
|
||||||
public RootBeanDefinition(RootBeanDefinition original) {
|
public RootBeanDefinition(RootBeanDefinition original) {
|
||||||
super(original);
|
super(original);
|
||||||
this.allowCaching = original.allowCaching;
|
|
||||||
this.decoratedDefinition = original.decoratedDefinition;
|
this.decoratedDefinition = original.decoratedDefinition;
|
||||||
|
this.allowCaching = original.allowCaching;
|
||||||
this.targetType = original.targetType;
|
this.targetType = original.targetType;
|
||||||
this.isFactoryMethodUnique = original.isFactoryMethodUnique;
|
this.isFactoryMethodUnique = original.isFactoryMethodUnique;
|
||||||
}
|
}
|
||||||
@@ -214,19 +220,32 @@ public class RootBeanDefinition extends AbstractBeanDefinition {
|
|||||||
return this.decoratedDefinition;
|
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.
|
* Specify the target type of this bean definition, if known in advance.
|
||||||
|
* @since 3.2.2
|
||||||
*/
|
*/
|
||||||
public void setTargetType(Class<?> targetType) {
|
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
|
* Return the target type of this bean definition, if known
|
||||||
* (either specified in advance or resolved on first instantiation).
|
* (either specified in advance or resolved on first instantiation).
|
||||||
|
* @since 3.2.2
|
||||||
*/
|
*/
|
||||||
public Class<?> getTargetType() {
|
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.BeanCreationException;
|
||||||
import org.springframework.beans.factory.BeanFactory;
|
import org.springframework.beans.factory.BeanFactory;
|
||||||
|
import org.springframework.beans.factory.BeanNameAware;
|
||||||
import org.springframework.beans.factory.FactoryBean;
|
import org.springframework.beans.factory.FactoryBean;
|
||||||
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
|
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
|
||||||
import org.springframework.beans.factory.NoUniqueBeanDefinitionException;
|
import org.springframework.beans.factory.NoUniqueBeanDefinitionException;
|
||||||
@@ -2047,6 +2048,24 @@ public class AutowiredAnnotationBeanPostProcessorTests {
|
|||||||
assertSame(bean2, bean1.gi2);
|
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
|
@Test
|
||||||
public void testCircularTypeReference() {
|
public void testCircularTypeReference() {
|
||||||
DefaultListableBeanFactory bf = new DefaultListableBeanFactory();
|
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")
|
@SuppressWarnings("rawtypes")
|
||||||
public static class PlainGenericInterface2Impl implements GenericInterface2 {
|
public static class PlainGenericInterface2Impl implements GenericInterface2 {
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user