Skip to content

Commit beac891

Browse files
committed
Honor @Autowired(required=false) at parameter level
Issue: SPR-15268 (cherry picked from commit d74542e)
1 parent 2d1b551 commit beac891

File tree

6 files changed

+119
-38
lines changed

6 files changed

+119
-38
lines changed

spring-beans/src/main/java/org/springframework/beans/factory/InjectionPoint.java

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2016 the original author or authors.
2+
* Copyright 2002-2017 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -111,6 +111,17 @@ public Annotation[] getAnnotations() {
111111
}
112112
}
113113

114+
/**
115+
* Retrieve a field/parameter annotation of the given type, if any.
116+
* @param annotationType the annotation type to retrieve
117+
* @return the annotation instance, or {@code null} if none found
118+
* @since 4.3.9
119+
*/
120+
public <A extends Annotation> A getAnnotation(Class<A> annotationType) {
121+
return (this.field != null ? this.field.getAnnotation(annotationType) :
122+
this.methodParameter.getParameterAnnotation(annotationType));
123+
}
124+
114125
/**
115126
* Return the type declared by the underlying field or method/constructor parameter,
116127
* indicating the injection type.

spring-beans/src/main/java/org/springframework/beans/factory/annotation/QualifierAnnotationAutowireCandidateResolver.java

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2016 the original author or authors.
2+
* Copyright 2002-2017 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -309,7 +309,21 @@ protected Annotation getFactoryMethodAnnotation(RootBeanDefinition bd, Class<? e
309309

310310

311311
/**
312-
* Determine whether the given dependency carries a value annotation.
312+
* Determine whether the given dependency declares an autowired annotation,
313+
* checking its required flag.
314+
* @see Autowired#required()
315+
*/
316+
@Override
317+
public boolean isRequired(DependencyDescriptor descriptor) {
318+
if (!super.isRequired(descriptor)) {
319+
return false;
320+
}
321+
Autowired autowired = descriptor.getAnnotation(Autowired.class);
322+
return (autowired == null || autowired.required());
323+
}
324+
325+
/**
326+
* Determine whether the given dependency declares a value annotation.
313327
* @see Value
314328
*/
315329
@Override

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

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1100,7 +1100,7 @@ public Object doResolveDependency(DependencyDescriptor descriptor, String beanNa
11001100

11011101
Map<String, Object> matchingBeans = findAutowireCandidates(beanName, type, descriptor);
11021102
if (matchingBeans.isEmpty()) {
1103-
if (descriptor.isRequired()) {
1103+
if (isRequired(descriptor)) {
11041104
raiseNoMatchingBeanFound(type, descriptor.getResolvableType(), descriptor);
11051105
}
11061106
return null;
@@ -1112,7 +1112,7 @@ public Object doResolveDependency(DependencyDescriptor descriptor, String beanNa
11121112
if (matchingBeans.size() > 1) {
11131113
autowiredBeanName = determineAutowireCandidate(matchingBeans, descriptor);
11141114
if (autowiredBeanName == null) {
1115-
if (descriptor.isRequired() || !indicatesMultipleBeans(type)) {
1115+
if (isRequired(descriptor) || !indicatesMultipleBeans(type)) {
11161116
return descriptor.resolveNotUnique(type, matchingBeans);
11171117
}
11181118
else {
@@ -1217,6 +1217,13 @@ else if (Map.class == type) {
12171217
}
12181218
}
12191219

1220+
private boolean isRequired(DependencyDescriptor descriptor) {
1221+
AutowireCandidateResolver resolver = getAutowireCandidateResolver();
1222+
return (resolver instanceof SimpleAutowireCandidateResolver ?
1223+
((SimpleAutowireCandidateResolver) resolver).isRequired(descriptor) :
1224+
descriptor.isRequired());
1225+
}
1226+
12201227
private boolean indicatesMultipleBeans(Class<?> type) {
12211228
return (type.isArray() || (type.isInterface() &&
12221229
(Collection.class.isAssignableFrom(type) || Map.class.isAssignableFrom(type))));

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

Lines changed: 5 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2016 the original author or authors.
2+
* Copyright 2002-2017 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -40,7 +40,8 @@
4040
* @author Juergen Hoeller
4141
* @since 4.0
4242
*/
43-
public class GenericTypeAwareAutowireCandidateResolver implements AutowireCandidateResolver, BeanFactoryAware {
43+
public class GenericTypeAwareAutowireCandidateResolver extends SimpleAutowireCandidateResolver
44+
implements BeanFactoryAware {
4445

4546
private BeanFactory beanFactory;
4647

@@ -57,8 +58,8 @@ protected final BeanFactory getBeanFactory() {
5758

5859
@Override
5960
public boolean isAutowireCandidate(BeanDefinitionHolder bdHolder, DependencyDescriptor descriptor) {
60-
if (!bdHolder.getBeanDefinition().isAutowireCandidate()) {
61-
// if explicitly false, do not proceed with any other checks
61+
if (!super.isAutowireCandidate(bdHolder, descriptor)) {
62+
// If explicitly false, do not proceed with any other checks...
6263
return false;
6364
}
6465
return (descriptor == null || checkGenericTypeMatch(bdHolder, descriptor));
@@ -166,23 +167,4 @@ protected ResolvableType getReturnTypeForFactoryMethod(RootBeanDefinition rbd, D
166167
return null;
167168
}
168169

169-
170-
/**
171-
* This implementation always returns {@code null}, leaving suggested value support up
172-
* to subclasses.
173-
*/
174-
@Override
175-
public Object getSuggestedValue(DependencyDescriptor descriptor) {
176-
return null;
177-
}
178-
179-
/**
180-
* This implementation always returns {@code null}, leaving lazy resolution support up
181-
* to subclasses.
182-
*/
183-
@Override
184-
public Object getLazyResolutionProxyIfNecessary(DependencyDescriptor descriptor, String beanName) {
185-
return null;
186-
}
187-
188170
}

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

Lines changed: 14 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2013 the original author or authors.
2+
* Copyright 2002-2017 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -16,7 +16,6 @@
1616

1717
package org.springframework.beans.factory.support;
1818

19-
import org.springframework.beans.factory.config.BeanDefinition;
2019
import org.springframework.beans.factory.config.BeanDefinitionHolder;
2120
import org.springframework.beans.factory.config.DependencyDescriptor;
2221

@@ -27,20 +26,27 @@
2726
* @author Mark Fisher
2827
* @author Juergen Hoeller
2928
* @since 2.5
30-
* @see BeanDefinition#isAutowireCandidate()
3129
*/
3230
public class SimpleAutowireCandidateResolver implements AutowireCandidateResolver {
3331

34-
/**
35-
* Determine if the provided bean definition is an autowire candidate.
36-
* <p>To be considered a candidate the bean's <em>autowire-candidate</em>
37-
* attribute must not have been set to 'false'.
38-
*/
3932
@Override
4033
public boolean isAutowireCandidate(BeanDefinitionHolder bdHolder, DependencyDescriptor descriptor) {
4134
return bdHolder.getBeanDefinition().isAutowireCandidate();
4235
}
4336

37+
/**
38+
* Determine whether the given descriptor is effectively required.
39+
* <p>The default implementation checks {@link DependencyDescriptor#isRequired()}.
40+
* @param descriptor the descriptor for the target method parameter or field
41+
* @return whether the descriptor is marked as required or possibly indicating
42+
* non-required status some other way (e.g. through a parameter annotation)
43+
* @since 4.3.9
44+
* @see DependencyDescriptor#isRequired()
45+
*/
46+
public boolean isRequired(DependencyDescriptor descriptor) {
47+
return descriptor.isRequired();
48+
}
49+
4450
@Override
4551
public Object getSuggestedValue(DependencyDescriptor descriptor) {
4652
return null;

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

Lines changed: 63 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -699,8 +699,7 @@ public void testConstructorResourceInjectionWithMultipleCandidatesAsOrderedColle
699699
AutowiredAnnotationBeanPostProcessor bpp = new AutowiredAnnotationBeanPostProcessor();
700700
bpp.setBeanFactory(bf);
701701
bf.addBeanPostProcessor(bpp);
702-
bf.registerBeanDefinition("annotatedBean", new RootBeanDefinition(
703-
ConstructorsCollectionResourceInjectionBean.class));
702+
bf.registerBeanDefinition("annotatedBean", new RootBeanDefinition(ConstructorsCollectionResourceInjectionBean.class));
704703
TestBean tb = new TestBean();
705704
bf.registerSingleton("testBean", tb);
706705
FixedOrder2NestedTestBean ntb1 = new FixedOrder2NestedTestBean();
@@ -717,6 +716,46 @@ public void testConstructorResourceInjectionWithMultipleCandidatesAsOrderedColle
717716
bf.destroySingletons();
718717
}
719718

719+
@Test
720+
public void testSingleConstructorInjectionWithMultipleCandidatesAsOrderedCollection() {
721+
DefaultListableBeanFactory bf = new DefaultListableBeanFactory();
722+
bf.setDependencyComparator(AnnotationAwareOrderComparator.INSTANCE);
723+
AutowiredAnnotationBeanPostProcessor bpp = new AutowiredAnnotationBeanPostProcessor();
724+
bpp.setBeanFactory(bf);
725+
bf.addBeanPostProcessor(bpp);
726+
bf.registerBeanDefinition("annotatedBean", new RootBeanDefinition(SingleConstructorCollectionInjectionBean.class));
727+
TestBean tb = new TestBean();
728+
bf.registerSingleton("testBean", tb);
729+
FixedOrder2NestedTestBean ntb1 = new FixedOrder2NestedTestBean();
730+
bf.registerSingleton("nestedTestBean1", ntb1);
731+
FixedOrder1NestedTestBean ntb2 = new FixedOrder1NestedTestBean();
732+
bf.registerSingleton("nestedTestBean2", ntb2);
733+
734+
SingleConstructorCollectionInjectionBean bean = (SingleConstructorCollectionInjectionBean) bf.getBean("annotatedBean");
735+
assertSame(tb, bean.getTestBean());
736+
assertEquals(2, bean.getNestedTestBeans().size());
737+
assertSame(ntb2, bean.getNestedTestBeans().get(0));
738+
assertSame(ntb1, bean.getNestedTestBeans().get(1));
739+
bf.destroySingletons();
740+
}
741+
742+
@Test
743+
public void testSingleConstructorInjectionWithEmptyCollection() {
744+
DefaultListableBeanFactory bf = new DefaultListableBeanFactory();
745+
bf.setAutowireCandidateResolver(new QualifierAnnotationAutowireCandidateResolver());
746+
AutowiredAnnotationBeanPostProcessor bpp = new AutowiredAnnotationBeanPostProcessor();
747+
bpp.setBeanFactory(bf);
748+
bf.addBeanPostProcessor(bpp);
749+
bf.registerBeanDefinition("annotatedBean", new RootBeanDefinition(SingleConstructorCollectionInjectionBean.class));
750+
TestBean tb = new TestBean();
751+
bf.registerSingleton("testBean", tb);
752+
753+
SingleConstructorCollectionInjectionBean bean = (SingleConstructorCollectionInjectionBean) bf.getBean("annotatedBean");
754+
assertSame(tb, bean.getTestBean());
755+
assertNull(bean.getNestedTestBeans());
756+
bf.destroySingletons();
757+
}
758+
720759
@Test
721760
public void testConstructorResourceInjectionWithMultipleCandidatesAndFallback() {
722761
DefaultListableBeanFactory bf = new DefaultListableBeanFactory();
@@ -2751,6 +2790,28 @@ public List<NestedTestBean> getNestedTestBeans() {
27512790
}
27522791

27532792

2793+
public static class SingleConstructorCollectionInjectionBean {
2794+
2795+
private ITestBean testBean;
2796+
2797+
private List<NestedTestBean> nestedTestBeans;
2798+
2799+
public SingleConstructorCollectionInjectionBean(ITestBean testBean,
2800+
@Autowired(required = false) List<NestedTestBean> nestedTestBeans) {
2801+
this.testBean = testBean;
2802+
this.nestedTestBeans = nestedTestBeans;
2803+
}
2804+
2805+
public ITestBean getTestBean() {
2806+
return this.testBean;
2807+
}
2808+
2809+
public List<NestedTestBean> getNestedTestBeans() {
2810+
return this.nestedTestBeans;
2811+
}
2812+
}
2813+
2814+
27542815
@SuppressWarnings("serial")
27552816
public static class MyTestBeanMap extends LinkedHashMap<String, TestBean> {
27562817
}

0 commit comments

Comments
 (0)