Skip to content

Commit 4a9af23

Browse files
committed
@Autowired/@Inject with array/List value gets sorted against Ordered/@order
Issue: SPR-5574
1 parent 486a56d commit 4a9af23

File tree

4 files changed

+215
-12
lines changed

4 files changed

+215
-12
lines changed

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

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@
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;
6061
import org.springframework.util.Assert;
6162
import org.springframework.util.ClassUtils;
6263
import org.springframework.util.ReflectionUtils;
@@ -436,7 +437,9 @@ private Object resolvedCachedArgument(String beanName, Object cachedArgument) {
436437
if (cachedArgument instanceof DependencyDescriptor) {
437438
DependencyDescriptor descriptor = (DependencyDescriptor) cachedArgument;
438439
TypeConverter typeConverter = this.beanFactory.getTypeConverter();
439-
return this.beanFactory.resolveDependency(descriptor, beanName, null, typeConverter);
440+
Object value = this.beanFactory.resolveDependency(descriptor, beanName, null, typeConverter);
441+
AnnotationAwareOrderComparator.sortIfNecessary(value);
442+
return value;
440443
}
441444
else if (cachedArgument instanceof RuntimeBeanReference) {
442445
return this.beanFactory.getBean(((RuntimeBeanReference) cachedArgument).getBeanName());
@@ -476,6 +479,7 @@ protected void inject(Object bean, String beanName, PropertyValues pvs) throws T
476479
Set<String> autowiredBeanNames = new LinkedHashSet<String>(1);
477480
TypeConverter typeConverter = beanFactory.getTypeConverter();
478481
value = beanFactory.resolveDependency(descriptor, beanName, autowiredBeanNames, typeConverter);
482+
AnnotationAwareOrderComparator.sortIfNecessary(value);
479483
synchronized (this) {
480484
if (!this.cached) {
481485
if (value != null || this.required) {
@@ -547,12 +551,14 @@ protected void inject(Object bean, String beanName, PropertyValues pvs) throws T
547551
MethodParameter methodParam = new MethodParameter(method, i);
548552
GenericTypeResolver.resolveParameterType(methodParam, bean.getClass());
549553
descriptors[i] = new DependencyDescriptor(methodParam, this.required);
550-
arguments[i] = beanFactory.resolveDependency(
554+
Object arg = beanFactory.resolveDependency(
551555
descriptors[i], beanName, autowiredBeanNames, typeConverter);
552-
if (arguments[i] == null && !this.required) {
556+
if (arg == null && !this.required) {
553557
arguments = null;
554558
break;
555559
}
560+
AnnotationAwareOrderComparator.sortIfNecessary(arg);
561+
arguments[i] = arg;
556562
}
557563
synchronized (this) {
558564
if (!this.cached) {

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

Lines changed: 171 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -16,14 +16,6 @@
1616

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

19-
import static org.junit.Assert.assertEquals;
20-
import static org.junit.Assert.assertNotNull;
21-
import static org.junit.Assert.assertNotSame;
22-
import static org.junit.Assert.assertNull;
23-
import static org.junit.Assert.assertSame;
24-
import static org.junit.Assert.assertTrue;
25-
import static org.junit.Assert.fail;
26-
2719
import java.io.Serializable;
2820
import java.lang.annotation.ElementType;
2921
import java.lang.annotation.Retention;
@@ -33,6 +25,7 @@
3325
import java.util.Map;
3426

3527
import org.junit.Test;
28+
3629
import org.springframework.beans.factory.BeanCreationException;
3730
import org.springframework.beans.factory.BeanFactory;
3831
import org.springframework.beans.factory.FactoryBean;
@@ -43,12 +36,16 @@
4336
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
4437
import org.springframework.beans.factory.support.GenericBeanDefinition;
4538
import org.springframework.beans.factory.support.RootBeanDefinition;
39+
import org.springframework.core.Ordered;
40+
import org.springframework.core.annotation.Order;
4641
import org.springframework.tests.sample.beans.ITestBean;
4742
import org.springframework.tests.sample.beans.IndexedTestBean;
4843
import org.springframework.tests.sample.beans.NestedTestBean;
4944
import org.springframework.tests.sample.beans.TestBean;
5045
import org.springframework.util.SerializationTestUtils;
5146

47+
import static org.junit.Assert.*;
48+
5249

5350
/**
5451
* Unit tests for {@link AutowiredAnnotationBeanPostProcessor}.
@@ -348,6 +345,148 @@ public void testOptionalResourceInjectionWithNoDependencies() {
348345
bf.destroySingletons();
349346
}
350347

348+
@Test
349+
public void testOrderedResourceInjection() {
350+
DefaultListableBeanFactory bf = new DefaultListableBeanFactory();
351+
AutowiredAnnotationBeanPostProcessor bpp = new AutowiredAnnotationBeanPostProcessor();
352+
bpp.setBeanFactory(bf);
353+
bf.addBeanPostProcessor(bpp);
354+
bf.registerBeanDefinition("annotatedBean", new RootBeanDefinition(OptionalResourceInjectionBean.class));
355+
TestBean tb = new TestBean();
356+
bf.registerSingleton("testBean", tb);
357+
IndexedTestBean itb = new IndexedTestBean();
358+
bf.registerSingleton("indexedTestBean", itb);
359+
OrderedNestedTestBean ntb1 = new OrderedNestedTestBean();
360+
ntb1.setOrder(2);
361+
bf.registerSingleton("nestedTestBean1", ntb1);
362+
OrderedNestedTestBean ntb2 = new OrderedNestedTestBean();
363+
ntb2.setOrder(1);
364+
bf.registerSingleton("nestedTestBean2", ntb2);
365+
366+
OptionalResourceInjectionBean bean = (OptionalResourceInjectionBean) bf.getBean("annotatedBean");
367+
assertSame(tb, bean.getTestBean());
368+
assertSame(tb, bean.getTestBean2());
369+
assertSame(tb, bean.getTestBean3());
370+
assertSame(tb, bean.getTestBean4());
371+
assertSame(itb, bean.getIndexedTestBean());
372+
assertEquals(2, bean.getNestedTestBeans().length);
373+
assertSame(ntb2, bean.getNestedTestBeans()[0]);
374+
assertSame(ntb1, bean.getNestedTestBeans()[1]);
375+
assertEquals(2, bean.nestedTestBeansField.length);
376+
assertSame(ntb2, bean.nestedTestBeansField[0]);
377+
assertSame(ntb1, bean.nestedTestBeansField[1]);
378+
bf.destroySingletons();
379+
}
380+
381+
@Test
382+
public void testAnnotationOrderedResourceInjection() {
383+
DefaultListableBeanFactory bf = new DefaultListableBeanFactory();
384+
AutowiredAnnotationBeanPostProcessor bpp = new AutowiredAnnotationBeanPostProcessor();
385+
bpp.setBeanFactory(bf);
386+
bf.addBeanPostProcessor(bpp);
387+
bf.registerBeanDefinition("annotatedBean", new RootBeanDefinition(OptionalResourceInjectionBean.class));
388+
TestBean tb = new TestBean();
389+
bf.registerSingleton("testBean", tb);
390+
IndexedTestBean itb = new IndexedTestBean();
391+
bf.registerSingleton("indexedTestBean", itb);
392+
FixedOrder2NestedTestBean ntb1 = new FixedOrder2NestedTestBean();
393+
bf.registerSingleton("nestedTestBean1", ntb1);
394+
FixedOrder1NestedTestBean ntb2 = new FixedOrder1NestedTestBean();
395+
bf.registerSingleton("nestedTestBean2", ntb2);
396+
397+
OptionalResourceInjectionBean bean = (OptionalResourceInjectionBean) bf.getBean("annotatedBean");
398+
assertSame(tb, bean.getTestBean());
399+
assertSame(tb, bean.getTestBean2());
400+
assertSame(tb, bean.getTestBean3());
401+
assertSame(tb, bean.getTestBean4());
402+
assertSame(itb, bean.getIndexedTestBean());
403+
assertEquals(2, bean.getNestedTestBeans().length);
404+
assertSame(ntb2, bean.getNestedTestBeans()[0]);
405+
assertSame(ntb1, bean.getNestedTestBeans()[1]);
406+
assertEquals(2, bean.nestedTestBeansField.length);
407+
assertSame(ntb2, bean.nestedTestBeansField[0]);
408+
assertSame(ntb1, bean.nestedTestBeansField[1]);
409+
bf.destroySingletons();
410+
}
411+
412+
@Test
413+
public void testOrderedCollectionResourceInjection() {
414+
DefaultListableBeanFactory bf = new DefaultListableBeanFactory();
415+
AutowiredAnnotationBeanPostProcessor bpp = new AutowiredAnnotationBeanPostProcessor();
416+
bpp.setBeanFactory(bf);
417+
bf.addBeanPostProcessor(bpp);
418+
RootBeanDefinition rbd = new RootBeanDefinition(OptionalCollectionResourceInjectionBean.class);
419+
rbd.setScope(RootBeanDefinition.SCOPE_PROTOTYPE);
420+
bf.registerBeanDefinition("annotatedBean", rbd);
421+
TestBean tb = new TestBean();
422+
bf.registerSingleton("testBean", tb);
423+
IndexedTestBean itb = new IndexedTestBean();
424+
bf.registerSingleton("indexedTestBean", itb);
425+
OrderedNestedTestBean ntb1 = new OrderedNestedTestBean();
426+
ntb1.setOrder(2);
427+
bf.registerSingleton("nestedTestBean1", ntb1);
428+
OrderedNestedTestBean ntb2 = new OrderedNestedTestBean();
429+
ntb2.setOrder(1);
430+
bf.registerSingleton("nestedTestBean2", ntb2);
431+
432+
// Two calls to verify that caching doesn't break re-creation.
433+
OptionalCollectionResourceInjectionBean bean = (OptionalCollectionResourceInjectionBean) bf.getBean("annotatedBean");
434+
bean = (OptionalCollectionResourceInjectionBean) bf.getBean("annotatedBean");
435+
assertSame(tb, bean.getTestBean());
436+
assertSame(tb, bean.getTestBean2());
437+
assertSame(tb, bean.getTestBean3());
438+
assertSame(tb, bean.getTestBean4());
439+
assertSame(itb, bean.getIndexedTestBean());
440+
assertEquals(2, bean.getNestedTestBeans().size());
441+
assertSame(ntb2, bean.getNestedTestBeans().get(0));
442+
assertSame(ntb1, bean.getNestedTestBeans().get(1));
443+
assertEquals(2, bean.nestedTestBeansSetter.size());
444+
assertSame(ntb2, bean.nestedTestBeansSetter.get(0));
445+
assertSame(ntb1, bean.nestedTestBeansSetter.get(1));
446+
assertEquals(2, bean.nestedTestBeansField.size());
447+
assertSame(ntb2, bean.nestedTestBeansField.get(0));
448+
assertSame(ntb1, bean.nestedTestBeansField.get(1));
449+
bf.destroySingletons();
450+
}
451+
452+
@Test
453+
public void testAnnotationOrderedCollectionResourceInjection() {
454+
DefaultListableBeanFactory bf = new DefaultListableBeanFactory();
455+
AutowiredAnnotationBeanPostProcessor bpp = new AutowiredAnnotationBeanPostProcessor();
456+
bpp.setBeanFactory(bf);
457+
bf.addBeanPostProcessor(bpp);
458+
RootBeanDefinition rbd = new RootBeanDefinition(OptionalCollectionResourceInjectionBean.class);
459+
rbd.setScope(RootBeanDefinition.SCOPE_PROTOTYPE);
460+
bf.registerBeanDefinition("annotatedBean", rbd);
461+
TestBean tb = new TestBean();
462+
bf.registerSingleton("testBean", tb);
463+
IndexedTestBean itb = new IndexedTestBean();
464+
bf.registerSingleton("indexedTestBean", itb);
465+
FixedOrder2NestedTestBean ntb1 = new FixedOrder2NestedTestBean();
466+
bf.registerSingleton("nestedTestBean1", ntb1);
467+
FixedOrder1NestedTestBean ntb2 = new FixedOrder1NestedTestBean();
468+
bf.registerSingleton("nestedTestBean2", ntb2);
469+
470+
// Two calls to verify that caching doesn't break re-creation.
471+
OptionalCollectionResourceInjectionBean bean = (OptionalCollectionResourceInjectionBean) bf.getBean("annotatedBean");
472+
bean = (OptionalCollectionResourceInjectionBean) bf.getBean("annotatedBean");
473+
assertSame(tb, bean.getTestBean());
474+
assertSame(tb, bean.getTestBean2());
475+
assertSame(tb, bean.getTestBean3());
476+
assertSame(tb, bean.getTestBean4());
477+
assertSame(itb, bean.getIndexedTestBean());
478+
assertEquals(2, bean.getNestedTestBeans().size());
479+
assertSame(ntb2, bean.getNestedTestBeans().get(0));
480+
assertSame(ntb1, bean.getNestedTestBeans().get(1));
481+
assertEquals(2, bean.nestedTestBeansSetter.size());
482+
assertSame(ntb2, bean.nestedTestBeansSetter.get(0));
483+
assertSame(ntb1, bean.nestedTestBeansSetter.get(1));
484+
assertEquals(2, bean.nestedTestBeansField.size());
485+
assertSame(ntb2, bean.nestedTestBeansField.get(0));
486+
assertSame(ntb1, bean.nestedTestBeansField.get(1));
487+
bf.destroySingletons();
488+
}
489+
351490
@Test
352491
public void testConstructorResourceInjection() {
353492
DefaultListableBeanFactory bf = new DefaultListableBeanFactory();
@@ -1476,4 +1615,28 @@ public boolean isSingleton() {
14761615
}
14771616
}
14781617

1618+
1619+
public static class OrderedNestedTestBean extends NestedTestBean implements Ordered {
1620+
1621+
private int order;
1622+
1623+
public void setOrder(int order) {
1624+
this.order = order;
1625+
}
1626+
1627+
@Override
1628+
public int getOrder() {
1629+
return this.order;
1630+
}
1631+
}
1632+
1633+
1634+
@Order(1)
1635+
public static class FixedOrder1NestedTestBean extends NestedTestBean {
1636+
}
1637+
1638+
@Order(2)
1639+
public static class FixedOrder2NestedTestBean extends NestedTestBean {
1640+
}
1641+
14791642
}

spring-core/src/main/java/org/springframework/core/OrderComparator.java

Lines changed: 18 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.
@@ -98,4 +98,21 @@ public static void sort(Object[] array) {
9898
}
9999
}
100100

101+
/**
102+
* Sort the given array or List with a default OrderComparator,
103+
* if necessary. Simply skips sorting when given any other value.
104+
* <p>Optimized to skip sorting for lists with size 0 or 1,
105+
* in order to avoid unnecessary array extraction.
106+
* @param value the array or List to sort
107+
* @see java.util.Arrays#sort(Object[], java.util.Comparator)
108+
*/
109+
public static void sortIfNecessary(Object value) {
110+
if (value instanceof Object[]) {
111+
sort((Object[]) value);
112+
}
113+
else if (value instanceof List) {
114+
sort((List) value);
115+
}
116+
}
117+
101118
}

spring-core/src/main/java/org/springframework/core/annotation/AnnotationAwareOrderComparator.java

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,4 +86,21 @@ public static void sort(Object[] array) {
8686
}
8787
}
8888

89+
/**
90+
* Sort the given array or List with a default AnnotationAwareOrderComparator,
91+
* if necessary. Simply skips sorting when given any other value.
92+
* <p>Optimized to skip sorting for lists with size 0 or 1,
93+
* in order to avoid unnecessary array extraction.
94+
* @param value the array or List to sort
95+
* @see java.util.Arrays#sort(Object[], java.util.Comparator)
96+
*/
97+
public static void sortIfNecessary(Object value) {
98+
if (value instanceof Object[]) {
99+
sort((Object[]) value);
100+
}
101+
else if (value instanceof List) {
102+
sort((List) value);
103+
}
104+
}
105+
89106
}

0 commit comments

Comments
 (0)