Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Sort spring @Beans by method level @Order annotation

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?

Controll-Flow

How do I tell spring to either preserve the annotated order or use the application context's bean definitions to sort the beans appropriately?

like image 405
ST-DDT Avatar asked Nov 06 '22 17:11

ST-DDT


1 Answers

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;
    }
like image 151
Alexander Petrov Avatar answered Nov 14 '22 23:11

Alexander Petrov