簡體   English   中英

按方法級別 @Order 注釋對 spring @Beans 進行排序

[英]Sort spring @Beans by method level @Order annotation

我的庫必須處理以任意順序指定的多個 bean(攔截器)(因為它們分布在多個配置文件中)。 在應用它們之前,我必須按優先級對它們進行排序。 我為此使用AnnotationAwareOrderComparator.sort(beans) 只要在該攔截器的 class 級別上添加@Order注釋,此方法就可以正常工作。 但是當我嘗試在 @Bean 方法的 @Configuration 類中使用它時它不起作用:

@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 ...
    }

}

但是如果我添加這樣的測試:

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

那么測試就會失敗。 從我的調試中我知道AnnotationAwareOrderComparator.findOrder(Object)總是為這些 bean 的順序返回 null(未指定)。 可能是因為 bean 實例未被代理,因此既沒有實現順序,也沒有在其 class 級別上進行順序注釋。 是否有我必須啟用的 BeanPostProcessor 或配置選項?

控制流

我如何告訴 spring 保留帶注釋的順序或使用應用程序上下文的 bean 定義對 bean 進行適當排序?

要使您的測試用例正常工作,您需要使用Ordered接口而不是注釋。 讓我們檢查一下AnnotationAwareOrderComparator的源代碼。 請注意,您將對象直接傳遞給sort方法。 所述AnnotationAwareOrderComparator使用findOrder以便於pased對象找到三個工件之一@Priority注釋@Order注釋或@Ordered接口。 在您的情況下,您正在傳遞攔截器實例,因此對於Methidf和對於Class的if檢查將返回false。 您將點擊最后一個if方法: if (obj != null)

在這種情況下,它只會檢查@Order注釋的對象clas,在您的情況下將為null。 有效的是你的測試用例是錯誤的。 請注意,如果實現Ordered接口,則測試用例將按預期運行。

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

我找到了一種方法來訪問僅存在於 bean 工廠方法中的注釋:

    public static Comparator<Object> beanFactoryAwareOrderComparator(final ApplicationContext context,
            final Class<?> beanType) {
        final Map<?, String> beans = HashBiMap.create(context.getBeansOfType(beanType)).inverse();
        return OrderComparator.INSTANCE.withSourceProvider(bean -> {

            // The AnnotationAwareOrderComparator does not have the "withSourceProvider" method
            // The OrderComparator.withSourceProvider does not properly account for the annotations
            final Integer priority = AnnotationAwareOrderComparator.INSTANCE.getPriority(bean);
            if (priority != null) {
                return (Ordered) () -> priority;
            }

            // Consult the bean factory method for annotations
            final String beanName = beans.get(bean);
            if (beanName != null) {
                final Order order = context.findAnnotationOnBean(beanName, Order.class);
                if (order != null) {
                    return (Ordered) order::value;
                }
            }

            // Nothing present
            return null;
        });
    }

來源: yidongnan/grpc-spring-boot-starter/InterceptorOrder

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM