前言
在前端中咱们常常应用事件机制,如鼠标点击事件等,而在java中,java也为咱们设置了事件机制。
咱们先模拟前端的事件机制,应用代码进行简略实现鼠标点击事件。
class Mouse { private List<mouselistener> listeners = new ArrayList<mouselistener>(); private int listenerCallbacks = 0; public void addListenerCallback() { listenerCallbacks++; } public int getListenerCallbacks() { return listenerCallbacks; } public void addListener(MouseListener listener) { listeners.add(listener); } public void click() { System.out.println("Clicked !"); for (MouseListener listener : listeners) { listener.onClick(this); } }} interface MouseListener { public void onClick(Mouse source);}public class EventBasedTest { @Test public void test() { Mouse mouse = new Mouse(); mouse.addListener(new MouseListener() { @Override public void onClick(Mouse mouse) { System.out.println("Listener#1 called"); mouse.addListenerCallback(); } }); mouse.addListener(new MouseListener() { @Override public void onClick(Mouse mouse) { System.out.println("Listener#2 called"); mouse.addListenerCallback(); } }); mouse.click(); }}
打印输出如下所示
Clicked !Listener#1 calledListener#2 called
筹备
实现java的Event须要实现三局部
- 事件类:事件的载体。
- 发布者:公布事件。
- 监听者:监听并处理事件。
咱们通过一个业务场景来具体实现java事件。如果咱们有个需要,用户创立胜利后给用户发送一个邮件。这里有两个事件要做:
- 创立用户
给用户发送邮件
这个很简略。@Servicepublic class EmailService { @Transactional public void sendEmail(String email) { //send email }}@Servicepublic 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; }}
但这种实现是有问题的。咱们想一下,这个性能的外围是创立用户,而发送邮件是一个副作用(发送邮件不能影响用户的创立),如果把这两个操作放在一个事务中会有什么问题?其实很显著,如果创立用户时抛出异样,事务回滚,办法提前退出,那么也不会发送邮件,这是失常的。然而上面两个场景是不可承受的:
- 如果邮件发送失败,事务产生回滚,用户创立失败。
- 如果邮件发送胜利后,事务提交失败,用户收到了邮件,可是用户创立失败,这不是咱们所心愿的。
咱们通过event机制进行业务解耦,同时达到易扩大的准则。
事件类
事件类为事件的载体,也就是所发布者公布的货色和监听者接管的货色都只能是事件,咱们所要在发布者和监听者之间传递的货色,也定义在咱们的自定义事件类里。
只有继承ApplicationEvent
,便是一个事件类。
public class UserCreatedEvent { private final User user; public UserCreatedEvent(User user) { this.user = user; } public User getUser() { return user; }}
发布者
发布者公布事件类,咱们能够调用ApplicationEventPublisher
的publishEvent()
办法公布事件类。
@Servicepublic class CustomerService { private final UserRepository userRepository; private final ApplicationEventPublisher applicationEventPublisher; public CustomerService(UserRepository userRepository, ApplicationEventPublisher applicationEventPublisher) { this.userRepository = userRepository; this.applicationEventPublisher = applicationEventPublisher; } @Transactional public Customer createCustomer(User user) { User newUser = userRepository.save(user); final UserCreatedEvent event = new UserCreatedEvent(newUser); applicationEventPublisher.publishEvent(event); return newUser; }}
监听者
监听者承受发布者公布的事件并解决。
实现也很简略,在办法上退出@EventListener
注解并接管事件类参数。
@Componentpublic class UserCreatedEventListener { private final EmailService emailService; public UserCreatedEventListener(EmailService emailService) { this.emailService = emailService; } @EventListener public void processUserCreatedEvent(UserCreatedEvent event) { emailService.sendEmail(event.getUser().getEmail()); }}
如果咱们有两个监听者,能够通过@Order注解来管制两个监听者的执行程序。
尽管咱们用EventListener的形式解耦了业务代码,看着很高大上,然而有趣味钻研源码就能够发现,这在底层还是一个一般的办法调用,这并没有达到咱们的目标。
如果咱们在Listener办法上加@Async让其异步执行,这跟不同的异步调用并无二异,可能会导致先发送短信后提交事务。这也不合乎咱们提出的要求。
事务隔离
应用@TransactionalEventListener注解,TransactionalEventListener注解是对EventListener的加强,被注解的办法能够在事务的不同阶段去触发执行,如果事件未在激活的事务中公布,除非显式设置了 fallbackExecution() 标记为true,否则该事件将被抛弃;如果事务正在运行,则依据其 TransactionPhase 解决该事件。
- AFTER_COMMIT - 默认设置,在事务提交后执行
- AFTER_ROLLBACK - 在事务回滚后执行
- AFTER_COMPLETION - 在事务实现后执行(不论是否胜利)
- BEFORE_COMMIT - 在事务提交前执行
@Componentpublic class UserCreatedEventListener { private final EmailService emailService; public UserCreatedEventListener(EmailService emailService) { this.emailService = emailService; } @TransactionalEventListener public void processUserCreatedEvent(UserCreatedEvent event) { emailService.sendEmail(event.getUser().getEmail()); }}
这样,咱们在事务提交后执行发送邮件办法。即保障了发送邮件性能不对保留用户性能进行影响,也不会使得发送邮件性能和保留用户性能没有关联。
总结
对批改敞开,对扩大凋谢。