@Autowired/@Inject with array/List value gets sorted against Ordered/@Order
Issue: SPR-5574
This commit is contained in:
parent
486a56dc1f
commit
4a9af233ae
|
|
@ -57,6 +57,7 @@ import org.springframework.core.Ordered;
|
|||
import org.springframework.core.PriorityOrdered;
|
||||
import org.springframework.core.annotation.AnnotatedElementUtils;
|
||||
import org.springframework.core.annotation.AnnotationAttributes;
|
||||
import org.springframework.core.annotation.AnnotationAwareOrderComparator;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.ClassUtils;
|
||||
import org.springframework.util.ReflectionUtils;
|
||||
|
|
@ -436,7 +437,9 @@ public class AutowiredAnnotationBeanPostProcessor extends InstantiationAwareBean
|
|||
if (cachedArgument instanceof DependencyDescriptor) {
|
||||
DependencyDescriptor descriptor = (DependencyDescriptor) cachedArgument;
|
||||
TypeConverter typeConverter = this.beanFactory.getTypeConverter();
|
||||
return this.beanFactory.resolveDependency(descriptor, beanName, null, typeConverter);
|
||||
Object value = this.beanFactory.resolveDependency(descriptor, beanName, null, typeConverter);
|
||||
AnnotationAwareOrderComparator.sortIfNecessary(value);
|
||||
return value;
|
||||
}
|
||||
else if (cachedArgument instanceof RuntimeBeanReference) {
|
||||
return this.beanFactory.getBean(((RuntimeBeanReference) cachedArgument).getBeanName());
|
||||
|
|
@ -476,6 +479,7 @@ public class AutowiredAnnotationBeanPostProcessor extends InstantiationAwareBean
|
|||
Set<String> autowiredBeanNames = new LinkedHashSet<String>(1);
|
||||
TypeConverter typeConverter = beanFactory.getTypeConverter();
|
||||
value = beanFactory.resolveDependency(descriptor, beanName, autowiredBeanNames, typeConverter);
|
||||
AnnotationAwareOrderComparator.sortIfNecessary(value);
|
||||
synchronized (this) {
|
||||
if (!this.cached) {
|
||||
if (value != null || this.required) {
|
||||
|
|
@ -547,12 +551,14 @@ public class AutowiredAnnotationBeanPostProcessor extends InstantiationAwareBean
|
|||
MethodParameter methodParam = new MethodParameter(method, i);
|
||||
GenericTypeResolver.resolveParameterType(methodParam, bean.getClass());
|
||||
descriptors[i] = new DependencyDescriptor(methodParam, this.required);
|
||||
arguments[i] = beanFactory.resolveDependency(
|
||||
Object arg = beanFactory.resolveDependency(
|
||||
descriptors[i], beanName, autowiredBeanNames, typeConverter);
|
||||
if (arguments[i] == null && !this.required) {
|
||||
if (arg == null && !this.required) {
|
||||
arguments = null;
|
||||
break;
|
||||
}
|
||||
AnnotationAwareOrderComparator.sortIfNecessary(arg);
|
||||
arguments[i] = arg;
|
||||
}
|
||||
synchronized (this) {
|
||||
if (!this.cached) {
|
||||
|
|
|
|||
|
|
@ -16,14 +16,6 @@
|
|||
|
||||
package org.springframework.beans.factory.annotation;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.junit.Assert.assertNotSame;
|
||||
import static org.junit.Assert.assertNull;
|
||||
import static org.junit.Assert.assertSame;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.junit.Assert.fail;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
|
|
@ -33,6 +25,7 @@ import java.util.List;
|
|||
import java.util.Map;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import org.springframework.beans.factory.BeanCreationException;
|
||||
import org.springframework.beans.factory.BeanFactory;
|
||||
import org.springframework.beans.factory.FactoryBean;
|
||||
|
|
@ -43,12 +36,16 @@ import org.springframework.beans.factory.support.AutowireCandidateQualifier;
|
|||
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
|
||||
import org.springframework.beans.factory.support.GenericBeanDefinition;
|
||||
import org.springframework.beans.factory.support.RootBeanDefinition;
|
||||
import org.springframework.core.Ordered;
|
||||
import org.springframework.core.annotation.Order;
|
||||
import org.springframework.tests.sample.beans.ITestBean;
|
||||
import org.springframework.tests.sample.beans.IndexedTestBean;
|
||||
import org.springframework.tests.sample.beans.NestedTestBean;
|
||||
import org.springframework.tests.sample.beans.TestBean;
|
||||
import org.springframework.util.SerializationTestUtils;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
|
||||
/**
|
||||
* Unit tests for {@link AutowiredAnnotationBeanPostProcessor}.
|
||||
|
|
@ -348,6 +345,148 @@ public final class AutowiredAnnotationBeanPostProcessorTests {
|
|||
bf.destroySingletons();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testOrderedResourceInjection() {
|
||||
DefaultListableBeanFactory bf = new DefaultListableBeanFactory();
|
||||
AutowiredAnnotationBeanPostProcessor bpp = new AutowiredAnnotationBeanPostProcessor();
|
||||
bpp.setBeanFactory(bf);
|
||||
bf.addBeanPostProcessor(bpp);
|
||||
bf.registerBeanDefinition("annotatedBean", new RootBeanDefinition(OptionalResourceInjectionBean.class));
|
||||
TestBean tb = new TestBean();
|
||||
bf.registerSingleton("testBean", tb);
|
||||
IndexedTestBean itb = new IndexedTestBean();
|
||||
bf.registerSingleton("indexedTestBean", itb);
|
||||
OrderedNestedTestBean ntb1 = new OrderedNestedTestBean();
|
||||
ntb1.setOrder(2);
|
||||
bf.registerSingleton("nestedTestBean1", ntb1);
|
||||
OrderedNestedTestBean ntb2 = new OrderedNestedTestBean();
|
||||
ntb2.setOrder(1);
|
||||
bf.registerSingleton("nestedTestBean2", ntb2);
|
||||
|
||||
OptionalResourceInjectionBean bean = (OptionalResourceInjectionBean) bf.getBean("annotatedBean");
|
||||
assertSame(tb, bean.getTestBean());
|
||||
assertSame(tb, bean.getTestBean2());
|
||||
assertSame(tb, bean.getTestBean3());
|
||||
assertSame(tb, bean.getTestBean4());
|
||||
assertSame(itb, bean.getIndexedTestBean());
|
||||
assertEquals(2, bean.getNestedTestBeans().length);
|
||||
assertSame(ntb2, bean.getNestedTestBeans()[0]);
|
||||
assertSame(ntb1, bean.getNestedTestBeans()[1]);
|
||||
assertEquals(2, bean.nestedTestBeansField.length);
|
||||
assertSame(ntb2, bean.nestedTestBeansField[0]);
|
||||
assertSame(ntb1, bean.nestedTestBeansField[1]);
|
||||
bf.destroySingletons();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAnnotationOrderedResourceInjection() {
|
||||
DefaultListableBeanFactory bf = new DefaultListableBeanFactory();
|
||||
AutowiredAnnotationBeanPostProcessor bpp = new AutowiredAnnotationBeanPostProcessor();
|
||||
bpp.setBeanFactory(bf);
|
||||
bf.addBeanPostProcessor(bpp);
|
||||
bf.registerBeanDefinition("annotatedBean", new RootBeanDefinition(OptionalResourceInjectionBean.class));
|
||||
TestBean tb = new TestBean();
|
||||
bf.registerSingleton("testBean", tb);
|
||||
IndexedTestBean itb = new IndexedTestBean();
|
||||
bf.registerSingleton("indexedTestBean", itb);
|
||||
FixedOrder2NestedTestBean ntb1 = new FixedOrder2NestedTestBean();
|
||||
bf.registerSingleton("nestedTestBean1", ntb1);
|
||||
FixedOrder1NestedTestBean ntb2 = new FixedOrder1NestedTestBean();
|
||||
bf.registerSingleton("nestedTestBean2", ntb2);
|
||||
|
||||
OptionalResourceInjectionBean bean = (OptionalResourceInjectionBean) bf.getBean("annotatedBean");
|
||||
assertSame(tb, bean.getTestBean());
|
||||
assertSame(tb, bean.getTestBean2());
|
||||
assertSame(tb, bean.getTestBean3());
|
||||
assertSame(tb, bean.getTestBean4());
|
||||
assertSame(itb, bean.getIndexedTestBean());
|
||||
assertEquals(2, bean.getNestedTestBeans().length);
|
||||
assertSame(ntb2, bean.getNestedTestBeans()[0]);
|
||||
assertSame(ntb1, bean.getNestedTestBeans()[1]);
|
||||
assertEquals(2, bean.nestedTestBeansField.length);
|
||||
assertSame(ntb2, bean.nestedTestBeansField[0]);
|
||||
assertSame(ntb1, bean.nestedTestBeansField[1]);
|
||||
bf.destroySingletons();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testOrderedCollectionResourceInjection() {
|
||||
DefaultListableBeanFactory bf = new DefaultListableBeanFactory();
|
||||
AutowiredAnnotationBeanPostProcessor bpp = new AutowiredAnnotationBeanPostProcessor();
|
||||
bpp.setBeanFactory(bf);
|
||||
bf.addBeanPostProcessor(bpp);
|
||||
RootBeanDefinition rbd = new RootBeanDefinition(OptionalCollectionResourceInjectionBean.class);
|
||||
rbd.setScope(RootBeanDefinition.SCOPE_PROTOTYPE);
|
||||
bf.registerBeanDefinition("annotatedBean", rbd);
|
||||
TestBean tb = new TestBean();
|
||||
bf.registerSingleton("testBean", tb);
|
||||
IndexedTestBean itb = new IndexedTestBean();
|
||||
bf.registerSingleton("indexedTestBean", itb);
|
||||
OrderedNestedTestBean ntb1 = new OrderedNestedTestBean();
|
||||
ntb1.setOrder(2);
|
||||
bf.registerSingleton("nestedTestBean1", ntb1);
|
||||
OrderedNestedTestBean ntb2 = new OrderedNestedTestBean();
|
||||
ntb2.setOrder(1);
|
||||
bf.registerSingleton("nestedTestBean2", ntb2);
|
||||
|
||||
// Two calls to verify that caching doesn't break re-creation.
|
||||
OptionalCollectionResourceInjectionBean bean = (OptionalCollectionResourceInjectionBean) bf.getBean("annotatedBean");
|
||||
bean = (OptionalCollectionResourceInjectionBean) bf.getBean("annotatedBean");
|
||||
assertSame(tb, bean.getTestBean());
|
||||
assertSame(tb, bean.getTestBean2());
|
||||
assertSame(tb, bean.getTestBean3());
|
||||
assertSame(tb, bean.getTestBean4());
|
||||
assertSame(itb, bean.getIndexedTestBean());
|
||||
assertEquals(2, bean.getNestedTestBeans().size());
|
||||
assertSame(ntb2, bean.getNestedTestBeans().get(0));
|
||||
assertSame(ntb1, bean.getNestedTestBeans().get(1));
|
||||
assertEquals(2, bean.nestedTestBeansSetter.size());
|
||||
assertSame(ntb2, bean.nestedTestBeansSetter.get(0));
|
||||
assertSame(ntb1, bean.nestedTestBeansSetter.get(1));
|
||||
assertEquals(2, bean.nestedTestBeansField.size());
|
||||
assertSame(ntb2, bean.nestedTestBeansField.get(0));
|
||||
assertSame(ntb1, bean.nestedTestBeansField.get(1));
|
||||
bf.destroySingletons();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAnnotationOrderedCollectionResourceInjection() {
|
||||
DefaultListableBeanFactory bf = new DefaultListableBeanFactory();
|
||||
AutowiredAnnotationBeanPostProcessor bpp = new AutowiredAnnotationBeanPostProcessor();
|
||||
bpp.setBeanFactory(bf);
|
||||
bf.addBeanPostProcessor(bpp);
|
||||
RootBeanDefinition rbd = new RootBeanDefinition(OptionalCollectionResourceInjectionBean.class);
|
||||
rbd.setScope(RootBeanDefinition.SCOPE_PROTOTYPE);
|
||||
bf.registerBeanDefinition("annotatedBean", rbd);
|
||||
TestBean tb = new TestBean();
|
||||
bf.registerSingleton("testBean", tb);
|
||||
IndexedTestBean itb = new IndexedTestBean();
|
||||
bf.registerSingleton("indexedTestBean", itb);
|
||||
FixedOrder2NestedTestBean ntb1 = new FixedOrder2NestedTestBean();
|
||||
bf.registerSingleton("nestedTestBean1", ntb1);
|
||||
FixedOrder1NestedTestBean ntb2 = new FixedOrder1NestedTestBean();
|
||||
bf.registerSingleton("nestedTestBean2", ntb2);
|
||||
|
||||
// Two calls to verify that caching doesn't break re-creation.
|
||||
OptionalCollectionResourceInjectionBean bean = (OptionalCollectionResourceInjectionBean) bf.getBean("annotatedBean");
|
||||
bean = (OptionalCollectionResourceInjectionBean) bf.getBean("annotatedBean");
|
||||
assertSame(tb, bean.getTestBean());
|
||||
assertSame(tb, bean.getTestBean2());
|
||||
assertSame(tb, bean.getTestBean3());
|
||||
assertSame(tb, bean.getTestBean4());
|
||||
assertSame(itb, bean.getIndexedTestBean());
|
||||
assertEquals(2, bean.getNestedTestBeans().size());
|
||||
assertSame(ntb2, bean.getNestedTestBeans().get(0));
|
||||
assertSame(ntb1, bean.getNestedTestBeans().get(1));
|
||||
assertEquals(2, bean.nestedTestBeansSetter.size());
|
||||
assertSame(ntb2, bean.nestedTestBeansSetter.get(0));
|
||||
assertSame(ntb1, bean.nestedTestBeansSetter.get(1));
|
||||
assertEquals(2, bean.nestedTestBeansField.size());
|
||||
assertSame(ntb2, bean.nestedTestBeansField.get(0));
|
||||
assertSame(ntb1, bean.nestedTestBeansField.get(1));
|
||||
bf.destroySingletons();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testConstructorResourceInjection() {
|
||||
DefaultListableBeanFactory bf = new DefaultListableBeanFactory();
|
||||
|
|
@ -1476,4 +1615,28 @@ public final class AutowiredAnnotationBeanPostProcessorTests {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
public static class OrderedNestedTestBean extends NestedTestBean implements Ordered {
|
||||
|
||||
private int order;
|
||||
|
||||
public void setOrder(int order) {
|
||||
this.order = order;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getOrder() {
|
||||
return this.order;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Order(1)
|
||||
public static class FixedOrder1NestedTestBean extends NestedTestBean {
|
||||
}
|
||||
|
||||
@Order(2)
|
||||
public static class FixedOrder2NestedTestBean extends NestedTestBean {
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright 2002-2012 the original author or authors.
|
||||
* Copyright 2002-2013 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
|
|
@ -98,4 +98,21 @@ public class OrderComparator implements Comparator<Object> {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sort the given array or List with a default OrderComparator,
|
||||
* if necessary. Simply skips sorting when given any other value.
|
||||
* <p>Optimized to skip sorting for lists with size 0 or 1,
|
||||
* in order to avoid unnecessary array extraction.
|
||||
* @param value the array or List to sort
|
||||
* @see java.util.Arrays#sort(Object[], java.util.Comparator)
|
||||
*/
|
||||
public static void sortIfNecessary(Object value) {
|
||||
if (value instanceof Object[]) {
|
||||
sort((Object[]) value);
|
||||
}
|
||||
else if (value instanceof List) {
|
||||
sort((List) value);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -86,4 +86,21 @@ public class AnnotationAwareOrderComparator extends OrderComparator {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sort the given array or List with a default AnnotationAwareOrderComparator,
|
||||
* if necessary. Simply skips sorting when given any other value.
|
||||
* <p>Optimized to skip sorting for lists with size 0 or 1,
|
||||
* in order to avoid unnecessary array extraction.
|
||||
* @param value the array or List to sort
|
||||
* @see java.util.Arrays#sort(Object[], java.util.Comparator)
|
||||
*/
|
||||
public static void sortIfNecessary(Object value) {
|
||||
if (value instanceof Object[]) {
|
||||
sort((Object[]) value);
|
||||
}
|
||||
else if (value instanceof List) {
|
||||
sort((List) value);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue