Skip to content

Commit 4447248

Browse files
committed
Introduced support for @lazy on injection points
This turned into a rather huge affair since it led to the introduction of a new AutowireCandidateResolver implementation in the spring-context module. That ACR impl is now being set through AnnotationConfigUtils; GenericApplicationContext and co do not set a default QualifierAnnotationAutowireCandidateResolver anymore (which has always been a smell anyway). At the same time, dependency ordering has moved from AutowiredAnnotationBeanPostProcessor to DefaultListableBeanFactory itself through a "dependencyComparator" strategy, applying to constructor dependencies and lazy resolution proxies as well. Issue: SPR-10353
1 parent 01b8d93 commit 4447248

File tree

16 files changed

+554
-49
lines changed

16 files changed

+554
-49
lines changed

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

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,6 @@
5757
import org.springframework.core.PriorityOrdered;
5858
import org.springframework.core.annotation.AnnotatedElementUtils;
5959
import org.springframework.core.annotation.AnnotationAttributes;
60-
import org.springframework.core.annotation.AnnotationAwareOrderComparator;
6160
import org.springframework.util.Assert;
6261
import org.springframework.util.ClassUtils;
6362
import org.springframework.util.ReflectionUtils;
@@ -436,10 +435,7 @@ private void registerDependentBeans(String beanName, Set<String> autowiredBeanNa
436435
private Object resolvedCachedArgument(String beanName, Object cachedArgument) {
437436
if (cachedArgument instanceof DependencyDescriptor) {
438437
DependencyDescriptor descriptor = (DependencyDescriptor) cachedArgument;
439-
TypeConverter typeConverter = this.beanFactory.getTypeConverter();
440-
Object value = this.beanFactory.resolveDependency(descriptor, beanName, null, typeConverter);
441-
AnnotationAwareOrderComparator.sortIfNecessary(value);
442-
return value;
438+
return this.beanFactory.resolveDependency(descriptor, beanName, null, null);
443439
}
444440
else if (cachedArgument instanceof RuntimeBeanReference) {
445441
return this.beanFactory.getBean(((RuntimeBeanReference) cachedArgument).getBeanName());
@@ -479,7 +475,6 @@ protected void inject(Object bean, String beanName, PropertyValues pvs) throws T
479475
Set<String> autowiredBeanNames = new LinkedHashSet<String>(1);
480476
TypeConverter typeConverter = beanFactory.getTypeConverter();
481477
value = beanFactory.resolveDependency(descriptor, beanName, autowiredBeanNames, typeConverter);
482-
AnnotationAwareOrderComparator.sortIfNecessary(value);
483478
synchronized (this) {
484479
if (!this.cached) {
485480
if (value != null || this.required) {
@@ -557,7 +552,6 @@ protected void inject(Object bean, String beanName, PropertyValues pvs) throws T
557552
arguments = null;
558553
break;
559554
}
560-
AnnotationAwareOrderComparator.sortIfNecessary(arg);
561555
arguments[i] = arg;
562556
}
563557
synchronized (this) {

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

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2012 the original author or authors.
2+
* Copyright 2002-2013 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.
@@ -131,6 +131,10 @@ public void setBeanFactory(BeanFactory beanFactory) {
131131
this.beanFactory = beanFactory;
132132
}
133133

134+
protected final BeanFactory getBeanFactory() {
135+
return this.beanFactory;
136+
}
137+
134138

135139
/**
136140
* Determine whether the provided bean definition is an autowire candidate.
@@ -336,4 +340,14 @@ protected Object extractValue(Annotation valueAnnotation) {
336340
return value;
337341
}
338342

343+
344+
/**
345+
* This implementation always returns {@code null},
346+
* leaving lazy resolution support up to subclasses.
347+
*/
348+
@Override
349+
public Object getLazyResolutionProxyIfNecessary(DependencyDescriptor descriptor, String beanName) {
350+
return null;
351+
}
352+
339353
}

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

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2012 the original author or authors.
2+
* Copyright 2002-2013 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.
@@ -23,8 +23,8 @@
2323
* Strategy interface for determining whether a specific bean definition
2424
* qualifies as an autowire candidate for a specific dependency.
2525
*
26-
* @author Mark Fisher
2726
* @author Juergen Hoeller
27+
* @author Mark Fisher
2828
* @since 2.5
2929
*/
3030
public interface AutowireCandidateResolver {
@@ -47,4 +47,15 @@ public interface AutowireCandidateResolver {
4747
*/
4848
Object getSuggestedValue(DependencyDescriptor descriptor);
4949

50+
/**
51+
* Build a proxy for lazy resolution of the actual dependency target,
52+
* if demanded by the injection point.
53+
* @param descriptor the descriptor for the target method parameter or field
54+
* @param beanName the name of the bean that contains the injection point
55+
* @return the lazy resolution proxy for the actual dependency target,
56+
* or {@code null} if straight resolution is to be performed
57+
* @since 4.0
58+
*/
59+
Object getLazyResolutionProxyIfNecessary(DependencyDescriptor descriptor, String beanName);
60+
5061
}

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

Lines changed: 39 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,8 @@
2929
import java.util.ArrayList;
3030
import java.util.Arrays;
3131
import java.util.Collection;
32+
import java.util.Collections;
33+
import java.util.Comparator;
3234
import java.util.HashMap;
3335
import java.util.LinkedHashMap;
3436
import java.util.List;
@@ -125,6 +127,9 @@ public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFacto
125127
/** Whether to allow eager class loading even for lazy-init beans */
126128
private boolean allowEagerClassLoading = true;
127129

130+
/** Optional OrderComparator for dependency Lists and arrays */
131+
private Comparator dependencyComparator;
132+
128133
/** Resolver to use for checking if a bean definition is an autowire candidate */
129134
private AutowireCandidateResolver autowireCandidateResolver = new SimpleAutowireCandidateResolver();
130135

@@ -205,6 +210,22 @@ public void setAllowEagerClassLoading(boolean allowEagerClassLoading) {
205210
this.allowEagerClassLoading = allowEagerClassLoading;
206211
}
207212

213+
/**
214+
* Set a {@link java.util.Comparator} for dependency Lists and arrays.
215+
* @see org.springframework.core.OrderComparator
216+
* @see org.springframework.core.annotation.AnnotationAwareOrderComparator
217+
*/
218+
public void setDependencyComparator(Comparator dependencyComparator) {
219+
this.dependencyComparator = dependencyComparator;
220+
}
221+
222+
/**
223+
* Return the dependency comparator for this BeanFactory (may be {@code null}.
224+
*/
225+
public Comparator getDependencyComparator() {
226+
return this.dependencyComparator;
227+
}
228+
208229
/**
209230
* Set a custom autowire candidate resolver for this BeanFactory to use
210231
* when deciding whether a bean definition should be considered as a
@@ -786,13 +807,18 @@ else if (descriptor.getDependencyType().equals(javaxInjectProviderClass)) {
786807
return new DependencyProviderFactory().createDependencyProvider(descriptor, beanName);
787808
}
788809
else {
789-
return doResolveDependency(descriptor, descriptor.getDependencyType(), beanName, autowiredBeanNames, typeConverter);
810+
Object result = getAutowireCandidateResolver().getLazyResolutionProxyIfNecessary(descriptor, beanName);
811+
if (result == null) {
812+
result = doResolveDependency(descriptor, beanName, autowiredBeanNames, typeConverter);
813+
}
814+
return result;
790815
}
791816
}
792817

793-
protected Object doResolveDependency(DependencyDescriptor descriptor, Class<?> type, String beanName,
818+
public Object doResolveDependency(DependencyDescriptor descriptor, String beanName,
794819
Set<String> autowiredBeanNames, TypeConverter typeConverter) throws BeansException {
795820

821+
Class<?> type = descriptor.getDependencyType();
796822
Object value = getAutowireCandidateResolver().getSuggestedValue(descriptor);
797823
if (value != null) {
798824
if (value instanceof String) {
@@ -819,7 +845,11 @@ protected Object doResolveDependency(DependencyDescriptor descriptor, Class<?> t
819845
autowiredBeanNames.addAll(matchingBeans.keySet());
820846
}
821847
TypeConverter converter = (typeConverter != null ? typeConverter : getTypeConverter());
822-
return converter.convertIfNecessary(matchingBeans.values(), type);
848+
Object result = converter.convertIfNecessary(matchingBeans.values(), type);
849+
if (this.dependencyComparator != null && result instanceof Object[]) {
850+
Arrays.sort((Object[]) result, this.dependencyComparator);
851+
}
852+
return result;
823853
}
824854
else if (Collection.class.isAssignableFrom(type) && type.isInterface()) {
825855
Class<?> elementType = descriptor.getCollectionType();
@@ -840,7 +870,11 @@ else if (Collection.class.isAssignableFrom(type) && type.isInterface()) {
840870
autowiredBeanNames.addAll(matchingBeans.keySet());
841871
}
842872
TypeConverter converter = (typeConverter != null ? typeConverter : getTypeConverter());
843-
return converter.convertIfNecessary(matchingBeans.values(), type);
873+
Object result = converter.convertIfNecessary(matchingBeans.values(), type);
874+
if (this.dependencyComparator != null && result instanceof List) {
875+
Collections.sort((List) result, this.dependencyComparator);
876+
}
877+
return result;
844878
}
845879
else if (Map.class.isAssignableFrom(type) && type.isInterface()) {
846880
Class<?> keyType = descriptor.getMapKeyType();
@@ -1091,7 +1125,7 @@ public DependencyObjectFactory(DependencyDescriptor descriptor, String beanName)
10911125

10921126
@Override
10931127
public Object getObject() throws BeansException {
1094-
return doResolveDependency(this.descriptor, this.descriptor.getDependencyType(), this.beanName, null, null);
1128+
return doResolveDependency(this.descriptor, this.beanName, null, null);
10951129
}
10961130
}
10971131

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

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
1-
21
/*
3-
* Copyright 2002-2012 the original author or authors.
2+
* Copyright 2002-2013 the original author or authors.
43
*
54
* Licensed under the Apache License, Version 2.0 (the "License");
65
* you may not use this file except in compliance with the License.
@@ -47,4 +46,9 @@ public Object getSuggestedValue(DependencyDescriptor descriptor) {
4746
return null;
4847
}
4948

49+
@Override
50+
public Object getLazyResolutionProxyIfNecessary(DependencyDescriptor descriptor, String beanName) {
51+
return null;
52+
}
53+
5054
}

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

Lines changed: 55 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@
3737
import org.springframework.beans.factory.support.GenericBeanDefinition;
3838
import org.springframework.beans.factory.support.RootBeanDefinition;
3939
import org.springframework.core.Ordered;
40+
import org.springframework.core.annotation.AnnotationAwareOrderComparator;
4041
import org.springframework.core.annotation.Order;
4142
import org.springframework.tests.sample.beans.ITestBean;
4243
import org.springframework.tests.sample.beans.IndexedTestBean;
@@ -46,16 +47,13 @@
4647

4748
import static org.junit.Assert.*;
4849

49-
5050
/**
51-
* Unit tests for {@link AutowiredAnnotationBeanPostProcessor}.
52-
*
5351
* @author Juergen Hoeller
5452
* @author Mark Fisher
5553
* @author Sam Brannen
5654
* @author Chris Beams
5755
*/
58-
public final class AutowiredAnnotationBeanPostProcessorTests {
56+
public class AutowiredAnnotationBeanPostProcessorTests {
5957

6058
@Test
6159
public void testIncompleteBeanDefinition() {
@@ -348,6 +346,7 @@ public void testOptionalResourceInjectionWithNoDependencies() {
348346
@Test
349347
public void testOrderedResourceInjection() {
350348
DefaultListableBeanFactory bf = new DefaultListableBeanFactory();
349+
bf.setDependencyComparator(AnnotationAwareOrderComparator.INSTANCE);
351350
AutowiredAnnotationBeanPostProcessor bpp = new AutowiredAnnotationBeanPostProcessor();
352351
bpp.setBeanFactory(bf);
353352
bf.addBeanPostProcessor(bpp);
@@ -381,6 +380,7 @@ public void testOrderedResourceInjection() {
381380
@Test
382381
public void testAnnotationOrderedResourceInjection() {
383382
DefaultListableBeanFactory bf = new DefaultListableBeanFactory();
383+
bf.setDependencyComparator(AnnotationAwareOrderComparator.INSTANCE);
384384
AutowiredAnnotationBeanPostProcessor bpp = new AutowiredAnnotationBeanPostProcessor();
385385
bpp.setBeanFactory(bf);
386386
bf.addBeanPostProcessor(bpp);
@@ -412,6 +412,7 @@ public void testAnnotationOrderedResourceInjection() {
412412
@Test
413413
public void testOrderedCollectionResourceInjection() {
414414
DefaultListableBeanFactory bf = new DefaultListableBeanFactory();
415+
bf.setDependencyComparator(AnnotationAwareOrderComparator.INSTANCE);
415416
AutowiredAnnotationBeanPostProcessor bpp = new AutowiredAnnotationBeanPostProcessor();
416417
bpp.setBeanFactory(bf);
417418
bf.addBeanPostProcessor(bpp);
@@ -452,6 +453,7 @@ public void testOrderedCollectionResourceInjection() {
452453
@Test
453454
public void testAnnotationOrderedCollectionResourceInjection() {
454455
DefaultListableBeanFactory bf = new DefaultListableBeanFactory();
456+
bf.setDependencyComparator(AnnotationAwareOrderComparator.INSTANCE);
455457
AutowiredAnnotationBeanPostProcessor bpp = new AutowiredAnnotationBeanPostProcessor();
456458
bpp.setBeanFactory(bf);
457459
bf.addBeanPostProcessor(bpp);
@@ -566,6 +568,55 @@ public void testConstructorResourceInjectionWithMultipleCandidatesAsCollection()
566568
bf.destroySingletons();
567569
}
568570

571+
@Test
572+
public void testConstructorResourceInjectionWithMultipleOrderedCandidates() {
573+
DefaultListableBeanFactory bf = new DefaultListableBeanFactory();
574+
bf.setDependencyComparator(AnnotationAwareOrderComparator.INSTANCE);
575+
AutowiredAnnotationBeanPostProcessor bpp = new AutowiredAnnotationBeanPostProcessor();
576+
bpp.setBeanFactory(bf);
577+
bf.addBeanPostProcessor(bpp);
578+
bf.registerBeanDefinition("annotatedBean", new RootBeanDefinition(ConstructorsResourceInjectionBean.class));
579+
TestBean tb = new TestBean();
580+
bf.registerSingleton("testBean", tb);
581+
FixedOrder2NestedTestBean ntb1 = new FixedOrder2NestedTestBean();
582+
bf.registerSingleton("nestedTestBean1", ntb1);
583+
FixedOrder1NestedTestBean ntb2 = new FixedOrder1NestedTestBean();
584+
bf.registerSingleton("nestedTestBean2", ntb2);
585+
586+
ConstructorsResourceInjectionBean bean = (ConstructorsResourceInjectionBean) bf.getBean("annotatedBean");
587+
assertNull(bean.getTestBean3());
588+
assertSame(tb, bean.getTestBean4());
589+
assertEquals(2, bean.getNestedTestBeans().length);
590+
assertSame(ntb2, bean.getNestedTestBeans()[0]);
591+
assertSame(ntb1, bean.getNestedTestBeans()[1]);
592+
bf.destroySingletons();
593+
}
594+
595+
@Test
596+
public void testConstructorResourceInjectionWithMultipleCandidatesAsOrderedCollection() {
597+
DefaultListableBeanFactory bf = new DefaultListableBeanFactory();
598+
bf.setDependencyComparator(AnnotationAwareOrderComparator.INSTANCE);
599+
AutowiredAnnotationBeanPostProcessor bpp = new AutowiredAnnotationBeanPostProcessor();
600+
bpp.setBeanFactory(bf);
601+
bf.addBeanPostProcessor(bpp);
602+
bf.registerBeanDefinition("annotatedBean", new RootBeanDefinition(
603+
ConstructorsCollectionResourceInjectionBean.class));
604+
TestBean tb = new TestBean();
605+
bf.registerSingleton("testBean", tb);
606+
FixedOrder2NestedTestBean ntb1 = new FixedOrder2NestedTestBean();
607+
bf.registerSingleton("nestedTestBean1", ntb1);
608+
FixedOrder1NestedTestBean ntb2 = new FixedOrder1NestedTestBean();
609+
bf.registerSingleton("nestedTestBean2", ntb2);
610+
611+
ConstructorsCollectionResourceInjectionBean bean = (ConstructorsCollectionResourceInjectionBean) bf.getBean("annotatedBean");
612+
assertNull(bean.getTestBean3());
613+
assertSame(tb, bean.getTestBean4());
614+
assertEquals(2, bean.getNestedTestBeans().size());
615+
assertSame(ntb2, bean.getNestedTestBeans().get(0));
616+
assertSame(ntb1, bean.getNestedTestBeans().get(1));
617+
bf.destroySingletons();
618+
}
619+
569620
@Test
570621
public void testConstructorResourceInjectionWithMultipleCandidatesAndFallback() {
571622
DefaultListableBeanFactory bf = new DefaultListableBeanFactory();
@@ -1102,7 +1153,6 @@ public static class ResourceInjectionBean {
11021153

11031154
private TestBean testBean2;
11041155

1105-
11061156
@Autowired
11071157
public void setTestBean2(TestBean testBean2) {
11081158
if (this.testBean2 != null) {

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

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,10 +16,8 @@
1616

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

19-
import static org.junit.Assert.assertEquals;
20-
import static org.springframework.tests.TestResourceUtils.qualifiedResource;
21-
2219
import org.junit.Test;
20+
2321
import org.springframework.beans.factory.config.BeanDefinitionHolder;
2422
import org.springframework.beans.factory.config.DependencyDescriptor;
2523
import org.springframework.beans.factory.support.AutowireCandidateResolver;
@@ -28,6 +26,9 @@
2826
import org.springframework.beans.factory.xml.XmlBeanDefinitionReader;
2927
import org.springframework.core.io.Resource;
3028

29+
import static org.junit.Assert.*;
30+
import static org.springframework.tests.TestResourceUtils.*;
31+
3132
/**
3233
* Unit tests for {@link CustomAutowireConfigurer}.
3334
*
@@ -87,6 +88,11 @@ public boolean isAutowireCandidate(BeanDefinitionHolder bdHolder, DependencyDesc
8788
public Object getSuggestedValue(DependencyDescriptor descriptor) {
8889
return null;
8990
}
91+
92+
@Override
93+
public Object getLazyResolutionProxyIfNecessary(DependencyDescriptor descriptor, String beanName) {
94+
return null;
95+
}
9096
}
9197

9298
}

0 commit comments

Comments
 (0)