这一块用的较少,了解一下即可。

1. 观察者模式

在 Spring 中,我们大家或多或少听说过一个概念,就是事件驱动和监听器,其实这是一种典型的观察者模式。

观察者模式也叫做发布订阅模式,它的特点就是监听/观察到一个对象被修改了或者做出了一些预期中的行为,然后就会通知它的订阅者们。

所以在 Spring 中,你可以把 IOC 理解为观察者,因为它本身也是事件广播器,把事件源当做观察的内容主题,监听器自然顾名思义就是订阅者了。

为什么说 IOC 本身也是广播器呢?

  • ApplicationContext 实现了 ApplicationEventPublisher ,所以已经具备了广播器发布事件的能力。
  • 而且 ApplicationEventMulticaster 组合了所有的监听器,使得事件广播器具有了广播事件的能力。

    2. 一个简单的案例

    我们先来看看如何在 Spring 中使用事件和监听器

我们下面就以两个事件来模拟, ContextRefreshedEvent 和 ContextClosedEvent,后面再解释他们是什么。

通过实现 ApplicationListener 接口

  1. @Component
  2. public class ContextRefreshedApplicationListener implements ApplicationListener<ContextRefreshedEvent> {
  3. @Override
  4. public void onApplicationEvent(ContextRefreshedEvent event) {
  5. System.out.println("监听到了 ContextRefreshedEvent 事件");
  6. }
  7. }

通过 @EventListener 注解

@Component
public class ContextClosedApplicationListener {
    @EventListener
    public void onContextClosedEvent(ContextClosedEvent event) {
        System.out.println("监听到了 ContextClosedEvent 事件");
    }
}

测试类

public class Test {
    public static void main(String[] args) {
        System.out.println("容器初始化准备");
        AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext("com.ideal");
        System.out.println("容器初始化完成");
        ctx.close();
        System.out.println("容器关闭");
    }
}

运行结果

容器初始化准备
监听到了 ContextRefreshedEvent 事件
容器初始化完成
监听到了 ContextClosedEvent 事件
容器关闭

可以看到我们的事件确实被监听到了,而且会随着容器的创建而监听到,随着容器销毁而销毁,这就是 Spring 内置的一些事件。

3. Spring 内置事件

3.1 ApplicationEvent 和 ApplicationContextEvent

我们的那几个内置事件,都是实现了 ApplicationContextEvent 接口,而它又实现了 ApplicationEvent。
这个比较顶级的 ApplicationEvent ,继承了 EventObject,而它本身也只是一个事件的抽象类,只有一个时间戳。

public abstract class ApplicationEvent extends EventObject {
    private static final long serialVersionUID = 7099057708183571937L;
    private final long timestamp;

    public ApplicationEvent(Object source) {
        super(source);
        this.timestamp = System.currentTimeMillis();
    }

    public ApplicationEvent(Object source, Clock clock) {
        super(source);
        this.timestamp = clock.millis();
    }

    public final long getTimestamp() {
        return this.timestamp;
    }
}

而这个 ApplicationContextEvent 的构造方法,把 IOC 容器也传递了进去,这样就能通过监听器直接获取 ApplicationContext了。

public abstract class ApplicationContextEvent extends ApplicationEvent {
    public ApplicationContextEvent(ApplicationContext source) {
        super(source);
    }

    public final ApplicationContext getApplicationContext() {
        return (ApplicationContext)this.getSource();
    }
}

3.2 几个内置事件

  • ContextRefreshedEvent:容器刷新完毕,但尚未启动(所有单例 bean 创建完成后)
  • ContextClosedEvent:IOC 容器已经关闭,尚未销毁所有 Bean
  • ContextStartedEvent:所有单例 bean 创建完成,且实现了 Lifecycle 接口的 Bean 的 start 方法被执行后。
  • ContextStoppedEvent:在 ContextClosedEvent 触发后面的时间点才会触发要把所有 Bean 停掉,才能释放资源,销毁 Bean。

    4. 自定义事件

    4.1 代码

    创建一个自己的事件,通过继承 ApplicationEvent 即可

    public class MyBusinessEvent extends ApplicationEvent {
      public MyBusinessEvent(Object source) {
          super(source);
      }
    
      public MyBusinessEvent(Object source, Clock clock) {
          super(source, clock);
      }
    }
    

    监听刚才这个自定义事件

    public class MyBusinessListener implements ApplicationListener<MyBusinessEvent> {
      @Override
      public void onApplicationEvent(MyBusinessEvent event) {
          System.out.println("监听到了 MyBusinessEvent 事件");
      }
    }
    

    接着,我们还需要一个事件发布的时机

    @Service
    public class MyBusinessService implements ApplicationEventPublisherAware {
    
      ApplicationEventPublisher publisher;
    
      public void myBusiness(String userId) {
          System.out.println("获取到用户Id" + userId);
          // 发布事件
          publisher.publishEvent(new MyBusinessEvent(userId));
      }
    
      @Override
      public void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher) {
          this.publisher = applicationEventPublisher;
      }
    }
    

    测试一下

    public class Test {
      public static void main(String[] args) {
          AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext("com.ideal");
          MyBusinessService myBusinessService = ctx.getBean(MyBusinessService.class);
          myBusinessService.myBusiness("w123456");
      }
    }
    

    运行结果

    监听到了 ContextRefreshedEvent 事件
    获取到用户Idw123456
    

    4.2 调整顺序

    我们还可以再监听器上面增加 @Order 注解,用来控制监听器触发的顺序,如果有多个监听器,就可以通过 @Order(0) 这样的标识,代表先执行,默认是 Integer.MAX_VALUE ,即最后的意思。