spring 的 ApplicationListener 事件监听使用了观察者设计模式;

观察者设计模式需要以下几个角色:

  1. 事件
  2. 观察者
  3. 事件发布者

定义事件

  1. // 实现 ApplicationEvent 接口, 定义事件
  2. public class ConnectEventVO extends ApplicationEvent {
  3. @Getter
  4. private EventVO eventVO;
  5. /**
  6. * Create a new ApplicationEvent.
  7. *
  8. * @param source the object on which the event
  9. *
  10. initially occurred (never {@code null})
  11. */
  12. public ConnectEventVO(Object source, EventVO eventVO) {
  13. super(source);
  14. this.eventVO = eventVO;
  15. }
  16. }

事件发布

  1. @Resource
  2. private ApplicationEventPublisher eventPublisher;
  3. public void onWebsocketClose(NettyClient ws, int code, Integer tryNum) {
  4. log.error("@DeribitMarketListener Channel Close!!!.code:{},tryNum:{}",
  5. code, tryNum);
  6. final EventVO eventVO = new EventVO(EXCHANGE_DERIBIT.getExchangeName(),
  7. CLOSE.toString(), this);
  8. eventPublisher.publishEvent(new ConnectEventVO(this, eventVO));
  9. }

定义观察者

  1. @Component("connectEventListener")
  2. public class ConnectEventListener {
  3. @Resource
  4. private ConnectMonitor connectMonitor;
  5. /**
  6. * 处理连接 close 事件
  7. *
  8. * @param connectEventVO 事件
  9. */
  10. @EventListener(condition = "#connectEventVO.eventVO.connectStatus == 'OPEN'")
  11. public void handleOpenEvent(ConnectEventVO connectEventVO) {
  12. log.info("@ConnectEventListener.handleOpenEvent eventVO:{}",
  13. connectEventVO.getEventVO());
  14. connectMonitor.resetConnectCount(connectEventVO.getEventVO());
  15. }
  16. /**
  17. * 处理连接 open 事件
  18. *
  19. * @param connectEventVO 事件
  20. */
  21. @EventListener(condition = "#connectEventVO.eventVO.connectStatus == 'CLOSE'")
  22. public void handleCloseEvent(ConnectEventVO connectEventVO) {
  23. EventVO eventVO = connectEventVO.getEventVO();
  24. log.info("@ConnectEventListener.handleCloseEvent exchange:{}",
  25. eventVO.getExchange());
  26. connectMonitor.reconnect(eventVO);
  27. }
  28. }

源码相关

事件发布后,拿到所有事件监听者,匹配到后。回调监听者,如果有 async 注解,回调方法会异步执行;

  1. // @see SimpleApplicationEventMulticaster
  2. public void multicastEvent(final ApplicationEvent event,
  3. @Nullable ResolvableType eventType) {
  4. ResolvableType type = (eventType != null ? eventType :
  5. resolveDefaultEventType(event));
  6. for (final ApplicationListener<?> listener :
  7. getApplicationListeners(event, type)) {
  8. Executor executor = getTaskExecutor();
  9. if (executor != null) {
  10. executor.execute(() -> invokeListener(listener, event));
  11. }
  12. else {
  13. invokeListener(listener, event);
  14. }
  15. }
  16. }

其他

  1. @Target({ElementType.METHOD, ElementType.ANNOTATION_TYPE})
  2. @Retention(RetentionPolicy.RUNTIME)
  3. @Documented
  4. public @interface EventListener {
  5. /**
  6. * Alias for {@link #classes}.
  7. */
  8. @AliasFor("classes")
  9. Class<?>[] value() default {};
  10. /**
  11. * The event classes that this listener handles.
  12. * <p>If this attribute is specified with a single value, the
  13. * annotated method may optionally accept a single parameter.
  14. * However, if this attribute is specified with multiple values,
  15. * the annotated method must <em>not</em> declare any parameters.
  16. */
  17. @AliasFor("value")
  18. Class<?>[] classes() default {};
  19. /**
  20. * Spring Expression Language (SpEL) attribute used for making the
  21. * event handling conditional.
  22. * <p>Default is {@code ""}, meaning the event is always handled.
  23. * <p>The SpEL expression evaluates against a dedicated context that
  24. * provides the following meta-data:
  25. * <ul>
  26. * <li>{@code #root.event}, {@code #root.args} for
  27. * references to the {@link ApplicationEvent} and method arguments
  28. * respectively.</li>
  29. * <li>Method arguments can be accessed by index. For instance the
  30. * first argument can be accessed via {@code #root.args[0]}, {@code #p0}
  31. * or {@code #a0}. Arguments can also be accessed by name if that
  32. * information is available.</li>
  33. * </ul>
  34. */
  35. String condition() default "";
  36. }

@EventListener 的 condition 是支持 SpEL 表达式的,这就会很灵活。

  1. @Component
  2. public class FooPredicate implements Predicate<FooEvent> {
  3. public boolean test(FooEvent event) {...}
  4. }
  5. // 可以调用 spring bean 的某个方法,作为前置条件
  6. @EventListener(condition="@fooPredicate.test(#event)")
  7. public void handle(FooEvent event) {
  8. System.out.println();
  9. }

TransactionalEventListener

使用这个listener 可以在事务提交后,做一些定制化的逻辑