Java中@Async异步失效的问题
最近在项目某个方法使用@Async注解,但是该方法还是同步执行了,异步不起作用,到底是什么原因呢?
伪代码如下:
@Slf4j
@Service
public class UserService {
@Async
public void async(String value) {
log.info("async:" + value);
}
}
这个问题还是比较有意思的,今天这篇文章总结了@Async 注解失效的 9 种场景,希望对你会有所帮助。
1.未使用@EnableAsync 注解
在 Spring 中要开启@Async 注解异步的功能,需要在项目的启动类,或者配置类上,使用@EnableAsync注解。
例如:
@EnableAsync
@SpringBootApplication(exclude = {DataSourceAutoConfiguration.class})
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
@EnableAsync注解相当于一个开关,控制是否开启@Async注解异步的功能,默认是关闭的。
如果在项目的启动类上没使用@EnableAsync 注解,则@Async 注解异步的功能不生效。
2.内部方法调用
我们在日常开发中,经常需要在一个方法中调用另外一个方法,例如:
@Slf4j
@Service
public class UserService {
public void test() {
async("test");
}
@Async
public void async(String value) {
log.info("async:{}", value);
}
}
这个示例中,在 UserService 类中的 test()方法中调用了 async()方法。
如果在 controller 中@Autowired 了 UserService 类的对象,调用了它的 test()方法,则 async()异步的功能会失效。
我们知道 Spring 通过@Async 注解实现异步的功能,底层其实是通过 Spring 的AOP实现的,也就是说它需要通过JDK 动态代理或者cglib,生成代理对象。
异步的功能,是在代理对象中增加的,我们必须调用代理对象的 test()方法才行。
而在类中直接进行方法的内部调用,在 test()方法中调用 async()方法,调用的是该类原对象的 async 方法,相当于调用了 this.async()方法,而并非 UserService 代理类的 async()方法。
因此,像这种内部方法调用,@Async 注解的异步功能会失效。
3.方法非 public
在 Java 中有 4 种权限修饰符
public:所有类都可以访问。private:只能同一个类访问。protected:同一个类,同一个包下的其他类,不同包下的子类可以访问。默认修饰符:同一个类,同一个包下的其他类可以访问。
在实际工作中,我们使用频率最高的可能是 public 和 private 了。
如果我在定义 Service 类中的某个方法时,有时把权限修饰符定义错了,例如:
@Slf4j
@Service
public class UserService {
@Async
private void async(String value) {
log.info("async:{}", value);
}
}
这个例子中将 UserService 类的 async()方法的权限修饰符定义成了 private 的,这样@Async 注解也会失效。
因为 private 修饰的方法,只能在 UserService 类的对象中使用。
而@Async 注解的异步功能,需要使用 Spring 的 AOP 生成 UserService 类的代理对象,该代理对象没法访问 UserService 类的 private 方法,因此会出现@Async 注解失效的问题。
4.方法返回值错误
我们在写一个新的方法时,经常需要定义方法的返回值。
返回值可以是 void、int、String、User 等等,但如果返回值定义错误,也可能会导致@Async 注解的异步功能失效。
例如:
@Service
public class UserService {
@Async
public String async(String value) {
log.info("async:{}", value);
return value;
}
}
UserService 类的 async 方法的返回值是 String,这种情况竟然会导致@Async 注解的异步功能失效。
在 AsyncExecutionInterceptor 类的 invoke()方法,会调用它的父类 AsyncExecutionAspectSupport 中的 doSubmit 方法,该方法时异步功能的核心代码,如下:

从图中看出,@Async 注解的异步方法的返回值,要么是 Future,要么是 null。
因此,在实际项目中,如果想要使用@Async 注解的异步功能,相关方法的返回值必须是void或者Future。
5.方法用 static 修饰了
有时候,我们的方法会使用 static 修饰,这样在调用的地方,可以直接使用类名.方法名,访问该方法了。
但如果在@Async 方法上加了 static 修饰符,例如:
@Slf4j
@Service
public class UserService {
@Async
public static void async(String value) {
log.info("async:{}", value);
}
}
这时@Async 的异步功能会失效,因为这种情况 idea 会直接报错:Methods annotated with ‘@Async’ must be overridable 。
使用@Async 注解声明的方法,必须是能被重写的,很显然 static 修饰的方法,是类的静态方法,是不允许被重写的。
因此这种情况下,@Async 注解的异步功能会失效。
6.方法用 final 修饰
在 Java 种 final 关键字,是一个非常特别的存在。
用 final 修饰的类,没法被继承。
用 final 修饰的方法,没法被重写。
用 final 修饰的变量,没法被修改。
如果 final 使用不当,也会导致@Async 注解的异步功能失效,例如:
@Slf4j
@Service
public class UserService {
public void test() {
async("test");
}
@Async
public final void async(String value) {
log.info("async:{}", value);
}
}
这种情况下 idea 也会直接报错:Methods annotated with ‘@Async’ must be overridable 。
因为使用 final 关键字修饰的方法,是没法被子类重写的。
因此这种情况下,@Async 注解的异步功能会失效。
7.业务类没加@Service 注解
有时候,我们在新加 Service 类时,会忘了加@Service注解,例如:
@Slf4j
//@Service
public class UserService {
@Async
public void async(String value) {
log.info("async:{}", value);
}
}
@Service
public class TestService {
@Autowired
private UserService userService;
public void test() {
userService.async("test");
}
}
这种情况下,@Async 注解异步的功能也不会生效。因为 UserService 类没有使用@Service、@Component 或者@Controller 等注解声明,该类不会被 Spring 管理,因此也就无法使用 Spring 的异步功能。
8.自己 new 的对象
在项目中,我们经常需要 new 一个对象,然后对他赋值,或者调用它的方法。
但如果 new 了一个 Service 类的对象,可能会出现一些意想不到的问题,例如:
@Slf4j
@Service
public class UserService {
@Async
public void async(String value) {
log.info("async:{}", value);
}
}
@Service
public class TestService {
public void test() {
UserService userService = new UserService();
userService.async("test");
}
}
在 TestService 类的 test()方法中,new 了一个 UserService 类的对象,然后调用该对象的 async()方法。
很显然这种情况下,async()方法只能同步执行,没法异步执行。
因为在项目中,我们自己 new 的对象,不会被 Spring 管理,因此也就无法使用 Spring 的异步功能。
不过我们可以通过BeanPostProcessor类,将创建的对象手动注入到 Spring 容器中。
9.Spring 无法扫描异步类
我们在 Spring 项目中可以使用@ComponentScan注解指定项目中扫描的包路径,例如:
@ComponentScan({"com.susan.demo.service1"})
@SpringBootApplication(exclude = {DataSourceAutoConfiguration.class})
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
项目中 com.susan.demo.service1 这个路径是不存在的,会导致@Async 注解异步的功能失效。
同时如果@ComponentScan 注解定义的路径,没有包含你新加的 Servcie 类的路径,@Async 注解异步的功能也会失效。
以上关于Java中@Async异步失效的问题的文章就介绍到这了,更多相关内容请搜索码云笔记以前的文章或继续浏览下面的相关文章,希望大家以后多多支持码云笔记。
如若内容造成侵权/违法违规/事实不符,请将相关资料发送至 admin@mybj123.com 进行投诉反馈,一经查实,立即处理!
重要:如软件存在付费、会员、充值等,均属软件开发者或所属公司行为,与本站无关,网友需自行判断
码云笔记 » Java中@Async异步失效的问题

微信
支付宝