Моя библиотека имеет дело с несколькими bean-компонентами (перехватчиками), указанными в произвольном порядке (поскольку они распределены по нескольким файлам конфигурации).
Прежде чем я смогу применить их, я должен отсортировать их по приоритету. Я использую AnnotationAwareOrderComparator.sort(beans)
для этого. Это хорошо работает, пока аннотация @Order
добавляется на уровне класса этого перехватчика.
Но это не работает, когда я пытаюсь использовать его в классах @Configuration в методах @Bean:
@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)
всегда возвращает null (не указано) для порядка этих бобов. Вероятно, потому что экземпляры bean-компонентов не проксируются и, следовательно, не реализуют порядок и не имеют аннотации порядка на уровне своего класса. Есть ли BeanPostProcessor или параметр конфигурации, который я должен включить?
Как мне указать Spring либо сохранить аннотированный порядок, либо использовать определения bean-компонентов контекста приложения для правильной сортировки bean-компонентов?
Чтобы ваш тестовый пример работал, вам нужно использовать интерфейс Ordered
вместо аннотации.
Давайте рассмотрим исходный код AnnotationAwareOrderComparator. Обратите внимание, что вы передаете объекты непосредственно методу сортировки. Компаратор AnnotationAwareOrderComparator использует findOrder
для промежуточных объектов, чтобы найти один из трех артефактов @Priority
аннотацию @Order
аннотацию или @Ordered
интерфейс. В вашем случае вы передаете экземпляры перехватчика, поэтому проверка if для Methidf и для класса вернет false. Вы нажмете последний метод if: if (obj != null)
в этом случае он просто проверит ваш объект clas для аннотации @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;
}
@ST-DDT можно использовать, и при запуске это учитывается. Но это до того, как у вас будет настоящий экземпляр. В вашем тестовом случае это не тот порядок, который вы используете неправильно, это AnnotationAwareOrderComparator. Вы ожидаете, что он будет работать с инициализированным экземпляром, а это не так. Согласно документации при запуске Приказ должен быть применен корректно. Поэтому, если вы поместите Autowire в список перехватчиков в своем тесте, они должны быть правильно упорядочены.
Тогда почему я могу поместить аннотацию заказа в этот метод? Однако в javadoc нет ничего, что говорило бы о том, что вы не можете использовать его для объявлений
@Bean
. Spring использует прокси и подобные конструкции во многих местах, почему бы и нет в этом случае? Подкласс класса bean-компонента только для того, чтобы установить порядок, кажется неправильным.