Spring异步编程 | 你的@Async就真的异步吗?异步历险奇遇记
点击上方“java进阶架构师”,选择右上角“置顶公众号”
20大进阶架构专题每日送达
引言有点长
前端的宝宝会用ajax,用异步编程到快乐的不行~ 我们java也有异步,用起来比他们还快乐~ 我们biaji一个注(gǒupí)解(gāoyào),也是快乐风男...
且看下面的栗子:
注册一个用户,给他的账户初始化积分(也可以想象成注册奖励),再给用户发个注册通知短信,再发个邮件,(只是举栗子,切莫牛角大法),这样一个流程,短信和邮件我觉得完全可以拆分出来,没必要拖在在主流程上来(再补充上事务[ACID:原子性,一致性,隔离性,持久性]就好了):
今天就这点业务,我在暗想,这不是一个注解就搞掂的嘛~~~ 哈哈哈
结果不是想象中的那么完美~~ 来看我的异(dind)步(ding)历险记
我的首发原创博客地址:你的@Async就真的异步吗 ☞ 异步历险奇遇记[1]
https://juejin.im/post/5d47a80a6fb9a06ad3470f9a 里面有gitHub项目地址,关注我,里面实战多多哦~
奇遇一 循环依赖异常
看code:
@Service
public class UserServiceImpl implements UserService {
@Autowired
UserService userService;
@Override
@Transactional(rollbackFor = Exception.class, propagation = Propagation.REQUIRES_NEW)
public int save(UserDTO userDTO) {
User user = new User();
BeanCopyUtils.copy(userDTO, user);
int insert = userMapper.insert(user);
System.out.println("User 保存用户成功:" + user);
userService.senMsg(user);
userService.senEmail(user);
return insert;
}
@Async
@Override
public Boolean senMsg(User user) {
try {
TimeUnit.SECONDS.sleep(2);
System.out.println("发送短信中:.....");
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "给用户id:" + user.getId() + ",手机号:" + user.getMobile() + "发送短信成功");
return true;
}
@Async
@Override
public Boolean senEmail(User user) {
try {
TimeUnit.SECONDS.sleep(3);
System.out.println("发送邮件中:.....");
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "给用户id:" + user.getId() + ",邮箱:" + user.getEmail() + "发送邮件成功");
return true;
}
结果:启动不起来,Spring循环依赖问题。 Spring不是解决了循环依赖问题吗,它是支持循环依赖的呀?怎么会呢?
不可否认,在这之前我也是这么坚信的,倘若你目前也和我有一样坚挺的想法,那就让异常UnsatisfiedDependencyException,has been injected into other beans [userServiceImpl] in its raw version as part of a circular reference,,来鼓励你,拥抱你, 就是这么的不给面子,赤裸裸的circular reference。
谈到Spring Bean的循环依赖,有的小伙伴可能比较陌生,毕竟开发过程中好像对循环依赖这个概念无感知。其实不然,你有这种错觉,那是因为你工作在Spring的襁褓中,从而让你“高枕无忧”~ 其实我们的代码中肯定被我们写了循环依赖,比如像这样:
@Service
public class AServiceImpl implements AService {
@Autowired
private BService bService;
...
}
@Service
public class BServiceImpl implements BService {
@Autowired
private AService aService;
...
}
通过实验总结出,出现使用@Async导致循环依赖问题的必要条件:
已开启@EnableAsync的支持
@Async注解所在的Bean被循环依赖了
奇遇二 异步失效异常
那么既然不能循环依赖,我们就不循环依赖,我们这么来:
@Service
public class UserServiceImpl implements UserService {
@Autowired
UserMapper userMapper;
@Autowired
SendService sendService;
@Override
@Transactional()
public int save(UserDTO userDTO) {
User user = new User();
BeanCopyUtils.copy(userDTO, user);
int insert = userMapper.insert(user);
System.out.println("User 保存用户成功:" + user);
this.senMsg(user);
this.senEmail(user);
return insert;
}
@Async
@Override
public Boolean senMsg(User user) {
System.out.println(Thread.currentThread().getName() + "给用户id:" + user.getId() + ",手机号:" + user.getMobile() + "发送短信成功");
return true;
}
@Async
@Override
public Boolean senEmail(User user) {
System.out.println(Threa