Honor @Autowired(required=false) at parameter level
Includes a revision of the AutowireCandidateResolver SPI with default methods. Issue: SPR-15268
This commit is contained in:
parent
07ef7a97c7
commit
d74542ed21
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright 2002-2016 the original author or authors.
|
* Copyright 2002-2017 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.
|
||||||
|
|
@ -111,6 +111,17 @@ public class InjectionPoint {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve a field/parameter annotation of the given type, if any.
|
||||||
|
* @param annotationType the annotation type to retrieve
|
||||||
|
* @return the annotation instance, or {@code null} if none found
|
||||||
|
* @since 4.3.9
|
||||||
|
*/
|
||||||
|
public <A extends Annotation> A getAnnotation(Class<A> annotationType) {
|
||||||
|
return (this.field != null ? this.field.getAnnotation(annotationType) :
|
||||||
|
this.methodParameter.getParameterAnnotation(annotationType));
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return the type declared by the underlying field or method/constructor parameter,
|
* Return the type declared by the underlying field or method/constructor parameter,
|
||||||
* indicating the injection type.
|
* indicating the injection type.
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright 2002-2016 the original author or authors.
|
* Copyright 2002-2017 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.
|
||||||
|
|
@ -309,7 +309,21 @@ public class QualifierAnnotationAutowireCandidateResolver extends GenericTypeAwa
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Determine whether the given dependency carries a value annotation.
|
* Determine whether the given dependency declares an autowired annotation,
|
||||||
|
* checking its required flag.
|
||||||
|
* @see Autowired#required()
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public boolean isRequired(DependencyDescriptor descriptor) {
|
||||||
|
if (!super.isRequired(descriptor)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
Autowired autowired = descriptor.getAnnotation(Autowired.class);
|
||||||
|
return (autowired == null || autowired.required());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determine whether the given dependency declares a value annotation.
|
||||||
* @see Value
|
* @see Value
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright 2002-2013 the original author or authors.
|
* Copyright 2002-2017 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.
|
||||||
|
|
@ -32,30 +32,54 @@ public interface AutowireCandidateResolver {
|
||||||
/**
|
/**
|
||||||
* Determine whether the given bean definition qualifies as an
|
* Determine whether the given bean definition qualifies as an
|
||||||
* autowire candidate for the given dependency.
|
* autowire candidate for the given dependency.
|
||||||
|
* <p>The default implementation checks
|
||||||
|
* {@link org.springframework.beans.factory.config.BeanDefinition#isAutowireCandidate()}.
|
||||||
* @param bdHolder the bean definition including bean name and aliases
|
* @param bdHolder the bean definition including bean name and aliases
|
||||||
* @param descriptor the descriptor for the target method parameter or field
|
* @param descriptor the descriptor for the target method parameter or field
|
||||||
* @return whether the bean definition qualifies as autowire candidate
|
* @return whether the bean definition qualifies as autowire candidate
|
||||||
|
* @see org.springframework.beans.factory.config.BeanDefinition#isAutowireCandidate()
|
||||||
*/
|
*/
|
||||||
boolean isAutowireCandidate(BeanDefinitionHolder bdHolder, DependencyDescriptor descriptor);
|
default boolean isAutowireCandidate(BeanDefinitionHolder bdHolder, DependencyDescriptor descriptor) {
|
||||||
|
return bdHolder.getBeanDefinition().isAutowireCandidate();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determine whether the given descriptor is effectively required.
|
||||||
|
* <p>The default implementation checks {@link DependencyDescriptor#isRequired()}.
|
||||||
|
* @param descriptor the descriptor for the target method parameter or field
|
||||||
|
* @return whether the descriptor is marked as required or possibly indicating
|
||||||
|
* non-required status some other way (e.g. through a parameter annotation)
|
||||||
|
* @since 5.0
|
||||||
|
* @see DependencyDescriptor#isRequired()
|
||||||
|
*/
|
||||||
|
default boolean isRequired(DependencyDescriptor descriptor) {
|
||||||
|
return descriptor.isRequired();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Determine whether a default value is suggested for the given dependency.
|
* Determine whether a default value is suggested for the given dependency.
|
||||||
|
* <p>The default implementation simply returns {@code null}.
|
||||||
* @param descriptor the descriptor for the target method parameter or field
|
* @param descriptor the descriptor for the target method parameter or field
|
||||||
* @return the value suggested (typically an expression String),
|
* @return the value suggested (typically an expression String),
|
||||||
* or {@code null} if none found
|
* or {@code null} if none found
|
||||||
* @since 3.0
|
* @since 3.0
|
||||||
*/
|
*/
|
||||||
Object getSuggestedValue(DependencyDescriptor descriptor);
|
default Object getSuggestedValue(DependencyDescriptor descriptor) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Build a proxy for lazy resolution of the actual dependency target,
|
* Build a proxy for lazy resolution of the actual dependency target,
|
||||||
* if demanded by the injection point.
|
* if demanded by the injection point.
|
||||||
|
* <p>The default implementation simply returns {@code null}.
|
||||||
* @param descriptor the descriptor for the target method parameter or field
|
* @param descriptor the descriptor for the target method parameter or field
|
||||||
* @param beanName the name of the bean that contains the injection point
|
* @param beanName the name of the bean that contains the injection point
|
||||||
* @return the lazy resolution proxy for the actual dependency target,
|
* @return the lazy resolution proxy for the actual dependency target,
|
||||||
* or {@code null} if straight resolution is to be performed
|
* or {@code null} if straight resolution is to be performed
|
||||||
* @since 4.0
|
* @since 4.0
|
||||||
*/
|
*/
|
||||||
Object getLazyResolutionProxyIfNecessary(DependencyDescriptor descriptor, String beanName);
|
default Object getLazyResolutionProxyIfNecessary(DependencyDescriptor descriptor, String beanName) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1090,7 +1090,7 @@ public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFacto
|
||||||
|
|
||||||
Map<String, Object> matchingBeans = findAutowireCandidates(beanName, type, descriptor);
|
Map<String, Object> matchingBeans = findAutowireCandidates(beanName, type, descriptor);
|
||||||
if (matchingBeans.isEmpty()) {
|
if (matchingBeans.isEmpty()) {
|
||||||
if (descriptor.isRequired()) {
|
if (isRequired(descriptor)) {
|
||||||
raiseNoMatchingBeanFound(type, descriptor.getResolvableType(), descriptor);
|
raiseNoMatchingBeanFound(type, descriptor.getResolvableType(), descriptor);
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
|
|
@ -1102,7 +1102,7 @@ public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFacto
|
||||||
if (matchingBeans.size() > 1) {
|
if (matchingBeans.size() > 1) {
|
||||||
autowiredBeanName = determineAutowireCandidate(matchingBeans, descriptor);
|
autowiredBeanName = determineAutowireCandidate(matchingBeans, descriptor);
|
||||||
if (autowiredBeanName == null) {
|
if (autowiredBeanName == null) {
|
||||||
if (descriptor.isRequired() || !indicatesMultipleBeans(type)) {
|
if (isRequired(descriptor) || !indicatesMultipleBeans(type)) {
|
||||||
return descriptor.resolveNotUnique(type, matchingBeans);
|
return descriptor.resolveNotUnique(type, matchingBeans);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
|
|
@ -1207,6 +1207,10 @@ public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFacto
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private boolean isRequired(DependencyDescriptor descriptor) {
|
||||||
|
return this.autowireCandidateResolver.isRequired(descriptor);
|
||||||
|
}
|
||||||
|
|
||||||
private boolean indicatesMultipleBeans(Class<?> type) {
|
private boolean indicatesMultipleBeans(Class<?> type) {
|
||||||
return (type.isArray() || (type.isInterface() &&
|
return (type.isArray() || (type.isInterface() &&
|
||||||
(Collection.class.isAssignableFrom(type) || Map.class.isAssignableFrom(type))));
|
(Collection.class.isAssignableFrom(type) || Map.class.isAssignableFrom(type))));
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright 2002-2016 the original author or authors.
|
* Copyright 2002-2017 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.
|
||||||
|
|
@ -40,7 +40,8 @@ import org.springframework.util.ClassUtils;
|
||||||
* @author Juergen Hoeller
|
* @author Juergen Hoeller
|
||||||
* @since 4.0
|
* @since 4.0
|
||||||
*/
|
*/
|
||||||
public class GenericTypeAwareAutowireCandidateResolver implements AutowireCandidateResolver, BeanFactoryAware {
|
public class GenericTypeAwareAutowireCandidateResolver extends SimpleAutowireCandidateResolver
|
||||||
|
implements BeanFactoryAware {
|
||||||
|
|
||||||
private BeanFactory beanFactory;
|
private BeanFactory beanFactory;
|
||||||
|
|
||||||
|
|
@ -57,8 +58,8 @@ public class GenericTypeAwareAutowireCandidateResolver implements AutowireCandid
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean isAutowireCandidate(BeanDefinitionHolder bdHolder, DependencyDescriptor descriptor) {
|
public boolean isAutowireCandidate(BeanDefinitionHolder bdHolder, DependencyDescriptor descriptor) {
|
||||||
if (!bdHolder.getBeanDefinition().isAutowireCandidate()) {
|
if (!super.isAutowireCandidate(bdHolder, descriptor)) {
|
||||||
// if explicitly false, do not proceed with any other checks
|
// If explicitly false, do not proceed with any other checks...
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return (descriptor == null || checkGenericTypeMatch(bdHolder, descriptor));
|
return (descriptor == null || checkGenericTypeMatch(bdHolder, descriptor));
|
||||||
|
|
@ -166,23 +167,4 @@ public class GenericTypeAwareAutowireCandidateResolver implements AutowireCandid
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This implementation always returns {@code null}, leaving suggested value support up
|
|
||||||
* to subclasses.
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
public Object getSuggestedValue(DependencyDescriptor descriptor) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This implementation always returns {@code null}, leaving lazy resolution support up
|
|
||||||
* to subclasses.
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
public Object getLazyResolutionProxyIfNecessary(DependencyDescriptor descriptor, String beanName) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright 2002-2013 the original author or authors.
|
* Copyright 2002-2017 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.
|
||||||
|
|
@ -16,7 +16,6 @@
|
||||||
|
|
||||||
package org.springframework.beans.factory.support;
|
package org.springframework.beans.factory.support;
|
||||||
|
|
||||||
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.DependencyDescriptor;
|
import org.springframework.beans.factory.config.DependencyDescriptor;
|
||||||
|
|
||||||
|
|
@ -27,20 +26,19 @@ import org.springframework.beans.factory.config.DependencyDescriptor;
|
||||||
* @author Mark Fisher
|
* @author Mark Fisher
|
||||||
* @author Juergen Hoeller
|
* @author Juergen Hoeller
|
||||||
* @since 2.5
|
* @since 2.5
|
||||||
* @see BeanDefinition#isAutowireCandidate()
|
|
||||||
*/
|
*/
|
||||||
public class SimpleAutowireCandidateResolver implements AutowireCandidateResolver {
|
public class SimpleAutowireCandidateResolver implements AutowireCandidateResolver {
|
||||||
|
|
||||||
/**
|
|
||||||
* Determine if the provided bean definition is an autowire candidate.
|
|
||||||
* <p>To be considered a candidate the bean's <em>autowire-candidate</em>
|
|
||||||
* attribute must not have been set to 'false'.
|
|
||||||
*/
|
|
||||||
@Override
|
@Override
|
||||||
public boolean isAutowireCandidate(BeanDefinitionHolder bdHolder, DependencyDescriptor descriptor) {
|
public boolean isAutowireCandidate(BeanDefinitionHolder bdHolder, DependencyDescriptor descriptor) {
|
||||||
return bdHolder.getBeanDefinition().isAutowireCandidate();
|
return bdHolder.getBeanDefinition().isAutowireCandidate();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isRequired(DependencyDescriptor descriptor) {
|
||||||
|
return descriptor.isRequired();
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Object getSuggestedValue(DependencyDescriptor descriptor) {
|
public Object getSuggestedValue(DependencyDescriptor descriptor) {
|
||||||
return null;
|
return null;
|
||||||
|
|
|
||||||
|
|
@ -699,8 +699,7 @@ public class AutowiredAnnotationBeanPostProcessorTests {
|
||||||
AutowiredAnnotationBeanPostProcessor bpp = new AutowiredAnnotationBeanPostProcessor();
|
AutowiredAnnotationBeanPostProcessor bpp = new AutowiredAnnotationBeanPostProcessor();
|
||||||
bpp.setBeanFactory(bf);
|
bpp.setBeanFactory(bf);
|
||||||
bf.addBeanPostProcessor(bpp);
|
bf.addBeanPostProcessor(bpp);
|
||||||
bf.registerBeanDefinition("annotatedBean", new RootBeanDefinition(
|
bf.registerBeanDefinition("annotatedBean", new RootBeanDefinition(ConstructorsCollectionResourceInjectionBean.class));
|
||||||
ConstructorsCollectionResourceInjectionBean.class));
|
|
||||||
TestBean tb = new TestBean();
|
TestBean tb = new TestBean();
|
||||||
bf.registerSingleton("testBean", tb);
|
bf.registerSingleton("testBean", tb);
|
||||||
FixedOrder2NestedTestBean ntb1 = new FixedOrder2NestedTestBean();
|
FixedOrder2NestedTestBean ntb1 = new FixedOrder2NestedTestBean();
|
||||||
|
|
@ -717,6 +716,46 @@ public class AutowiredAnnotationBeanPostProcessorTests {
|
||||||
bf.destroySingletons();
|
bf.destroySingletons();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testSingleConstructorInjectionWithMultipleCandidatesAsOrderedCollection() {
|
||||||
|
DefaultListableBeanFactory bf = new DefaultListableBeanFactory();
|
||||||
|
bf.setDependencyComparator(AnnotationAwareOrderComparator.INSTANCE);
|
||||||
|
AutowiredAnnotationBeanPostProcessor bpp = new AutowiredAnnotationBeanPostProcessor();
|
||||||
|
bpp.setBeanFactory(bf);
|
||||||
|
bf.addBeanPostProcessor(bpp);
|
||||||
|
bf.registerBeanDefinition("annotatedBean", new RootBeanDefinition(SingleConstructorCollectionInjectionBean.class));
|
||||||
|
TestBean tb = new TestBean();
|
||||||
|
bf.registerSingleton("testBean", tb);
|
||||||
|
FixedOrder2NestedTestBean ntb1 = new FixedOrder2NestedTestBean();
|
||||||
|
bf.registerSingleton("nestedTestBean1", ntb1);
|
||||||
|
FixedOrder1NestedTestBean ntb2 = new FixedOrder1NestedTestBean();
|
||||||
|
bf.registerSingleton("nestedTestBean2", ntb2);
|
||||||
|
|
||||||
|
SingleConstructorCollectionInjectionBean bean = (SingleConstructorCollectionInjectionBean) bf.getBean("annotatedBean");
|
||||||
|
assertSame(tb, bean.getTestBean());
|
||||||
|
assertEquals(2, bean.getNestedTestBeans().size());
|
||||||
|
assertSame(ntb2, bean.getNestedTestBeans().get(0));
|
||||||
|
assertSame(ntb1, bean.getNestedTestBeans().get(1));
|
||||||
|
bf.destroySingletons();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testSingleConstructorInjectionWithEmptyCollection() {
|
||||||
|
DefaultListableBeanFactory bf = new DefaultListableBeanFactory();
|
||||||
|
bf.setAutowireCandidateResolver(new QualifierAnnotationAutowireCandidateResolver());
|
||||||
|
AutowiredAnnotationBeanPostProcessor bpp = new AutowiredAnnotationBeanPostProcessor();
|
||||||
|
bpp.setBeanFactory(bf);
|
||||||
|
bf.addBeanPostProcessor(bpp);
|
||||||
|
bf.registerBeanDefinition("annotatedBean", new RootBeanDefinition(SingleConstructorCollectionInjectionBean.class));
|
||||||
|
TestBean tb = new TestBean();
|
||||||
|
bf.registerSingleton("testBean", tb);
|
||||||
|
|
||||||
|
SingleConstructorCollectionInjectionBean bean = (SingleConstructorCollectionInjectionBean) bf.getBean("annotatedBean");
|
||||||
|
assertSame(tb, bean.getTestBean());
|
||||||
|
assertNull(bean.getNestedTestBeans());
|
||||||
|
bf.destroySingletons();
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testConstructorResourceInjectionWithMultipleCandidatesAndFallback() {
|
public void testConstructorResourceInjectionWithMultipleCandidatesAndFallback() {
|
||||||
DefaultListableBeanFactory bf = new DefaultListableBeanFactory();
|
DefaultListableBeanFactory bf = new DefaultListableBeanFactory();
|
||||||
|
|
@ -2757,6 +2796,28 @@ public class AutowiredAnnotationBeanPostProcessorTests {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public static class SingleConstructorCollectionInjectionBean {
|
||||||
|
|
||||||
|
private ITestBean testBean;
|
||||||
|
|
||||||
|
private List<NestedTestBean> nestedTestBeans;
|
||||||
|
|
||||||
|
public SingleConstructorCollectionInjectionBean(ITestBean testBean,
|
||||||
|
@Autowired(required = false) List<NestedTestBean> nestedTestBeans) {
|
||||||
|
this.testBean = testBean;
|
||||||
|
this.nestedTestBeans = nestedTestBeans;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ITestBean getTestBean() {
|
||||||
|
return this.testBean;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<NestedTestBean> getNestedTestBeans() {
|
||||||
|
return this.nestedTestBeans;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
@SuppressWarnings("serial")
|
@SuppressWarnings("serial")
|
||||||
public static class MyTestBeanMap extends LinkedHashMap<String, TestBean> {
|
public static class MyTestBeanMap extends LinkedHashMap<String, TestBean> {
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue