Вызов метода Spring @Async внутри метода @Scheduled

Я использую загрузку Spring с @EnableScheduling и @EnableAsync.

У меня есть метод, помеченный знаком @Scheduled. У меня есть еще несколько методов, помеченных знаком @Async.

Теперь я вызываю эти @Async методы в @Scheduled методе и распечатываю имя текущего потока в методах async. Я вижу, что все они имеют одно и то же имя потока, которое на самом деле является потоком, выполняющим метод @Scheduled.

Я не вижу выполнения асинхронного метода. Что здесь не так?

Вот мой класс загрузки приложения

@SpringBootApplication
@EnableScheduling
@EnableAsync
public class ApplicationBoot {

    public static void main(String[] args) {
        SpringApplication.run(ApplicationBoot.class, args);
    }
}

Вот мой класс планировщика

@Component
public class TaskScheduler {
    private static final Logger logger = Logger.getLogger(TaskScheduler.class);

    @Scheduled(fixedDelay = 10000)
    public void ScheduledMethod() {
        methodOne();
        methodTwo();
        methodThree();
    }

    @Async
    private void methodOne() {
        logger.info("Method one called  by Thread : " + Thread.currentThread().getName() + "  at " + new Date());
    }

    @Async
    private void methodTwo() {
        logger.info("Method two called  by Thread : " + Thread.currentThread().getName() + "  at " + new Date());
    }

    @Async
    private void methodThree() {
        logger.info("Method three called  by Thread : " + Thread.currentThread().getName() + "  at " + new Date());
    }
}

Вывод

Первый метод, вызываемый потоком: pool-1-thread-1 во вторник, 04 апреля, 16:32:27 IST 2017

Второй метод, вызываемый потоком: pool-1-thread-1 во вторник, 04 апреля, 16:32:27 IST 2017 г.

Третий метод, вызываемый потоком: pool-1-thread-1 во вторник, 04 апреля, 16:32:27 IST 2017 г.


person Soumitri Pattnaik    schedule 04.04.2017    source источник
comment
Некоторые примеры кода были бы полезны.   -  person Manuel    schedule 04.04.2017
comment
В дополнение к коду, пожалуйста, опубликуйте конфигурацию и образец вывода, демонстрирующий указанное поведение   -  person Bond - Java Bond    schedule 04.04.2017
comment
@Manuel добавил пример кода.   -  person Soumitri Pattnaik    schedule 04.04.2017
comment
@ Bond-JavaBond добавил образец кода в редактирование   -  person Soumitri Pattnaik    schedule 04.04.2017
comment
Ваш код просто никогда не будет работать. Прежде всего, это частные методы, а АОП работает только для общедоступных методов. Рядом с этим @Async применяется с использованием AOP для создания прокси, вы вызываете методы изнутри прокси, поэтому AOP не будет применяться. В основном ваши @Async аннотации бесполезны ...   -  person M. Deinum    schedule 04.04.2017
comment
@ M.Deinum, значит ли это, что асинхронные вызовы внутри запланированного метода не будут работать? Или для этого тоже есть какая-то конфигурация?   -  person Soumitri Pattnaik    schedule 04.04.2017
comment
Вызов асинхронных методов в том же классе не работает (независимо от того, используется ли @Scheduled или нет).   -  person M. Deinum    schedule 04.04.2017


Ответы (2)


Объяснение

Spring создает прокси вокруг вашего экземпляра. ScheduledMethod вызывает внутри 3 метода, которые не проксифицируются и, следовательно, не являются асинхронными.

cf. документация:

Если вы вызываете метод для ссылки на объект, метод вызывается непосредственно для этой ссылки на объект, как можно увидеть ниже.

См. Этот вопрос Spring AOP не работает, когда метод вызывается внутри bean-компонента для обходного пути, но лучше всего тот, который предлагается в документе The best approach (the term best is used loosely here) is to refactor your code such that the self-invocation does not happen...

Обратите внимание, что частный метод не является тоже поддерживается:

Из-за того, что структура Spring AOP основана на прокси, защищенные методы по определению не перехватываются ни для прокси JDK (где это не применимо), ни для прокси CGLIB (где это технически возможно, но не рекомендуется для целей AOP). Как следствие, любой заданный pointcut будет сопоставляться только с общедоступными методами!

Пример обходного пути

@Component
public class ServiceMethod {
    private static final Logger logger = Logger.getLogger(ServiceMethod .class);

    @Async
    public void methodOne() {
        logger.info("Method one called  by Thread : " + Thread.currentThread().getName() + "  at " + new Date());
    }

    @Async
    public void methodTwo() {
        logger.info("Method two called  by Thread : " + Thread.currentThread().getName() + "  at " + new Date());
    }

    @Async
    public void methodThree() {
        logger.info("Method three called  by Thread : " + Thread.currentThread().getName() + "  at " + new Date());
    }
}

@Component
public class TaskScheduler {
    private static final Logger logger = Logger.getLogger(TaskScheduler.class);

    @Autowired
    private ServiceMethod serviceMethod;

    @Scheduled(fixedDelay = 10000)
    public void ScheduledMethod() {
        serviceMethod.methodOne();
        serviceMethod.methodTwo();
        serviceMethod.methodThree();
    }
}
person Nicolas Labrot    schedule 04.04.2017
comment
Что, если бы я не мог или не хотел выделять отдельный класс? Единственный способ сделать это асинхронным - написать все вещи Executor вручную? Или есть другой способ сделать это Spring? - person Kevin M; 08.03.2019
comment
Можно использовать изменение времени загрузки AspectJ: docs.spring.io/spring/docs/current/spring-framework-reference/. - person Nicolas Labrot; 09.03.2019

Возможно, вы не настроили Thread Pool с дополнительным Threads для своего Scheduler.

Из документов

Если вы не предоставите атрибут размера пула, пул потоков по умолчанию будет иметь только один поток.

person Abdullah Khan    schedule 04.04.2017