diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/annotation/AutowiredAnnotationBeanPostProcessor.java b/spring-beans/src/main/java/org/springframework/beans/factory/annotation/AutowiredAnnotationBeanPostProcessor.java index e24b75d4518..fffac6f73dc 100644 --- a/spring-beans/src/main/java/org/springframework/beans/factory/annotation/AutowiredAnnotationBeanPostProcessor.java +++ b/spring-beans/src/main/java/org/springframework/beans/factory/annotation/AutowiredAnnotationBeanPostProcessor.java @@ -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 autowiredBeanNames = new LinkedHashSet(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) { diff --git a/spring-beans/src/test/java/org/springframework/beans/factory/annotation/AutowiredAnnotationBeanPostProcessorTests.java b/spring-beans/src/test/java/org/springframework/beans/factory/annotation/AutowiredAnnotationBeanPostProcessorTests.java index 7181f0b8a12..d5f8de6f417 100644 --- a/spring-beans/src/test/java/org/springframework/beans/factory/annotation/AutowiredAnnotationBeanPostProcessorTests.java +++ b/spring-beans/src/test/java/org/springframework/beans/factory/annotation/AutowiredAnnotationBeanPostProcessorTests.java @@ -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 { + } + } diff --git a/spring-core/src/main/java/org/springframework/core/OrderComparator.java b/spring-core/src/main/java/org/springframework/core/OrderComparator.java index 51408c7441d..f8e007f562f 100644 --- a/spring-core/src/main/java/org/springframework/core/OrderComparator.java +++ b/spring-core/src/main/java/org/springframework/core/OrderComparator.java @@ -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 { } } + /** + * Sort the given array or List with a default OrderComparator, + * if necessary. Simply skips sorting when given any other value. + *

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); + } + } + } diff --git a/spring-core/src/main/java/org/springframework/core/annotation/AnnotationAwareOrderComparator.java b/spring-core/src/main/java/org/springframework/core/annotation/AnnotationAwareOrderComparator.java index 19ba5fec9a5..7a21d93da82 100644 --- a/spring-core/src/main/java/org/springframework/core/annotation/AnnotationAwareOrderComparator.java +++ b/spring-core/src/main/java/org/springframework/core/annotation/AnnotationAwareOrderComparator.java @@ -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. + *

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); + } + } + }