TransactionalEventListener使用场景以及实现原理,最后要躲个大坑

这篇文章主要讲解TransactionalEventListener是怎样工作的?适合在什么场景,能解决哪些问题?以及和EventListener不同之处。

示例

这里举个业务场景,假如我们有个需求,用户创建成功后给用户发送一个邮件。这里有两个事情要做:

  1. 创建用户
  2. 给用户发送邮件

对于这种需求,我们可能会不假思索的有以下实现。

@Entity
public class User {
   
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    private String name;
    private String email;

    public User() {
   }
    ...
    //getters
    //equals and hashcode
}

为User创建个Repository

public interface UserRepository extends JpaRepository<User, Long> {
   }
@Service
public class EmailService{
   

  @Transactional
  public void sendEmail(String email) {
   
    //send email
  }
}
@Service
public class UserService {
   
  private final EmailService emailService;
  private final UserRepository userRepository;

  public UserService(EmailService emailService, UserRepository userRepository) {
   
    this.emailService = emailService;
    this.userRepository = userRepository;
  }

  @Transactional
  public User createUser(User user) {
   
    User newUser = userRepository.save(user);
    emailService.sendEmail(user.getEmail());
    return newUser;
  }
}

对于上面的实现,是最容易实现的,但这种实现是有问题的。我们想一下,这个功能的核心是创建用户,而发送邮件是一个副作用(发送邮件不能影响用户的创建),如果把这两个操作放在一个事务中会有什么问题?其实很明显,如果创建用户时抛出异常,事务回滚,方法提前退出,那么也不会发送邮件,这是正常的。但是下面两个场景是不可接受的:

  1. 如果邮件发送失败,事务发生回滚,用户创建失败。
  2. 如果邮件发送成功后,事务提交失败,这下就尴尬了,用户收到了邮件,可是用户创建失败。

虽然这些情况出现的概率很小,但作为对自己有要求的程序猿,这是不可容忍的,我们要对自己写的业务负责。

好了,我们对上面的实现做个重构,直接将创建用户和发送邮件的业务代码拆开,使用Spring application event的方式解耦实现。

修改后的Service是这样的

@Service
public class CustomerService {
   

    private final UserRepository userRepository;
    private final ApplicationEventPublisher applicationEventPublisher;

    public CustomerService(UserRepository userRepository, ApplicationEventPublisher applicationEventPublisher) 
  • 10
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 5
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值