简体   繁体   English

按方法级别 @Order 注释对 spring @Beans 进行排序

[英]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).我的库必须处理以任意顺序指定的多个 bean(拦截器)(因为它们分布在多个配置文件中)。 Before I can apply them I have to sort them by their priority.在应用它们之前,我必须按优先级对它们进行排序。 I use AnnotationAwareOrderComparator.sort(beans) for that.我为此使用AnnotationAwareOrderComparator.sort(beans) This works well as long as the @Order annotation is added on the class level of that interceptor.只要在该拦截器的 class 级别上添加@Order注释,此方法就可以正常工作。 But it does not work when I try to use it in @Configuration classes on @Bean methods:但是当我尝试在 @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 ...
    }

}

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.从我的调试中我知道AnnotationAwareOrderComparator.findOrder(Object)总是为这些 bean 的顺序返回 null(未指定)。 Probably because the bean instances aren't proxied and thus neither implement order nor have the order annotation on their class level.可能是因为 bean 实例未被代理,因此既没有实现顺序,也没有在其 class 级别上进行顺序注释。 Is there a BeanPostProcessor or an config option that I have to enable?是否有我必须启用的 BeanPostProcessor 或配置选项?

控制流

How do I tell spring to either preserve the annotated order or use the application context's bean definitions to sort the beans appropriately?我如何告诉 spring 保留带注释的顺序或使用应用程序上下文的 bean 定义对 bean 进行适当排序?

For your test case to work you need to use Ordered interface instead of the annotation. 要使您的测试用例正常工作,您需要使用Ordered接口而不是注释。 Lets examine the source code of AnnotationAwareOrderComparator. 让我们检查一下AnnotationAwareOrderComparator的源代码。 Notice that you are passing directly the objects to the sort method. 请注意,您将对象直接传递给sort方法。 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. 所述AnnotationAwareOrderComparator使用findOrder以便于pased对象找到三个工件之一@Priority注释@Order注释或@Ordered接口。 In your case you are passing the interceptor instances so the if check for Methidf and for Class will return false. 在您的情况下,您正在传递拦截器实例,因此对于Methidf和对于Class的if检查将返回false。 You will hit the last if method: if (obj != null) 您将点击最后一个if方法: if (obj != null)

in which case it will just check you object clas for @Order annotation which will be null in your case. 在这种情况下,它只会检查@Order注释的对象clas,在您的情况下将为null。 Effectivly your test case is just wrong. 有效的是你的测试用例是错误的。 Notice though that your testcase will behave as expected if you implement Ordered interface. 请注意,如果实现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;
    }

I found a way to access the annotions that are only present on the bean factory methods:我找到了一种方法来访问仅存在于 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;
        });
    }

Source: yidongnan/grpc-spring-boot-starter/InterceptorOrder来源: yidongnan/grpc-spring-boot-starter/InterceptorOrder

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM