Browse Source

RootBeanDefinition accepts ResolvableType for target type hint

Issue: SPR-14580
pull/1502/head
Juergen Hoeller 10 years ago
parent
commit
4b06b60007
  1. 5
      spring-beans/src/main/java/org/springframework/beans/factory/support/AbstractAutowireCapableBeanFactory.java
  2. 31
      spring-beans/src/main/java/org/springframework/beans/factory/support/GenericTypeAwareAutowireCandidateResolver.java
  3. 37
      spring-beans/src/main/java/org/springframework/beans/factory/support/RootBeanDefinition.java
  4. 50
      spring-beans/src/test/java/org/springframework/beans/factory/annotation/AutowiredAnnotationBeanPostProcessorTests.java

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

@ -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;

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

@ -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...

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

@ -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);
} }
/** /**

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

@ -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 {

Loading…
Cancel
Save