SpringBoot @Async 注解无效

简介: SpringBoot @Async 注解无效
开发环境
  • SpringBoot 2.1.10.RELEASE
  • JDK 1.8
场景

在一个类的方法中,调用同类的异步方法无效,例如以下示例:

package com.nobody.controller;

import java.util.concurrent.TimeUnit;

import org.springframework.scheduling.annotation.Async;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("demo")
public class DemoController {

    @GetMapping("test")
    public void test() {
        
        asyncTask();

        System.out.println("主线程 Thread name:" + Thread.currentThread().getName());
        
    }
    
    @Async
    public void asyncTask() {
        try {
            TimeUnit.SECONDS.sleep(1);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("异步方法 Thread name:" + Thread.currentThread().getName());
    }

}

启动类也添加@EnableAsync注解

package com.nobody;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.scheduling.annotation.EnableAsync;

@SpringBootApplication
@EnableAsync
public class Application {

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

}

输出结果:

异步方法 Thread name:http-nio-9563-exec-1
主线程 Thread name:http-nio-9563-exec-1

从结果分析,明显是串行单线程执行。

原因分析

类内部方法调用时,直接进行内部调用,没有走Spring的代理类。Async注解的实现都是基于Spring的AOP,而AOP的实现是基于动态代理模式实现的。调用方法的是对象本身而不是代理对象,没有经过Spring容器。

解决方案

新建一个类,通过注解让Spring容器管理,然后在调用类中注入对象。

package com.nobody.domain;

import java.util.concurrent.TimeUnit;

import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;

@Service
public class AsyncService {
    
    @Async
    public void asyncTask() {
        try {
            TimeUnit.SECONDS.sleep(1);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("异步方法 Thread name:" + Thread.currentThread().getName());
    }


}
package com.nobody.controller;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import com.nobody.domain.AsyncService;

@RestController
@RequestMapping("demo")
public class DemoController {

    @Autowired
    private AsyncService asyncService;

    @GetMapping("test")
    public void test() {

        asyncService.asyncTask();

        System.out.println("主线程 Thread name:" + Thread.currentThread().getName());

    }


}

输出结果:

主线程 Thread name:http-nio-9563-exec-1
异步方法 Thread name:task-1

如果异步方法非要写在同一个类中,可以懒加载注入本类的实例,进行调用,例如:

package com.nobody.controller;

import java.util.concurrent.TimeUnit;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;
import org.springframework.scheduling.annotation.Async;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("demo")
public class DemoController {

    @Autowired
    @Lazy // 懒加载注入,不加会报错
    private DemoController demoController;

    @GetMapping("test")
    public void test() {

        // 通过DemoController实例调用
        demoController.asyncTask();

        System.out.println("主线程 Thread name:" + Thread.currentThread().getName());

    }

    // 方法不能是private类型,不然还是串行单线程执行
    @Async
    public void asyncTask() {
        try {
            TimeUnit.SECONDS.sleep(1);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("异步方法 Thread name:" + Thread.currentThread().getName());
    }


}

输出结果:

主线程 Thread name:http-nio-9563-exec-1
异步方法 Thread name:task-1
相关文章
|
24天前
|
并行计算 Java 数据处理
SpringBoot高级并发实践:自定义线程池与@Async异步调用深度解析
SpringBoot高级并发实践:自定义线程池与@Async异步调用深度解析
110 0
|
14天前
|
Java Spring
在使用Spring的`@Value`注解注入属性值时,有一些特殊字符需要注意
【10月更文挑战第9天】在使用Spring的`@Value`注解注入属性值时,需注意一些特殊字符的正确处理方法,包括空格、引号、反斜杠、新行、制表符、逗号、大括号、$、百分号及其他特殊字符。通过适当包裹或转义,确保这些字符能被正确解析和注入。
|
3天前
|
XML JSON Java
SpringBoot必须掌握的常用注解!
SpringBoot必须掌握的常用注解!
14 4
SpringBoot必须掌握的常用注解!
|
4天前
|
存储 缓存 Java
Spring缓存注解【@Cacheable、@CachePut、@CacheEvict、@Caching、@CacheConfig】使用及注意事项
Spring缓存注解【@Cacheable、@CachePut、@CacheEvict、@Caching、@CacheConfig】使用及注意事项
28 2
|
4天前
|
JSON Java 数据库
SpringBoot项目使用AOP及自定义注解保存操作日志
SpringBoot项目使用AOP及自定义注解保存操作日志
22 1
|
19天前
|
架构师 Java 开发者
得物面试:Springboot自动装配机制是什么?如何控制一个bean 是否加载,使用什么注解?
在40岁老架构师尼恩的读者交流群中,近期多位读者成功获得了知名互联网企业的面试机会,如得物、阿里、滴滴等。然而,面对“Spring Boot自动装配机制”等核心面试题,部分读者因准备不足而未能顺利通过。为此,尼恩团队将系统化梳理和总结这一主题,帮助大家全面提升技术水平,让面试官“爱到不能自已”。
得物面试:Springboot自动装配机制是什么?如何控制一个bean 是否加载,使用什么注解?
|
23天前
|
XML Java 数据库
Spring boot的最全注解
Spring boot的最全注解
|
25天前
|
JSON NoSQL Java
springBoot:jwt&redis&文件操作&常见请求错误代码&参数注解 (九)
该文档涵盖JWT(JSON Web Token)的组成、依赖、工具类创建及拦截器配置,并介绍了Redis的依赖配置与文件操作相关功能,包括文件上传、下载、删除及批量删除的方法。同时,文档还列举了常见的HTTP请求错误代码及其含义,并详细解释了@RequestParam与@PathVariable等参数注解的区别与用法。
|
25天前
|
Java API Spring
springBoot:注解&封装类&异常类&登录实现类 (八)
本文介绍了Spring Boot项目中的一些关键代码片段,包括使用`@PathVariable`绑定路径参数、创建封装类Result和异常处理类GlobalException、定义常量接口Constants、自定义异常ServiceException以及实现用户登录功能。通过这些代码,展示了如何构建RESTful API,处理请求参数,统一返回结果格式,以及全局异常处理等核心功能。
|
11天前
|
存储 Java 数据管理
强大!用 @Audited 注解增强 Spring Boot 应用,打造健壮的数据审计功能
本文深入介绍了如何在Spring Boot应用中使用`@Audited`注解和`spring-data-envers`实现数据审计功能,涵盖从添加依赖、配置实体类到查询审计数据的具体步骤,助力开发人员构建更加透明、合规的应用系统。