[英]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;
});
}
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.