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
This commit is contained in:
parent
64bb308763
commit
001d0e734c
|
@ -0,0 +1,58 @@
|
|||
/*
|
||||
* Copyright 2002-2014 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.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.beans.factory.support;
|
||||
|
||||
import java.util.Comparator;
|
||||
|
||||
import org.springframework.core.annotation.AnnotationAwareOrderComparator;
|
||||
import org.springframework.core.annotation.DefaultOrderProviderComparator;
|
||||
|
||||
/**
|
||||
* The default {@link Comparator} to use to order dependencies. Extend
|
||||
* from {@link DefaultOrderProviderComparator} so that the bean factory
|
||||
* has the ability to provide an {@link org.springframework.core.annotation.OrderProvider}
|
||||
* that is aware of more bean metadata, if any.
|
||||
*
|
||||
* @author Stephane Nicoll
|
||||
* @since 4.1
|
||||
* @see org.springframework.core.annotation.OrderProviderComparator
|
||||
* @see org.springframework.core.annotation.OrderProvider
|
||||
* @see DefaultListableBeanFactory#setDependencyComparator(java.util.Comparator)
|
||||
*/
|
||||
public class DefaultDependencyComparator extends DefaultOrderProviderComparator implements Comparator<Object> {
|
||||
|
||||
/**
|
||||
* Shared default instance of DefaultDependencyComparator.
|
||||
*/
|
||||
public static final DefaultDependencyComparator INSTANCE = new DefaultDependencyComparator();
|
||||
|
||||
private final Comparator<Object> comparator;
|
||||
|
||||
public DefaultDependencyComparator(Comparator<Object> comparator) {
|
||||
this.comparator = comparator;
|
||||
}
|
||||
|
||||
public DefaultDependencyComparator() {
|
||||
this(AnnotationAwareOrderComparator.INSTANCE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int compare(Object o1, Object o2) {
|
||||
return comparator.compare(o1, o2);
|
||||
}
|
||||
|
||||
}
|
|
@ -32,6 +32,7 @@ import java.util.Collection;
|
|||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.HashMap;
|
||||
import java.util.IdentityHashMap;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
@ -60,6 +61,7 @@ import org.springframework.beans.factory.config.ConfigurableBeanFactory;
|
|||
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
|
||||
import org.springframework.beans.factory.config.DependencyDescriptor;
|
||||
import org.springframework.core.annotation.AnnotationUtils;
|
||||
import org.springframework.core.annotation.OrderProviderComparator;
|
||||
import org.springframework.core.annotation.OrderUtils;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.ClassUtils;
|
||||
|
@ -895,7 +897,7 @@ public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFacto
|
|||
TypeConverter converter = (typeConverter != null ? typeConverter : getTypeConverter());
|
||||
Object result = converter.convertIfNecessary(matchingBeans.values(), type);
|
||||
if (this.dependencyComparator != null && result instanceof Object[]) {
|
||||
Arrays.sort((Object[]) result, this.dependencyComparator);
|
||||
sortArray((Object[]) result, matchingBeans);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
@ -922,7 +924,7 @@ public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFacto
|
|||
TypeConverter converter = (typeConverter != null ? typeConverter : getTypeConverter());
|
||||
Object result = converter.convertIfNecessary(matchingBeans.values(), type);
|
||||
if (this.dependencyComparator != null && result instanceof List) {
|
||||
Collections.sort((List<?>) result, this.dependencyComparator);
|
||||
sortList((List<?>) result, matchingBeans);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
@ -983,6 +985,32 @@ public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFacto
|
|||
}
|
||||
}
|
||||
|
||||
private void sortArray(Object[] items, Map<String, Object> matchingBeans) {
|
||||
if (this.dependencyComparator instanceof OrderProviderComparator) {
|
||||
((OrderProviderComparator) this.dependencyComparator)
|
||||
.sortArray(items, createFactoryAwareOrderProvider(matchingBeans));
|
||||
} else {
|
||||
Arrays.sort(items, this.dependencyComparator);
|
||||
}
|
||||
}
|
||||
|
||||
private void sortList(List<?> items, Map<String, Object> matchingBeans) {
|
||||
if (this.dependencyComparator instanceof OrderProviderComparator) {
|
||||
((OrderProviderComparator) this.dependencyComparator)
|
||||
.sortList(items, createFactoryAwareOrderProvider(matchingBeans));
|
||||
} else {
|
||||
Collections.sort(items, this.dependencyComparator);
|
||||
}
|
||||
}
|
||||
|
||||
private FactoryAwareOrderProvider createFactoryAwareOrderProvider(Map<String, Object> beans) {
|
||||
IdentityHashMap<Object, String> instancesToBeanNames = new IdentityHashMap<Object, String>();
|
||||
for (Map.Entry<String, Object> entry : beans.entrySet()) {
|
||||
instancesToBeanNames.put(entry.getValue(), entry.getKey());
|
||||
}
|
||||
return new FactoryAwareOrderProvider(instancesToBeanNames, this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Find bean instances that match the required type.
|
||||
* Called during autowiring for the specified bean.
|
||||
|
|
|
@ -0,0 +1,84 @@
|
|||
/*
|
||||
* Copyright 2002-2014 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.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.beans.factory.support;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.Map;
|
||||
|
||||
import org.springframework.beans.factory.config.BeanDefinition;
|
||||
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
|
||||
import org.springframework.core.annotation.AnnotationUtils;
|
||||
import org.springframework.core.annotation.Order;
|
||||
import org.springframework.core.annotation.OrderProvider;
|
||||
|
||||
/**
|
||||
* An {@link OrderProvider} implementation that is aware of the
|
||||
* bean metadata of the instances to sort.
|
||||
*
|
||||
* <p>Lookup for the method factory of an instance to sort, if
|
||||
* any and retrieve the {@link Order} value defined on it. This
|
||||
* essentially allows for the following construct:
|
||||
*
|
||||
* <pre class="code">
|
||||
* @Configuration
|
||||
* public class AppConfig {
|
||||
*
|
||||
* @Bean
|
||||
* @Order(5)
|
||||
* public MyService myService() {
|
||||
* return new MyService();
|
||||
* }
|
||||
* }</pre>
|
||||
*
|
||||
* @author Stephane Nicoll
|
||||
* @since 4.1
|
||||
*/
|
||||
public class FactoryAwareOrderProvider implements OrderProvider {
|
||||
|
||||
private final Map<Object, String> instancesToBeanNames;
|
||||
|
||||
private final ConfigurableListableBeanFactory beanFactory;
|
||||
|
||||
public FactoryAwareOrderProvider(Map<Object, String> instancesToBeanNames,
|
||||
ConfigurableListableBeanFactory beanFactory) {
|
||||
this.instancesToBeanNames = instancesToBeanNames;
|
||||
this.beanFactory = beanFactory;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Integer getOrder(Object obj) {
|
||||
Method factoryMethod = getFactoryMethod(instancesToBeanNames.get(obj));
|
||||
if (factoryMethod != null) {
|
||||
Order order = AnnotationUtils.getAnnotation(factoryMethod, Order.class);
|
||||
if (order != null) {
|
||||
return order.value();
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private Method getFactoryMethod(String beanName) {
|
||||
if (beanName != null && beanFactory.containsBeanDefinition(beanName)) {
|
||||
BeanDefinition bd = beanFactory.getMergedBeanDefinition(beanName);
|
||||
if (bd instanceof RootBeanDefinition) {
|
||||
return ((RootBeanDefinition) bd).getResolvedFactoryMethod();
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
|
@ -38,11 +38,11 @@ import org.springframework.beans.factory.config.BeanDefinition;
|
|||
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
|
||||
import org.springframework.beans.factory.config.TypedStringValue;
|
||||
import org.springframework.beans.factory.support.AutowireCandidateQualifier;
|
||||
import org.springframework.beans.factory.support.DefaultDependencyComparator;
|
||||
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.AnnotationAwareOrderComparator;
|
||||
import org.springframework.core.annotation.Order;
|
||||
import org.springframework.tests.sample.beans.ITestBean;
|
||||
import org.springframework.tests.sample.beans.IndexedTestBean;
|
||||
|
@ -51,12 +51,15 @@ import org.springframework.tests.sample.beans.TestBean;
|
|||
import org.springframework.util.SerializationTestUtils;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
import static org.mockito.Matchers.any;
|
||||
import static org.mockito.Mockito.*;
|
||||
|
||||
/**
|
||||
* @author Juergen Hoeller
|
||||
* @author Mark Fisher
|
||||
* @author Sam Brannen
|
||||
* @author Chris Beams
|
||||
* @author Stephane Nicoll
|
||||
*/
|
||||
public class AutowiredAnnotationBeanPostProcessorTests {
|
||||
|
||||
|
@ -351,7 +354,7 @@ public class AutowiredAnnotationBeanPostProcessorTests {
|
|||
@Test
|
||||
public void testOrderedResourceInjection() {
|
||||
DefaultListableBeanFactory bf = new DefaultListableBeanFactory();
|
||||
bf.setDependencyComparator(AnnotationAwareOrderComparator.INSTANCE);
|
||||
bf.setDependencyComparator(DefaultDependencyComparator.INSTANCE);
|
||||
AutowiredAnnotationBeanPostProcessor bpp = new AutowiredAnnotationBeanPostProcessor();
|
||||
bpp.setBeanFactory(bf);
|
||||
bf.addBeanPostProcessor(bpp);
|
||||
|
@ -382,10 +385,38 @@ public class AutowiredAnnotationBeanPostProcessorTests {
|
|||
bf.destroySingletons();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testOrderedResourceInjectionDetectsFactoryAwareComparator() {
|
||||
DefaultListableBeanFactory bf = new DefaultListableBeanFactory();
|
||||
DefaultDependencyComparator comparator = mock(DefaultDependencyComparator.class);
|
||||
bf.setDependencyComparator(comparator);
|
||||
|
||||
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);
|
||||
final OrderedNestedTestBean ntb1 = new OrderedNestedTestBean();
|
||||
ntb1.setOrder(2);
|
||||
bf.registerSingleton("nestedTestBean1", ntb1);
|
||||
final OrderedNestedTestBean ntb2 = new OrderedNestedTestBean();
|
||||
ntb2.setOrder(1);
|
||||
bf.registerSingleton("nestedTestBean2", ntb2);
|
||||
|
||||
OptionalResourceInjectionBean bean = (OptionalResourceInjectionBean) bf.getBean("annotatedBean");
|
||||
verify(comparator, never()).compare(any(), any());
|
||||
verify(comparator, never()).sortList(any(),any());
|
||||
verify(comparator, times(2)).sortArray(any(),any());
|
||||
bf.destroySingletons();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAnnotationOrderedResourceInjection() {
|
||||
DefaultListableBeanFactory bf = new DefaultListableBeanFactory();
|
||||
bf.setDependencyComparator(AnnotationAwareOrderComparator.INSTANCE);
|
||||
bf.setDependencyComparator(DefaultDependencyComparator.INSTANCE);
|
||||
AutowiredAnnotationBeanPostProcessor bpp = new AutowiredAnnotationBeanPostProcessor();
|
||||
bpp.setBeanFactory(bf);
|
||||
bf.addBeanPostProcessor(bpp);
|
||||
|
@ -417,7 +448,7 @@ public class AutowiredAnnotationBeanPostProcessorTests {
|
|||
@Test
|
||||
public void testOrderedCollectionResourceInjection() {
|
||||
DefaultListableBeanFactory bf = new DefaultListableBeanFactory();
|
||||
bf.setDependencyComparator(AnnotationAwareOrderComparator.INSTANCE);
|
||||
bf.setDependencyComparator(DefaultDependencyComparator.INSTANCE);
|
||||
AutowiredAnnotationBeanPostProcessor bpp = new AutowiredAnnotationBeanPostProcessor();
|
||||
bpp.setBeanFactory(bf);
|
||||
bf.addBeanPostProcessor(bpp);
|
||||
|
@ -458,7 +489,7 @@ public class AutowiredAnnotationBeanPostProcessorTests {
|
|||
@Test
|
||||
public void testAnnotationOrderedCollectionResourceInjection() {
|
||||
DefaultListableBeanFactory bf = new DefaultListableBeanFactory();
|
||||
bf.setDependencyComparator(AnnotationAwareOrderComparator.INSTANCE);
|
||||
bf.setDependencyComparator(DefaultDependencyComparator.INSTANCE);
|
||||
AutowiredAnnotationBeanPostProcessor bpp = new AutowiredAnnotationBeanPostProcessor();
|
||||
bpp.setBeanFactory(bf);
|
||||
bf.addBeanPostProcessor(bpp);
|
||||
|
@ -576,7 +607,7 @@ public class AutowiredAnnotationBeanPostProcessorTests {
|
|||
@Test
|
||||
public void testConstructorResourceInjectionWithMultipleOrderedCandidates() {
|
||||
DefaultListableBeanFactory bf = new DefaultListableBeanFactory();
|
||||
bf.setDependencyComparator(AnnotationAwareOrderComparator.INSTANCE);
|
||||
bf.setDependencyComparator(DefaultDependencyComparator.INSTANCE);
|
||||
AutowiredAnnotationBeanPostProcessor bpp = new AutowiredAnnotationBeanPostProcessor();
|
||||
bpp.setBeanFactory(bf);
|
||||
bf.addBeanPostProcessor(bpp);
|
||||
|
@ -600,7 +631,7 @@ public class AutowiredAnnotationBeanPostProcessorTests {
|
|||
@Test
|
||||
public void testConstructorResourceInjectionWithMultipleCandidatesAsOrderedCollection() {
|
||||
DefaultListableBeanFactory bf = new DefaultListableBeanFactory();
|
||||
bf.setDependencyComparator(AnnotationAwareOrderComparator.INSTANCE);
|
||||
bf.setDependencyComparator(DefaultDependencyComparator.INSTANCE);
|
||||
AutowiredAnnotationBeanPostProcessor bpp = new AutowiredAnnotationBeanPostProcessor();
|
||||
bpp.setBeanFactory(bf);
|
||||
bf.addBeanPostProcessor(bpp);
|
||||
|
|
|
@ -0,0 +1,68 @@
|
|||
/*
|
||||
* Copyright 2002-2014 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.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.beans.factory.support;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import org.springframework.core.Ordered;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Stephane Nicoll
|
||||
*/
|
||||
public class DependencyComparatorTests {
|
||||
|
||||
private final DefaultDependencyComparator comparator = new DefaultDependencyComparator();
|
||||
|
||||
@Test
|
||||
public void plainComparator() {
|
||||
List<Object> items = new ArrayList<Object>();
|
||||
C c = new C(5);
|
||||
C c2 = new C(-5);
|
||||
items.add(c);
|
||||
items.add(c2);
|
||||
Collections.sort(items, comparator);
|
||||
assertOrder(items, c2, c);
|
||||
}
|
||||
|
||||
private void assertOrder(List<?> actual, Object... expected) {
|
||||
for (int i = 0; i < actual.size(); i++) {
|
||||
assertSame("Wrong instance at index '" + i + "'", expected[i], actual.get(i));
|
||||
}
|
||||
assertEquals("Wrong number of items", expected.length, actual.size());
|
||||
}
|
||||
|
||||
private static class C implements Ordered {
|
||||
private final int order;
|
||||
|
||||
private C(int order) {
|
||||
this.order = order;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getOrder() {
|
||||
return order;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,139 @@
|
|||
/*
|
||||
* Copyright 2002-2014 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.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.beans.factory.support;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
import static org.mockito.BDDMockito.*;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.verify;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.HashMap;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.junit.rules.TestName;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.MockitoAnnotations;
|
||||
|
||||
import org.springframework.beans.factory.config.BeanDefinition;
|
||||
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
|
||||
import org.springframework.core.annotation.Order;
|
||||
import org.springframework.util.ReflectionUtils;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Stephane Nicoll
|
||||
*/
|
||||
public class FactoryAwareOrderProviderTests {
|
||||
|
||||
@Rule
|
||||
public final TestName name = new TestName();
|
||||
|
||||
@Mock
|
||||
private ConfigurableListableBeanFactory beanFactory;
|
||||
|
||||
@Before
|
||||
public void setup() {
|
||||
MockitoAnnotations.initMocks(this);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void noBeanName() {
|
||||
FactoryAwareOrderProvider orderProvider = createOrderProvider(new HashMap<Object, String>());
|
||||
assertNull(orderProvider.getOrder(25));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void beanNameNotRegistered() {
|
||||
HashMap<Object, String> beans = new HashMap<>();
|
||||
beans.put(25, "myBean");
|
||||
given(beanFactory.containsBeanDefinition("myBean")).willReturn(false);
|
||||
FactoryAwareOrderProvider orderProvider = createOrderProvider(beans);
|
||||
assertNull(orderProvider.getOrder(25));
|
||||
verify(beanFactory).containsBeanDefinition("myBean");
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void beanNameNoRootBeanDefinition() {
|
||||
HashMap<Object, String> beans = new HashMap<>();
|
||||
beans.put(25, "myBean");
|
||||
given(beanFactory.containsBeanDefinition("myBean")).willReturn(true);
|
||||
given(beanFactory.getMergedBeanDefinition("myBean")).willReturn(mock(BeanDefinition.class));
|
||||
FactoryAwareOrderProvider orderProvider = createOrderProvider(beans);
|
||||
assertNull(orderProvider.getOrder(25));
|
||||
verify(beanFactory).containsBeanDefinition("myBean");
|
||||
verify(beanFactory).getMergedBeanDefinition("myBean");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void beanNameNoFactory() {
|
||||
HashMap<Object, String> beans = new HashMap<>();
|
||||
beans.put(25, "myBean");
|
||||
RootBeanDefinition rbd = mock(RootBeanDefinition.class);
|
||||
given(rbd.getResolvedFactoryMethod()).willReturn(null);
|
||||
|
||||
given(beanFactory.containsBeanDefinition("myBean")).willReturn(true);
|
||||
given(beanFactory.getMergedBeanDefinition("myBean")).willReturn(rbd);
|
||||
FactoryAwareOrderProvider orderProvider = createOrderProvider(beans);
|
||||
assertNull(orderProvider.getOrder(25));
|
||||
verify(beanFactory).containsBeanDefinition("myBean");
|
||||
verify(beanFactory).getMergedBeanDefinition("myBean");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void beanNameFactoryNoOrderValue() {
|
||||
HashMap<Object, String> beans = new HashMap<>();
|
||||
beans.put(25, "myBean");
|
||||
|
||||
Method m = ReflectionUtils.findMethod(getClass(), name.getMethodName());
|
||||
RootBeanDefinition rbd = mock(RootBeanDefinition.class);
|
||||
given(rbd.getResolvedFactoryMethod()).willReturn(m);
|
||||
|
||||
given(beanFactory.containsBeanDefinition("myBean")).willReturn(true);
|
||||
given(beanFactory.getMergedBeanDefinition("myBean")).willReturn(rbd);
|
||||
FactoryAwareOrderProvider orderProvider = createOrderProvider(beans);
|
||||
assertNull(orderProvider.getOrder(25));
|
||||
verify(beanFactory).containsBeanDefinition("myBean");
|
||||
verify(beanFactory).getMergedBeanDefinition("myBean");
|
||||
}
|
||||
|
||||
@Test
|
||||
@Order(500)
|
||||
public void beanNameFactoryOrderValue() {
|
||||
HashMap<Object, String> beans = new HashMap<>();
|
||||
beans.put(25, "myBean");
|
||||
|
||||
Method m = ReflectionUtils.findMethod(getClass(), name.getMethodName());
|
||||
RootBeanDefinition rbd = mock(RootBeanDefinition.class);
|
||||
given(rbd.getResolvedFactoryMethod()).willReturn(m);
|
||||
|
||||
given(beanFactory.containsBeanDefinition("myBean")).willReturn(true);
|
||||
given(beanFactory.getMergedBeanDefinition("myBean")).willReturn(rbd);
|
||||
FactoryAwareOrderProvider orderProvider = createOrderProvider(beans);
|
||||
assertEquals(Integer.valueOf(500), orderProvider.getOrder(25));
|
||||
verify(beanFactory).containsBeanDefinition("myBean");
|
||||
verify(beanFactory).getMergedBeanDefinition("myBean");
|
||||
}
|
||||
|
||||
private FactoryAwareOrderProvider createOrderProvider(HashMap<Object, String> beans) {
|
||||
return new FactoryAwareOrderProvider(beans, beanFactory);
|
||||
}
|
||||
|
||||
}
|
|
@ -28,6 +28,7 @@ import org.springframework.beans.factory.config.BeanDefinition;
|
|||
import org.springframework.beans.factory.config.BeanDefinitionHolder;
|
||||
import org.springframework.beans.factory.support.AbstractBeanDefinition;
|
||||
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
|
||||
import org.springframework.beans.factory.support.DefaultDependencyComparator;
|
||||
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
|
||||
import org.springframework.beans.factory.support.RootBeanDefinition;
|
||||
import org.springframework.context.support.GenericApplicationContext;
|
||||
|
@ -243,7 +244,7 @@ public class AnnotationConfigUtils {
|
|||
DefaultListableBeanFactory beanFactory = unwrapDefaultListableBeanFactory(registry);
|
||||
if (beanFactory != null) {
|
||||
if (!(beanFactory.getDependencyComparator() instanceof AnnotationAwareOrderComparator)) {
|
||||
beanFactory.setDependencyComparator(AnnotationAwareOrderComparator.INSTANCE);
|
||||
beanFactory.setDependencyComparator(DefaultDependencyComparator.INSTANCE);
|
||||
}
|
||||
if (!(beanFactory.getAutowireCandidateResolver() instanceof ContextAnnotationAutowireCandidateResolver)) {
|
||||
beanFactory.setAutowireCandidateResolver(new ContextAnnotationAutowireCandidateResolver());
|
||||
|
|
|
@ -0,0 +1,93 @@
|
|||
/*
|
||||
* Copyright 2002-2014 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.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.context.annotation.spr11310;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.core.annotation.Order;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Stephane Nicoll
|
||||
*/
|
||||
public class Spr11310Tests {
|
||||
|
||||
@Test
|
||||
public void orderedList() {
|
||||
ApplicationContext context = new AnnotationConfigApplicationContext(Config.class);
|
||||
StringHolder holder = context.getBean(StringHolder.class);
|
||||
assertEquals("second", holder.itemsList.get(0));
|
||||
assertEquals("first", holder.itemsList.get(1));
|
||||
assertEquals("unknownOrder", holder.itemsList.get(2));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void orderedArray() {
|
||||
ApplicationContext context = new AnnotationConfigApplicationContext(Config.class);
|
||||
StringHolder holder = context.getBean(StringHolder.class);
|
||||
assertEquals("second", holder.itemsArray[0]);
|
||||
assertEquals("first", holder.itemsArray[1]);
|
||||
assertEquals("unknownOrder", holder.itemsArray[2]);
|
||||
}
|
||||
|
||||
|
||||
@Configuration
|
||||
static class Config {
|
||||
|
||||
@Bean
|
||||
@Order(50)
|
||||
public String first() {
|
||||
return "first";
|
||||
}
|
||||
|
||||
@Bean
|
||||
public String unknownOrder() {
|
||||
return "unknownOrder";
|
||||
}
|
||||
|
||||
@Bean
|
||||
@Order(5)
|
||||
public String second() {
|
||||
return "second";
|
||||
}
|
||||
|
||||
@Bean
|
||||
public StringHolder stringHolder() {
|
||||
return new StringHolder();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
private static class StringHolder {
|
||||
@Autowired
|
||||
private List<String> itemsList;
|
||||
|
||||
@Autowired
|
||||
private String[] itemsArray;
|
||||
|
||||
}
|
||||
}
|
|
@ -0,0 +1,70 @@
|
|||
/*
|
||||
* Copyright 2002-2014 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.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.core.annotation;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* A default {@link OrderProviderComparator} implementation that uses the
|
||||
* value provided by the {@link OrderProvider} and fallbacks to
|
||||
* {@link AnnotationAwareOrderComparator} if none is set.
|
||||
*
|
||||
* <p>This essentially means that the value of the {@link OrderProvider}
|
||||
* takes precedence over the behavior of {@link AnnotationAwareOrderComparator}
|
||||
*
|
||||
* @author Stephane Nicoll
|
||||
* @since 4.1
|
||||
*/
|
||||
public class DefaultOrderProviderComparator implements OrderProviderComparator {
|
||||
|
||||
/**
|
||||
* Shared default instance of DefaultOrderProviderComparator.
|
||||
*/
|
||||
public static final DefaultOrderProviderComparator INSTANCE = new DefaultOrderProviderComparator();
|
||||
|
||||
@Override
|
||||
public void sortList(List<?> items, OrderProvider orderProvider) {
|
||||
Collections.sort(items, new OrderProviderAwareComparator(orderProvider));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sortArray(Object[] items, OrderProvider orderProvider) {
|
||||
Arrays.sort(items, new OrderProviderAwareComparator(orderProvider));
|
||||
}
|
||||
|
||||
|
||||
private static class OrderProviderAwareComparator extends AnnotationAwareOrderComparator {
|
||||
|
||||
private final OrderProvider orderProvider;
|
||||
|
||||
private OrderProviderAwareComparator(OrderProvider orderProvider) {
|
||||
this.orderProvider = orderProvider;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected int getOrder(Object obj) {
|
||||
Integer order = this.orderProvider.getOrder(obj);
|
||||
if (order != null) {
|
||||
return order;
|
||||
}
|
||||
return super.getOrder(obj);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,36 @@
|
|||
/*
|
||||
* Copyright 2002-2014 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.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.core.annotation;
|
||||
|
||||
/**
|
||||
* Strategy interface to provide the order value of a given instance.
|
||||
*
|
||||
* @author Stephane Nicoll
|
||||
* @since 4.1
|
||||
* @see OrderProviderComparator
|
||||
*/
|
||||
public interface OrderProvider {
|
||||
|
||||
/**
|
||||
* Return the order value of the specified object or {@code null} if
|
||||
* it cannot be retrieved.
|
||||
* @param obj the object to handle
|
||||
* @return the order value for that object or {@code null} if none is found
|
||||
*/
|
||||
Integer getOrder(Object obj);
|
||||
|
||||
}
|
|
@ -0,0 +1,49 @@
|
|||
/*
|
||||
* Copyright 2002-2014 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.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.core.annotation;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Sort a collection of element according to an {@link OrderProvider}.
|
||||
*
|
||||
* @author Stephane Nicoll
|
||||
* @since 4.1
|
||||
*/
|
||||
public interface OrderProviderComparator {
|
||||
|
||||
/**
|
||||
* Sort the specified list of items according to their order value,
|
||||
* using the specified {@link OrderProvider} to retrieve an order
|
||||
* if necessary.
|
||||
* @param items the items to sort
|
||||
* @param orderProvider the order provider to use
|
||||
* @see java.util.Collections#sort(java.util.List, java.util.Comparator)
|
||||
*/
|
||||
void sortList(List<?> items, OrderProvider orderProvider);
|
||||
|
||||
/**
|
||||
* Sort the specified array of items according to their order value,
|
||||
* using the specified {@link OrderProvider} to retrieve an order
|
||||
* if necessary.
|
||||
* @param items the items to sort
|
||||
* @param orderProvider the order provider to use
|
||||
* @see java.util.Arrays#sort(Object[], java.util.Comparator)
|
||||
*/
|
||||
void sortArray(Object[] items, OrderProvider orderProvider);
|
||||
|
||||
}
|
|
@ -0,0 +1,193 @@
|
|||
/*
|
||||
* Copyright 2002-2014 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.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.core.annotation;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import org.springframework.core.Ordered;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Stephane Nicoll
|
||||
*/
|
||||
public class DefaultOrderProviderComparatorTests {
|
||||
|
||||
private final DefaultOrderProviderComparator comparator = new DefaultOrderProviderComparator();
|
||||
|
||||
@Test
|
||||
public void listNoFactoryMethod() {
|
||||
A a = new A();
|
||||
C c = new C(-50);
|
||||
B b = new B();
|
||||
|
||||
List<?> items = Arrays.asList(a, c, b);
|
||||
comparator.sortList(items, new OrderProvider() {
|
||||
@Override
|
||||
public Integer getOrder(Object obj) {
|
||||
return null;
|
||||
}
|
||||
});
|
||||
assertOrder(items, c, a, b);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void listFactoryMethod() {
|
||||
A a = new A();
|
||||
C c = new C(3);
|
||||
B b = new B();
|
||||
|
||||
List<?> items = Arrays.asList(a, c, b);
|
||||
comparator.sortList(items, new OrderProvider() {
|
||||
@Override
|
||||
public Integer getOrder(Object obj) {
|
||||
if (obj == a) {
|
||||
return 4;
|
||||
}
|
||||
if (obj == b) {
|
||||
return 2;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
});
|
||||
assertOrder(items, b, c, a);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void listFactoryMethodOverridesStaticOrder() {
|
||||
A a = new A();
|
||||
C c = new C(5);
|
||||
C c2 = new C(-5);
|
||||
|
||||
|
||||
List<?> items = Arrays.asList(a, c, c2);
|
||||
comparator.sortList(items, new OrderProvider() {
|
||||
@Override
|
||||
public Integer getOrder(Object obj) {
|
||||
if (obj == a) {
|
||||
return 4;
|
||||
}
|
||||
if (obj == c2) {
|
||||
return 2;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
});
|
||||
assertOrder(items, c2, a, c);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void arrayNoFactoryMethod() {
|
||||
A a = new A();
|
||||
C c = new C(-50);
|
||||
B b = new B();
|
||||
|
||||
Object[] items = new Object[] {a, c, b};
|
||||
comparator.sortArray(items, new OrderProvider() {
|
||||
@Override
|
||||
public Integer getOrder(Object obj) {
|
||||
return null;
|
||||
}
|
||||
});
|
||||
assertOrder(items, c, a, b);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void arrayFactoryMethod() {
|
||||
A a = new A();
|
||||
C c = new C(3);
|
||||
B b = new B();
|
||||
|
||||
Object[] items = new Object[] {a, c, b};
|
||||
comparator.sortArray(items, new OrderProvider() {
|
||||
@Override
|
||||
public Integer getOrder(Object obj) {
|
||||
if (obj == a) {
|
||||
return 4;
|
||||
}
|
||||
if (obj == b) {
|
||||
return 2;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
});
|
||||
assertOrder(items, b, c, a);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void arrayFactoryMethodOverridesStaticOrder() {
|
||||
A a = new A();
|
||||
C c = new C(5);
|
||||
C c2 = new C(-5);
|
||||
|
||||
Object[] items = new Object[] {a, c, c2};
|
||||
comparator.sortArray(items, new OrderProvider() {
|
||||
@Override
|
||||
public Integer getOrder(Object obj) {
|
||||
if (obj == a) {
|
||||
return 4;
|
||||
}
|
||||
if (obj == c2) {
|
||||
return 2;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
});
|
||||
assertOrder(items, c2, a, c);
|
||||
}
|
||||
|
||||
private void assertOrder(List<?> actual, Object... expected) {
|
||||
for (int i = 0; i < actual.size(); i++) {
|
||||
assertSame("Wrong instance at index '" + i + "'", expected[i], actual.get(i));
|
||||
}
|
||||
assertEquals("Wrong number of items", expected.length, actual.size());
|
||||
}
|
||||
|
||||
private void assertOrder(Object[] actual, Object... expected) {
|
||||
for (int i = 0; i < actual.length; i++) {
|
||||
assertSame("Wrong instance at index '" + i + "'", expected[i], actual[i]);
|
||||
}
|
||||
assertEquals("Wrong number of items", expected.length, expected.length);
|
||||
}
|
||||
|
||||
|
||||
@Order(1)
|
||||
private static class A {
|
||||
}
|
||||
|
||||
@Order(2)
|
||||
private static class B {
|
||||
}
|
||||
|
||||
private static class C implements Ordered {
|
||||
private final int order;
|
||||
|
||||
private C(int order) {
|
||||
this.order = order;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getOrder() {
|
||||
return order;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
Loading…
Reference in New Issue