My library has to deal with multiple beans (interceptors) that are specified in arbitrary order (because they are spread over multiple configuration files).
Before I can apply them I have to sort them by their priority. I use AnnotationAwareOrderComparator.sort(beans)
for that. This works well as long as the @Order
annotation is added on the class level of that interceptor.
But it does not work when I try to use it in @Configuration classes on @Bean methods:
@Configuration
public class Config {
@Bean
@Order(1)
public ServerInterceptor exceptionTranslatingServerInterceptor() {
return ...;
}
@Bean
@Order(2)
public ServerInterceptor authenticatingServerInterceptor() {
return ...;
}
@Bean
@Order(3)
public ServerInterceptor authorizationCheckingServerInterceptor() {
return ...
}
}
But if I add a test like this:
@Test
void testOrderingOfTheDefaultInterceptors() {
List<ServerInterceptor> expected = new ArrayList<>();
expected.add(applicationContext.getBean(ExceptionTranslatingServerInterceptor.class));
expected.add(applicationContext.getBean(AuthenticatingServerInterceptor.class));
expected.add(applicationContext.getBean(AuthorizationCheckingServerInterceptor.class));
List<ServerInterceptor> actual = new ArrayList<>(this.registry.getServerInterceptors());
assertEquals(expected, actual); // Accidentally passes
// System.out.println(actual);
Collections.shuffle(actual);
AnnotationAwareOrderComparator.sort(actual);
assertEquals(expected, actual); // Fails
// System.out.println(actual);
}
Then the test will fail.
From my debugging I know that AnnotationAwareOrderComparator.findOrder(Object)
always returns null (unspecified) for the order of those beans. Probably because the bean instances aren't proxied and thus neither implement order nor have the order annotation on their class level. Is there a BeanPostProcessor or an config option that I have to enable?
How do I tell spring to either preserve the annotated order or use the application context's bean definitions to sort the beans appropriately?
For your test case to work you need to use Ordered
interface instead of the annotation.
Lets examine the source code of AnnotationAwareOrderComparator. Notice that you are passing directly the objects to the sort method. The AnnotationAwareOrderComparator is using the findOrder
upon the pased objects in order to find one of the three artifacts @Priority
annotation @Order
annotation or @Ordered
interface. In your case you are passing the interceptor instances so the if check for Methidf and for Class will return false. You will hit the last if method: if (obj != null)
in which case it will just check you object clas for @Order annotation which will be null in your case. Effectivly your test case is just wrong. Notice though that your testcase will behave as expected if you implement Ordered interface.
public static void sort(List list) {
if (list.size() > 1) {
Collections.sort(list, INSTANCE);
}
}
protected Integer findOrder(Object obj) {
// Check for regular Ordered interface
Integer order = super.findOrder(obj);
if (order != null) {
return order;
}
// Check for @Order and @Priority on various kinds of elements
if (obj instanceof Class) {
return OrderUtils.getOrder((Class) obj);
}
else if (obj instanceof Method) {
Order ann = AnnotationUtils.findAnnotation((Method) obj, Order.class);
if (ann != null) {
return ann.value();
}
}
else if (obj instanceof AnnotatedElement) {
Order ann = AnnotationUtils.getAnnotation((AnnotatedElement) obj, Order.class);
if (ann != null) {
return ann.value();
}
}
else if (obj != null) {
return OrderUtils.getOrder(obj.getClass());
}
return null;
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With