Как найти аннотации у метода аннотированного @Async
Есть локальная задача- найти методы с кастомной аннотацией, суть ее в маркере. То есть если эта аннотация имеется над методом, то название метода сохраняется для последующей работы с ним. Задача легко решается перебором бинов в ApplicationContext и методов у них, с поиском нужной аннотации у метода. Но как только у нужного метода имеется аннотация @Async, то isAnnotationPresent показывает, что такой аннотации нет, да и вообще у метода нет никаких аннотаций, в том числе и @Async. Всё еще интереснее, если в классе несколько методов с искомой аннотацией, и у одного из методов этого класса есть аннотация @Async, то описанный выше способ поиска аннотаций, показывает их отсутствие даже у методов которые @Async не отмечены. Вопрос, как можно найти все методы с нужной аннотацией в проекте?
Экспериментальный класс поиска Аннотации:
@Component
@Slf4j
@EnableAsync
public class BeanFinder {
private final ApplicationContext applicationContext;
public BeanFinder(ApplicationContext applicationContext) {
this.applicationContext = applicationContext;
}
@EventListener(ApplicationReadyEvent.class)
public void findBean() {
log.info("find async");
Arrays.stream(applicationContext.getBeanDefinitionNames()).
forEach(x -> Arrays.stream(applicationContext.getBean(x).getClass().getDeclaredMethods())
.filter(y -> y.isAnnotationPresent(Annotation1.class)).forEach(y -> log.info(y.getName())));
}
}
Экспериментальная аннотация:
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface Annotation1 {
}
Экспериментальный класс с методами:
@Component
public class BeanAsync {
@Async
@Annotation1
public void method1Async() {
}
@Annotation1
public void method2() {
}
public void method3() {
}
@Async
public void method4() {
}
}
Ответы (1 шт):
Так происходит потому, что для реализации асинхронного выполнения Spring оборачивает бин в прокси-объект (собственно, аналогично тому, как Hibernate поступает с lazy-полями).
У прокси-класса, естественно, нет указанных вами аннттаций, поэтому нужно достучаться до исходного класса, в чём поможет ClassUtils.getUserClass()
Итого, немного видоизменив, метод findBean() может выглядеть так:
public void findBean() {
log.info("find async");
Arrays.stream(applicationContext.getBeanDefinitionNames()).
map(s -> ClassUtils.getUserClass(applicationContext.getBean(s)))
.flatMap(aClass -> Arrays.stream(aClass.getDeclaredMethods()))
.filter(method -> method.isAnnotationPresent(Annotation1.class))
.forEach(method -> log.info(method.getName()));
}
Вывод в лог:
find async
method2
method1Async
P.S. следствие того, что асинхронное выполнение обеспечивается прокси-объектом: нельзя вызвать асинхронный метод из других методов того же объекта, т.к. вызов пойдет минуя прокси-обёртку.