Spring @Scheduled 方法内部调用 @Async 方法

9
我正在使用Spring Boot,使用@EnableScheduling@EnableAsync
我有一个方法,它被注释为@Scheduled。 我还有几个方法,它们被注释为@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 调用,时间为 2017 年 4 月 4 日星期二下午 4:32:27 IST。

方法二由线程 pool-1-thread-1 调用,时间为 2017 年 4 月 4 日星期二下午 4:32:27 IST。

方法三由线程 pool-1-thread-1 调用,时间为 2017 年 4 月 4 日星期二下午 4:32:27 IST。


3
提供一些代码示例会有帮助。 - Manuel
1
除了代码之外,请发布展示所述行为的配置和样本输出。 - Bond - Java Bond
@Manuel 添加了示例代码。 - Soumitri Pattnaik
@Bond-JavaBond在编辑中添加了示例代码。 - Soumitri Pattnaik
3
你的代码永远不会起作用,首先,这些是私有方法,而AOP只适用于公共方法。此外,@Async是使用AOP应用的,需要创建代理,你正在从代理内部调用方法,因此AOP将不会被应用。基本上,你的@Async注解是无用的... - M. Deinum
显示剩余2条评论
2个回答

14

解释

Spring会在你的实例周围创建一个代理。 ScheduledMethod 内部调用3个方法,这些方法不被代理,因此不是异步的。

参见文档:

如果在对象引用上调用方法,则该方法将直接在该对象引用上调用,如下所示。

查看此问题Spring AOP not working, when the method is called internally within a 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 目的)。因此,任何给定的切入点都将仅与公共方法匹配!

解决方法示例

@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();
    }
}

如果我不能或者不想拆分出一个单独的类,怎么办呢?难道唯一的方法就是手写所有Executor相关的代码来实现异步吗?还是有Spring的其它方式可以实现呢? - Kevin M
可以使用AspectJ加载时织入:https://docs.spring.io/spring/docs/current/spring-framework-reference/core.html#aop-aj-ltw。 - Nicolas Labrot

5
您可能还没有为您的计划程序配置足够的线程池和线程。请参考文档。文档中指出,如果您没有提供一个pool-size属性,线程池的默认大小将只有一个线程。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接