Spring 中的@Async在 Service 类中不起作用?

@Async in Spring doesn't work in Service class?

提问人:jnemecz 提问时间:10/14/2016 最后编辑:Jason Lawjnemecz 更新时间:9/28/2020 访问量:13663

问:

@Async独立 Spring Boot 应用程序中带注释的类中的方法不会异步运行。我做错了什么?@Service

当我直接从主类(注释)运行相同的方法时,它可以工作。例:@SpringBootApplication

主类

@SpringBootApplication
@EnableAsync
public class Application implements CommandLineRunner {

    @Override
    public void run(String... args) throws Exception {
        // here when I call downloadAnSave() it runs asynchronously...
        // but when I call downloadAnSave() via downloadAllImages() it does not run asynchronously...
    }

}

和我的服务类(这里的异步行为不起作用):

@EnableAsync
@Service
public class ImageProcessorService implements IIMageProcessorService {

    public void downloadAllImages(Run lastRun) {
        // this method calls downloadAnSave() in loop and should run asynchronously....
    }

    @Async
    @Override
    public boolean downloadAnSave(String productId, String imageUrl) {
        //
    }

}
java 异步 spring-boot

评论

2赞 g00glen00b 10/14/2016
控制器中 Spring Boot @Async方法的可能重复项正在同步执行
0赞 g00glen00b 10/14/2016
问题本身似乎并不完全重复,但同样的答案(和评论)适用于这里。

答:

25赞 aviad 10/14/2016 #1

从同一类中调用异步方法将触发原始方法,而不是截获的方法。 您需要使用 async 方法创建另一个服务,并从您的服务调用它。

Spring 使用通用注解为您创建的每个服务和组件创建一个代理。只有那些代理包含由方法注释(如异步)定义的所需行为。因此,不是通过代理而是通过原始裸类调用这些方法不会触发这些行为。

评论

1赞 jnemecz 10/14/2016
谢谢,我会尝试的。为了澄清和我的信息,您能解释一下“会触发原始方法而不是被拦截的方法”是什么意思吗?谢谢。
0赞 Jan Siekierski 7/12/2019
你也可以自我注射,它会起作用,但我认为不建议这样做,因为它通常会违反单一责任原则
0赞 Jason Law 9/6/2019
您能解释一下为什么自我注入会打破“单一责任原则”吗?
1赞 apr 12/20/2021
多谢。在另一项服务中完美运行
3赞 Torino 9/28/2020 #2

解决方法是:

@EnableAsync
@Service("ip-service")
public class ImageProcessorService implements IIMageProcessorService {
    
    @Autowired
    @Qualifier("ip-service")
    ImageProcessorService ipService;

    public void downloadAllImages(Run lastRun) {
        // this method calls downloadAnSave() in loop and should run asynchronously....
        ipService.downloadAnSave(productId, imageUrl);
    }

    @Async
    @Override
    public boolean downloadAnSave(String productId, String imageUrl) {
        //
    }
}

使用这种方法,您可以调用代理的方法,而不是类实例。 相同的方法可以与使用代理的其他工具一起使用,例如@Transactional等。

评论

0赞 AlexS 2/28/2022
它在最新的Springboot版本中不起作用,并说:“不鼓励依赖循环引用,默认情况下禁止它们。
1赞 Markus Mangei 7/20/2022
可以通过将 @ Lazy 添加到 @ Autowired 属性来避免此错误。