前言在前端,我们经常会用到事件机制,比如鼠标点击事件等,而在java中,java也为我们设置了事件机制。我们先模仿一下前端的事件机制,用代码简单的实现一下鼠标点击事件。classMouse{privateListlisteners=newArrayList();私人intlistenerCallbacks=0;publicvoidaddListenerCallback(){listenerCallbacks++;}publicintgetListenerCallbacks(){返回listenerCallbacks;}publicvoidaddListener(MouseListenerlistener){listeners.add(listener);}publicvoidclick(){System.out.println("点击!");for(MouseListenerlistener:listeners){listener.onClick(this);}}}interfaceMouseListener{publicvoidonClick(Mousesource);}publicclassEventBasedTest{@Testpublicvoidtest(){Mousemouse=newMouse();mouse.addListener(newMouseListener(){@OverridepublicvoidonClick(Mousemouse){System.out.println("Listener#1called");mouse.addListenerCallback();}});mouse.addListener(newMouseListener(){@OverridepublicvoidonClick(鼠标emouse){System.out.println("Listener#2called");鼠标.addListenerCallback();}});鼠标点击();}}打印输出看起来像Clicked!Listener#1calledListener#2calledPreparetoimplementJava的Event需要实现事件类的三个部分:事件发布者的载体:发布事件。Listener:监听并处理事件。我们具体通过一个业务场景来实现java事件。如果我们有需求,在用户创建成功后,会发邮件给用户。这里有两件事要做:创建一个用户给用户发送一封电子邮件这很简单。@ServicepublicclassEmailService{@TransactionalpublicvoidsendEmail(Stringemail){//发送邮件}}@ServicepublicclassUserService{privatefinalEmailServiceemailService;私有最终UserRepositoryuserRepository;publicUserService(EmailServiceemailService,UserRepositoryuserRepository){this.emailService=emailService;this.userRepository=userRepository;}@TransactionalpublicUsercreateUser(Useruser){UsernewUser=this.userRepository.save(user);this.emailService.sendEmail(user.getEmail());返回新用户;}}但是这个实现是有问题的。我们想一想,这个功能的核心是创建用户,发送邮件是一个副作用(发送邮件不能影响用户创建),如果这两个操作放在一个事务中会有什么问题?其实很明显,如果在创建用户的时候抛出异常,事务回滚,方法提前退出,那么就不会发邮件,这是正常的。但是下面两种场景是不能接受的:邮件发送失败,事务回滚,用户创建失败。如果邮件发送成功,事务提交失败,用户收到邮件,但是创建用户失败,这不是我们想要的。我们首先通过事件机制对业务进行解耦,同时达到易扩展的原则。事件类事件类是事件的载体,即发布者发布的东西和监听者接收的东西只能是事件,而我们要在发布者和监听者之间传递的东西也定义在我们在事件类中的习惯。只要继承了ApplicationEvent,就是一个事件类。公共类UserCreatedEvent{私人最终用户用户;publicUserCreatedEvent(Useruser){this.user=user;}publicUsergetUser(){返回用户;}}PublisherPublisher发布事件类,我们可以调用ApplicationEventPublisher的publishEvent()方法发布事件类。@ServicepublicclassCustomerService{privatefinalUserRepositoryuserRepository;私人最终ApplicationEventPublisherapplicationEventPublisher;publicCustomerService(UserRepositoryuserRepository,ApplicationEventPublisherapplicationEventPublisher){this.userRepository=userRepository;this.applicationEventPublisher=applicationEventPublisher;}@TransactionalpublicCustomercreateCustomer(Useruser){UsernewUser=this.userRepository.save(user);finalUserCreatedEvent事件=newUserCreatedEvent(newUser);this.applicationEventPublisher.publishEvent(事件);返回新用户;}}Listener监听器接受发布者发布的事件并进行处理。实现也很简单,在方法上加上@EventListener注解,接收事件类参数即可。@ComponentpublicclassUserCreatedEventListener{privatefinalEmailServiceemailService;publicUserCreatedEventListener(EmailServiceemailService){this.emailService=emailService;}@EventListenerpublicvoidprocessUserCreatedEvent(UserCreatedEventevent){this.emailService.sendEmail(event.getUser().getEmail(;}}如果我们有两个监听器,我们可以通过@Order注解控制两个监听器的执行顺序。虽然我们用EventListener方法对业务代码进行了解耦,看起来很高端,但是如果有兴趣研究源码,可以发现这仍然是底层的普通方法调用,并没有达到我们的目的.如果我们在Listener方法中加上@Async使其异步执行,这和不同的异步调用没有区别,可能会导致先发送短信后才提交事务,这也不符合我们提出的要求事务隔离使用@TransactionalEventListener注解,TransactionalEventListener注解是对EventListener的增强,注解的方法可以在事务的不同阶段触发执行。如果事件未在活动事务中发布,除非fallbackExecution()标志显式设置为true,否则事件将被丢弃;如果事务正在运行,则根据其TransactionPhase处理事件。AFTER_COMMIT-默认设置,事务提交后执行AFTER_ROLLBACK-事务回滚后执行AFTER_COMPLETION-事务完成后执行(无论成功与否)BEFORE_COMMIT-事务提交前执行@ComponentpublicclassUserCreatedEventListener{privatefinalEmailServiceemailService;publicUserCreatedEventListener(EmailServiceemailService){this.emailService=emailService;}@TransactionalEventListenerpublicvoidprocessUserCreatedEvent(UserCreatedEventevent){this.emailService.sendEmail(event.getUser().getEmail());}}这样,我们在交易提交方法后发送邮件。即保证发送邮件的功能不影响保存用户的功能,发送邮件的功能和保存用户的功能不会没有关联。总结对修改关闭,对扩展开放。参考:Spring5源码分析——Spring框架中事件和监听器TransactionalEventListener的使用场景和实现原理,最后给隐藏一个大坑的SpringEvent初步解释订阅发布模式和观察者模式的区别