【问题标题】:Sort spring @Beans by method level @Order annotation按方法级别@Order注释对spring @Beans进行排序
【发布时间】:2019-05-27 20:07:35
【问题描述】:

我的库必须处理以任意顺序指定的多个 bean(拦截器)(因为它们分布在多个配置文件中)。 在我可以应用它们之前,我必须按它们的优先级对它们进行排序。我为此使用AnnotationAwareOrderComparator.sort(beans)。只要在该拦截器的类级别上添加了@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 实例没有被代理,因此在它们的类级别上既没有实现 order 也没有 order 注释。是否有我必须启用的 BeanPostProcessor 或配置选项?

我如何告诉 spring 保留带注释的顺序或使用应用程序上下文的 bean 定义对 bean 进行适当的排序?

【问题讨论】:

    标签: java spring sorting configuration


    【解决方案1】:

    要使您的测试用例正常工作,您需要使用Ordered 接口而不是注释。 让我们检查一下 AnnotationAwareOrderComparator 的源代码。请注意,您将对象直接传递给 sort 方法。 AnnotationAwareOrderComparator 使用 findOrder 对已传递的对象,以便找到三个工件之一 @Priority 注释 @Order 注释或 @Ordered 接口。在您的情况下,您正在传递拦截器实例,因此如果检查 Methidf 和 Class 将返回 false。您将点击最后一个 if 方法:if (obj != null)

    在这种情况下,它只会检查您的 @Order 注释的对象类,在您的情况下该注释将为空。实际上,您的测试用例是错误的。请注意,如果您实现 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;
        }
    

    【讨论】:

    • 那为什么我可以在该方法上放置订单注释呢? javadoc 中没有任何内容表明您不能将它用于@Bean 声明。 Spring 在很多地方都使用了代理和类似的构造,为什么不针对这种情况呢?子类化一个 bean 类只是为了设置顺序感觉不对。
    • @ST-DDT 你可以使用它,并且在启动过程中会考虑到它。但这是在您拥有实际实例之前。在您的测试用例中,不是您没有正确使用的订单,而是 AnnotationAwareOrderComparator。您期望它可以在初始化的实例上工作,而它不是。根据启动期间的文档,应正确应用订单。因此,如果您将 Autowire 放在测试中的拦截器列表中,它们应该被正确排序。
    猜你喜欢
    • 1970-01-01
    • 2021-01-04
    • 2019-10-11
    • 1970-01-01
    • 2012-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多