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