Skip to content

Commit 001d0e7

Browse files
committed
Support for @order at the bean declaration level
This commit introduces OrderProvider and OrderProviderComparator, two interfaces designed to externalize how a collection of element is sorted according to their order value. FactoryAwareOrderProvider is an OrderProvider implementation that knows about the objects to order and the corresponding BeanFactory instance. This allows to retrieve additional metadata about the actual instances to sort, such as its factory method. A @bean method can now holds an additional @order to define the order value that this bean should have when injected as part of a collection or array. Issue: SPR-11310
1 parent 64bb308 commit 001d0e7

File tree

12 files changed

+860
-10
lines changed

12 files changed

+860
-10
lines changed
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
/*
2+
* Copyright 2002-2014 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.springframework.beans.factory.support;
18+
19+
import java.util.Comparator;
20+
21+
import org.springframework.core.annotation.AnnotationAwareOrderComparator;
22+
import org.springframework.core.annotation.DefaultOrderProviderComparator;
23+
24+
/**
25+
* The default {@link Comparator} to use to order dependencies. Extend
26+
* from {@link DefaultOrderProviderComparator} so that the bean factory
27+
* has the ability to provide an {@link org.springframework.core.annotation.OrderProvider}
28+
* that is aware of more bean metadata, if any.
29+
*
30+
* @author Stephane Nicoll
31+
* @since 4.1
32+
* @see org.springframework.core.annotation.OrderProviderComparator
33+
* @see org.springframework.core.annotation.OrderProvider
34+
* @see DefaultListableBeanFactory#setDependencyComparator(java.util.Comparator)
35+
*/
36+
public class DefaultDependencyComparator extends DefaultOrderProviderComparator implements Comparator<Object> {
37+
38+
/**
39+
* Shared default instance of DefaultDependencyComparator.
40+
*/
41+
public static final DefaultDependencyComparator INSTANCE = new DefaultDependencyComparator();
42+
43+
private final Comparator<Object> comparator;
44+
45+
public DefaultDependencyComparator(Comparator<Object> comparator) {
46+
this.comparator = comparator;
47+
}
48+
49+
public DefaultDependencyComparator() {
50+
this(AnnotationAwareOrderComparator.INSTANCE);
51+
}
52+
53+
@Override
54+
public int compare(Object o1, Object o2) {
55+
return comparator.compare(o1, o2);
56+
}
57+
58+
}

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

Lines changed: 30 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@
3232
import java.util.Collections;
3333
import java.util.Comparator;
3434
import java.util.HashMap;
35+
import java.util.IdentityHashMap;
3536
import java.util.LinkedHashMap;
3637
import java.util.List;
3738
import java.util.Map;
@@ -60,6 +61,7 @@
6061
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
6162
import org.springframework.beans.factory.config.DependencyDescriptor;
6263
import org.springframework.core.annotation.AnnotationUtils;
64+
import org.springframework.core.annotation.OrderProviderComparator;
6365
import org.springframework.core.annotation.OrderUtils;
6466
import org.springframework.util.Assert;
6567
import org.springframework.util.ClassUtils;
@@ -895,7 +897,7 @@ public Object doResolveDependency(DependencyDescriptor descriptor, String beanNa
895897
TypeConverter converter = (typeConverter != null ? typeConverter : getTypeConverter());
896898
Object result = converter.convertIfNecessary(matchingBeans.values(), type);
897899
if (this.dependencyComparator != null && result instanceof Object[]) {
898-
Arrays.sort((Object[]) result, this.dependencyComparator);
900+
sortArray((Object[]) result, matchingBeans);
899901
}
900902
return result;
901903
}
@@ -922,7 +924,7 @@ else if (Collection.class.isAssignableFrom(type) && type.isInterface()) {
922924
TypeConverter converter = (typeConverter != null ? typeConverter : getTypeConverter());
923925
Object result = converter.convertIfNecessary(matchingBeans.values(), type);
924926
if (this.dependencyComparator != null && result instanceof List) {
925-
Collections.sort((List<?>) result, this.dependencyComparator);
927+
sortList((List<?>) result, matchingBeans);
926928
}
927929
return result;
928930
}
@@ -983,6 +985,32 @@ else if (Map.class.isAssignableFrom(type) && type.isInterface()) {
983985
}
984986
}
985987

988+
private void sortArray(Object[] items, Map<String, Object> matchingBeans) {
989+
if (this.dependencyComparator instanceof OrderProviderComparator) {
990+
((OrderProviderComparator) this.dependencyComparator)
991+
.sortArray(items, createFactoryAwareOrderProvider(matchingBeans));
992+
} else {
993+
Arrays.sort(items, this.dependencyComparator);
994+
}
995+
}
996+
997+
private void sortList(List<?> items, Map<String, Object> matchingBeans) {
998+
if (this.dependencyComparator instanceof OrderProviderComparator) {
999+
((OrderProviderComparator) this.dependencyComparator)
1000+
.sortList(items, createFactoryAwareOrderProvider(matchingBeans));
1001+
} else {
1002+
Collections.sort(items, this.dependencyComparator);
1003+
}
1004+
}
1005+
1006+
private FactoryAwareOrderProvider createFactoryAwareOrderProvider(Map<String, Object> beans) {
1007+
IdentityHashMap<Object, String> instancesToBeanNames = new IdentityHashMap<Object, String>();
1008+
for (Map.Entry<String, Object> entry : beans.entrySet()) {
1009+
instancesToBeanNames.put(entry.getValue(), entry.getKey());
1010+
}
1011+
return new FactoryAwareOrderProvider(instancesToBeanNames, this);
1012+
}
1013+
9861014
/**
9871015
* Find bean instances that match the required type.
9881016
* Called during autowiring for the specified bean.
Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
/*
2+
* Copyright 2002-2014 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.springframework.beans.factory.support;
18+
19+
import java.lang.reflect.Method;
20+
import java.util.Map;
21+
22+
import org.springframework.beans.factory.config.BeanDefinition;
23+
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
24+
import org.springframework.core.annotation.AnnotationUtils;
25+
import org.springframework.core.annotation.Order;
26+
import org.springframework.core.annotation.OrderProvider;
27+
28+
/**
29+
* An {@link OrderProvider} implementation that is aware of the
30+
* bean metadata of the instances to sort.
31+
*
32+
* <p>Lookup for the method factory of an instance to sort, if
33+
* any and retrieve the {@link Order} value defined on it. This
34+
* essentially allows for the following construct:
35+
*
36+
* <pre class="code">
37+
* &#064;Configuration
38+
* public class AppConfig {
39+
*
40+
* &#064;Bean
41+
* &#064;Order(5)
42+
* public MyService myService() {
43+
* return new MyService();
44+
* }
45+
* }</pre>
46+
*
47+
* @author Stephane Nicoll
48+
* @since 4.1
49+
*/
50+
public class FactoryAwareOrderProvider implements OrderProvider {
51+
52+
private final Map<Object, String> instancesToBeanNames;
53+
54+
private final ConfigurableListableBeanFactory beanFactory;
55+
56+
public FactoryAwareOrderProvider(Map<Object, String> instancesToBeanNames,
57+
ConfigurableListableBeanFactory beanFactory) {
58+
this.instancesToBeanNames = instancesToBeanNames;
59+
this.beanFactory = beanFactory;
60+
}
61+
62+
@Override
63+
public Integer getOrder(Object obj) {
64+
Method factoryMethod = getFactoryMethod(instancesToBeanNames.get(obj));
65+
if (factoryMethod != null) {
66+
Order order = AnnotationUtils.getAnnotation(factoryMethod, Order.class);
67+
if (order != null) {
68+
return order.value();
69+
}
70+
}
71+
return null;
72+
}
73+
74+
private Method getFactoryMethod(String beanName) {
75+
if (beanName != null && beanFactory.containsBeanDefinition(beanName)) {
76+
BeanDefinition bd = beanFactory.getMergedBeanDefinition(beanName);
77+
if (bd instanceof RootBeanDefinition) {
78+
return ((RootBeanDefinition) bd).getResolvedFactoryMethod();
79+
}
80+
}
81+
return null;
82+
}
83+
84+
}

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

Lines changed: 38 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -38,11 +38,11 @@
3838
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
3939
import org.springframework.beans.factory.config.TypedStringValue;
4040
import org.springframework.beans.factory.support.AutowireCandidateQualifier;
41+
import org.springframework.beans.factory.support.DefaultDependencyComparator;
4142
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
4243
import org.springframework.beans.factory.support.GenericBeanDefinition;
4344
import org.springframework.beans.factory.support.RootBeanDefinition;
4445
import org.springframework.core.Ordered;
45-
import org.springframework.core.annotation.AnnotationAwareOrderComparator;
4646
import org.springframework.core.annotation.Order;
4747
import org.springframework.tests.sample.beans.ITestBean;
4848
import org.springframework.tests.sample.beans.IndexedTestBean;
@@ -51,12 +51,15 @@
5151
import org.springframework.util.SerializationTestUtils;
5252

5353
import static org.junit.Assert.*;
54+
import static org.mockito.Matchers.any;
55+
import static org.mockito.Mockito.*;
5456

5557
/**
5658
* @author Juergen Hoeller
5759
* @author Mark Fisher
5860
* @author Sam Brannen
5961
* @author Chris Beams
62+
* @author Stephane Nicoll
6063
*/
6164
public class AutowiredAnnotationBeanPostProcessorTests {
6265

@@ -351,7 +354,7 @@ public void testOptionalResourceInjectionWithNoDependencies() {
351354
@Test
352355
public void testOrderedResourceInjection() {
353356
DefaultListableBeanFactory bf = new DefaultListableBeanFactory();
354-
bf.setDependencyComparator(AnnotationAwareOrderComparator.INSTANCE);
357+
bf.setDependencyComparator(DefaultDependencyComparator.INSTANCE);
355358
AutowiredAnnotationBeanPostProcessor bpp = new AutowiredAnnotationBeanPostProcessor();
356359
bpp.setBeanFactory(bf);
357360
bf.addBeanPostProcessor(bpp);
@@ -382,10 +385,38 @@ public void testOrderedResourceInjection() {
382385
bf.destroySingletons();
383386
}
384387

388+
@Test
389+
public void testOrderedResourceInjectionDetectsFactoryAwareComparator() {
390+
DefaultListableBeanFactory bf = new DefaultListableBeanFactory();
391+
DefaultDependencyComparator comparator = mock(DefaultDependencyComparator.class);
392+
bf.setDependencyComparator(comparator);
393+
394+
AutowiredAnnotationBeanPostProcessor bpp = new AutowiredAnnotationBeanPostProcessor();
395+
bpp.setBeanFactory(bf);
396+
bf.addBeanPostProcessor(bpp);
397+
bf.registerBeanDefinition("annotatedBean", new RootBeanDefinition(OptionalResourceInjectionBean.class));
398+
TestBean tb = new TestBean();
399+
bf.registerSingleton("testBean", tb);
400+
IndexedTestBean itb = new IndexedTestBean();
401+
bf.registerSingleton("indexedTestBean", itb);
402+
final OrderedNestedTestBean ntb1 = new OrderedNestedTestBean();
403+
ntb1.setOrder(2);
404+
bf.registerSingleton("nestedTestBean1", ntb1);
405+
final OrderedNestedTestBean ntb2 = new OrderedNestedTestBean();
406+
ntb2.setOrder(1);
407+
bf.registerSingleton("nestedTestBean2", ntb2);
408+
409+
OptionalResourceInjectionBean bean = (OptionalResourceInjectionBean) bf.getBean("annotatedBean");
410+
verify(comparator, never()).compare(any(), any());
411+
verify(comparator, never()).sortList(any(),any());
412+
verify(comparator, times(2)).sortArray(any(),any());
413+
bf.destroySingletons();
414+
}
415+
385416
@Test
386417
public void testAnnotationOrderedResourceInjection() {
387418
DefaultListableBeanFactory bf = new DefaultListableBeanFactory();
388-
bf.setDependencyComparator(AnnotationAwareOrderComparator.INSTANCE);
419+
bf.setDependencyComparator(DefaultDependencyComparator.INSTANCE);
389420
AutowiredAnnotationBeanPostProcessor bpp = new AutowiredAnnotationBeanPostProcessor();
390421
bpp.setBeanFactory(bf);
391422
bf.addBeanPostProcessor(bpp);
@@ -417,7 +448,7 @@ public void testAnnotationOrderedResourceInjection() {
417448
@Test
418449
public void testOrderedCollectionResourceInjection() {
419450
DefaultListableBeanFactory bf = new DefaultListableBeanFactory();
420-
bf.setDependencyComparator(AnnotationAwareOrderComparator.INSTANCE);
451+
bf.setDependencyComparator(DefaultDependencyComparator.INSTANCE);
421452
AutowiredAnnotationBeanPostProcessor bpp = new AutowiredAnnotationBeanPostProcessor();
422453
bpp.setBeanFactory(bf);
423454
bf.addBeanPostProcessor(bpp);
@@ -458,7 +489,7 @@ public void testOrderedCollectionResourceInjection() {
458489
@Test
459490
public void testAnnotationOrderedCollectionResourceInjection() {
460491
DefaultListableBeanFactory bf = new DefaultListableBeanFactory();
461-
bf.setDependencyComparator(AnnotationAwareOrderComparator.INSTANCE);
492+
bf.setDependencyComparator(DefaultDependencyComparator.INSTANCE);
462493
AutowiredAnnotationBeanPostProcessor bpp = new AutowiredAnnotationBeanPostProcessor();
463494
bpp.setBeanFactory(bf);
464495
bf.addBeanPostProcessor(bpp);
@@ -576,7 +607,7 @@ public void testConstructorResourceInjectionWithMultipleCandidatesAsCollection()
576607
@Test
577608
public void testConstructorResourceInjectionWithMultipleOrderedCandidates() {
578609
DefaultListableBeanFactory bf = new DefaultListableBeanFactory();
579-
bf.setDependencyComparator(AnnotationAwareOrderComparator.INSTANCE);
610+
bf.setDependencyComparator(DefaultDependencyComparator.INSTANCE);
580611
AutowiredAnnotationBeanPostProcessor bpp = new AutowiredAnnotationBeanPostProcessor();
581612
bpp.setBeanFactory(bf);
582613
bf.addBeanPostProcessor(bpp);
@@ -600,7 +631,7 @@ public void testConstructorResourceInjectionWithMultipleOrderedCandidates() {
600631
@Test
601632
public void testConstructorResourceInjectionWithMultipleCandidatesAsOrderedCollection() {
602633
DefaultListableBeanFactory bf = new DefaultListableBeanFactory();
603-
bf.setDependencyComparator(AnnotationAwareOrderComparator.INSTANCE);
634+
bf.setDependencyComparator(DefaultDependencyComparator.INSTANCE);
604635
AutowiredAnnotationBeanPostProcessor bpp = new AutowiredAnnotationBeanPostProcessor();
605636
bpp.setBeanFactory(bf);
606637
bf.addBeanPostProcessor(bpp);
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
/*
2+
* Copyright 2002-2014 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.springframework.beans.factory.support;
18+
19+
import static org.junit.Assert.*;
20+
21+
import java.util.ArrayList;
22+
import java.util.Collections;
23+
import java.util.List;
24+
25+
import org.junit.Test;
26+
27+
import org.springframework.core.Ordered;
28+
29+
/**
30+
*
31+
* @author Stephane Nicoll
32+
*/
33+
public class DependencyComparatorTests {
34+
35+
private final DefaultDependencyComparator comparator = new DefaultDependencyComparator();
36+
37+
@Test
38+
public void plainComparator() {
39+
List<Object> items = new ArrayList<Object>();
40+
C c = new C(5);
41+
C c2 = new C(-5);
42+
items.add(c);
43+
items.add(c2);
44+
Collections.sort(items, comparator);
45+
assertOrder(items, c2, c);
46+
}
47+
48+
private void assertOrder(List<?> actual, Object... expected) {
49+
for (int i = 0; i < actual.size(); i++) {
50+
assertSame("Wrong instance at index '" + i + "'", expected[i], actual.get(i));
51+
}
52+
assertEquals("Wrong number of items", expected.length, actual.size());
53+
}
54+
55+
private static class C implements Ordered {
56+
private final int order;
57+
58+
private C(int order) {
59+
this.order = order;
60+
}
61+
62+
@Override
63+
public int getOrder() {
64+
return order;
65+
}
66+
}
67+
68+
}

0 commit comments

Comments
 (0)