Spring的自定义事件广播机制, 能非常好的将我们的业务代码解耦, 提升代码的可读性, 学习一下, 没坏处

1. 最基础的使用

  • 下面通过三步, 实现了最基础的Spring事件广播

    自定义事件类

    1. @Getter
    2. public class MyEvent extends ApplicationEvent { //也可以不继承
    3. public LiveRoomTimeChangeEvent(Object source) {
    4. super(source);
    5. }
    6. }

    定义事件监听器

    1. @Component
    2. public class MyEventListener {
    3. @EventListener
    4. public void consumeEvent(MyEvent myEvent) {
    5. //doSomething
    6. }
    7. }

  • 广播事件

    1. @Component
    2. public class A {
    3. @Autowired
    4. private ApplicationContext applicationContext;
    5. public void methodA() {
    6. //广播事件
    7. applicationContext.publishEvent(new MyEvent(this));
    8. }
    9. }

    2. 通过实现接口的方式注册事件监听器

    在前面的例子中, 我们使用了 @EventListener注解的方式声明监听器
    其实Spring还提供了另一种方式实现监听器

  1. @Component
  2. public class MyEventListener2 implements ApplicationListener<MyEvent> {
  3. @Override
  4. public void onApplicationEvent(MyEvent event) {
  5. System.out.println(Thread.currentThread().getName());
  6. }
  7. }
  • 这样写的优势是, 用泛型规范了代码, 可以在编译时发现一些错误

    3. 异步消费

    默认情况是不会异步消费事件的, 我们需要提前配置才能让事件异步的进行消费

    3.1 默认情况同步消费

  • 默认消费事件与广播事件, 是在同一个线程完成的

  • 看下图的调试结果

image.png

  • 在controller和listener中都打印了当前线程

    3.2 实现异步消费

  • org.springframework.context.event.SimpleApplicationEventMulticaster#multicastEvent(org.springframework.context.ApplicationEvent, org.springframework.core.ResolvableType)

  • 通过观察源码可以看到 必须提前设置 taskExecutor 才能异步地消费事件

image.png

  • 我们这需要下面几件事
  1. 自定义事件分发器 ApplicationEventMulticaster
  2. 自定义线程池
  3. 将自定义线程池设置到事件分发器中
    1. @Configuration
    2. public class AsyncEventConfig implements BeanPostProcessor {
    3. @Bean("applicationEventMulticaster") //注意必须是这个名字
    4. public ApplicationEventMulticaster applicationEventMulticaster() { //手动创建事件广播器
    5. SimpleApplicationEventMulticaster result = new SimpleApplicationEventMulticaster();
    6. result.setTaskExecutor(this.eventExecutor().getObject());//设置异步线程池
    7. return result;
    8. }
    9. @Bean
    10. public ThreadPoolExecutorFactoryBean eventExecutor() {//创建线程池
    11. ThreadPoolExecutorFactoryBean eventExecutor = new ThreadPoolExecutorFactoryBean();
    12. eventExecutor.setThreadNamePrefix("eventExecutor-");
    13. eventExecutor.setCorePoolSize(3);
    14. return eventExecutor;
    15. }
    16. }
  • 打印线程看看效果

image.png

使用这种方法会有一个问题, 就是所有的事件全部变成异步的了, 如果希望同步的消费反而不行了
而且所有的事件, 全在一个线程池中, 这样就没有优先级的区分, 不是非常完美

3.3 使用@Async实现异步调用

  • 启用Spring的Async功能
  • 并注册多个线程池

    1. @EnableAsync
    2. @Configuration
    3. public class AsyncConfig {
    4. //这里可以初始化多个线程池, 让@Async执行的时候指定线程池
    5. @Bean("一号线程池")
    6. public ThreadPoolExecutorFactoryBean eventExecutor1() {
    7. ThreadPoolExecutorFactoryBean eventExecutor = new ThreadPoolExecutorFactoryBean();
    8. eventExecutor.setThreadNamePrefix("一号线程池-");
    9. eventExecutor.setCorePoolSize(3);
    10. return eventExecutor;
    11. }
    12. @Bean("二号线程池")
    13. public ThreadPoolExecutorFactoryBean eventExecutor2() {
    14. ThreadPoolExecutorFactoryBean eventExecutor = new ThreadPoolExecutorFactoryBean();
    15. eventExecutor.setThreadNamePrefix("二号线程池-");
    16. eventExecutor.setCorePoolSize(3);
    17. return eventExecutor;
    18. }
    19. }
  • 注册监听器的时候使用 @Async

  • 并指定执行的线程池

    1. @Component
    2. public class MyEventListener1 {
    3. @Async("一号线程池")
    4. @EventListener
    5. public void consumeEvent(MyEvent myEvent) {
    6. System.out.println(Thread.currentThread().getName());
    7. }
    8. }
    9. @Component
    10. public class MyEventListener2 implements ApplicationListener<MyEvent> {
    11. @Async("二号线程池")
    12. @Override
    13. public void onApplicationEvent(MyEvent event) {
    14. System.out.println(Thread.currentThread().getName());
    15. }
    16. }
  • 看看推送事件之后的执行结果

image.png

这里注意一下, 如果使用 @Async 的方式, 就不要自己注册 ApplicationEventMulticaster 了, 浪费资源