SpringBoot中使用@Async注解失效场景及说明
作者:安腾斯科技
场景一:调用者与被调用者在同一个类中
当调用 @Async 注解的方法的类和被调用的方法在同一个类中时,@Async 注解不会生效。因为 Spring 的 AOP 代理是基于接口的,对于同一个类中的方法调用,不会经过代理,因此 @Async 注解不会被处理。
例如:
@Service public class MyService { @Async public void asyncMethod() { // 模拟耗时操作 try { System.out.println("开始异步执行"); Thread.sleep(5000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("Async method executed."); } public void callAsyncMethod() { asyncMethod(); // 直接调用,不会异步执行 } }
场景二:配置类未启用异步支持
如果配置类中没有启用异步支持,即没有使用 @EnableAsync 注解,那么 @Async 注解同样不会生效。
例如:
// 没有使用 @EnableAsync 注解,因此不会启用异步支持 @Configuration public class AsyncConfig { // ... 其他配置 ... } @Service public class MyService { @Async public void asyncMethod() { // 模拟耗时操作 try { Thread.sleep(5000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("Async method executed."); } }
场景三:方法不是 public 的
@Async 注解的方法必须是 public 的,否则不会被 Spring AOP 代理捕获,导致异步执行不生效。
例如:
@Service public class MyService { @Async // 但这个方法不是 public 的,所以 @Async 不会生效 protected void asyncMethod() { // 模拟耗时操作 try { Thread.sleep(5000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("Async method executed."); } public void callAsyncMethod() { asyncMethod(); // 直接调用,但由于 asyncMethod 不是 public 的,因此不会异步执行 } }
场景四:线程池未正确配置
在使用 @Async 注解时,如果没有正确配置线程池,可能会遇到异步任务没有按预期执行的情况。例如,线程池被配置为只有一个线程,且该线程一直被占用,那么新的异步任务就无法执行。
例如:
@Configuration @EnableAsync public class AsyncConfig implements AsyncConfigurer { @Override public Executor getAsyncExecutor() { // 创建一个只有一个线程的线程池,这会导致并发问题 ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor(); executor.setCorePoolSize(1); executor.setMaxPoolSize(1); executor.setQueueCapacity(10); executor.setThreadNamePrefix("Async-"); executor.initialize(); return executor; } // ... 其他配置 ... } @Service public class MyService { @Async public void asyncMethod() { // 模拟耗时操作 try { Thread.sleep(5000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("Async method executed."); } }
场景五:异常处理不当
如果在异步方法中抛出了异常,并且没有妥善处理,那么这个异常可能会导致任务失败,而调用者可能无法感知到异常的发生。
例如:
@Service public class MyService { @Async public void asyncMethod() { // 模拟一个可能会抛出异常的耗时操作 throw new RuntimeException("Async method exception"); } } // 调用者 @Service public class CallerService { @Autowired private MyService myService; public void callAsyncMethod() { myService.asyncMethod(); // 调用异步方法,但如果该方法抛出异常,调用者不会立即感知到 } }
场景六:Spring代理未生效
如果通过 new 关键字直接创建了服务类的实例,而不是通过 Spring 容器来获取,那么 Spring 的 AOP 代理将不会生效,导致 @Async 注解无效。
例如:
@Service public class MyService { @Async public void asyncMethod() { // 模拟耗时操作 try { Thread.sleep(5000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("Async method executed."); } } public class SomeNonSpringClass { public void someMethod() { MyService myService = new MyService(); // 直接通过 new 创建 MyService 实例,不会经过 Spring 代理 myService.asyncMethod(); // 这里 @Async 不会生效 } }
场景七:使用 @Transactional 与 @Async 同时注解方法,导致事务失效
在同一个方法上同时使用 @Transactional 和 @Async 注解可能会导致问题。由于 @Async 会导致方法在一个新的线程中执行,而 @Transactional 通常需要在一个由 Spring 管理的事务代理中执行,这两个注解的结合使用可能会导致事务管理失效或行为不可预测。此种场景不会导致@Async注解失效,但是会导致@Transactional注解失效,也就是事务失效。
例如:
@Service public class MyService { @Autowired private MyRepository myRepository; // 错误的用法:同时使用了 @Transactional 和 @Async @Transactional @Async public void asyncTransactionalMethod() { // 模拟一个数据库操作 myRepository.save(new MyEntity()); // 模拟可能抛出异常的代码 if (true) { throw new RuntimeException("Database operation failed!"); } } } @Repository public interface MyRepository extends JpaRepository<MyEntity, Long> { // ... } @Entity public class MyEntity { // ... 实体类的属性和映射 ... }
总结一下 :
绝大多数人会遇到的坑点主要会集中在没有配置自定义线程池、异步方法在同一个类中调用、事务不起作用这几个问题上。所以,万金油的写法还是专门定义一个AsyncService,将异步方法都写在里面,需要使用的时候,就在其他类将其注入即可。
以上就是SpringBoot中使用@Async注解失效场景及说明的详细内容,更多关于SpringBoot @Async注解失效的资料请关注脚本之家其它相关文章!