1. 观察者模式
在 Spring 中,我们大家或多或少听说过一个概念,就是事件驱动和监听器,其实这是一种典型的观察者模式。
观察者模式也叫做发布订阅模式,它的特点就是监听/观察到一个对象被修改了或者做出了一些预期中的行为,然后就会通知它的订阅者们。
所以在 Spring 中,你可以把 IOC 理解为观察者,因为它本身也是事件广播器,把事件源当做观察的内容主题,监听器自然顾名思义就是订阅者了。
为什么说 IOC 本身也是广播器呢?
- ApplicationContext 实现了 ApplicationEventPublisher ,所以已经具备了广播器发布事件的能力。
- 而且 ApplicationEventMulticaster 组合了所有的监听器,使得事件广播器具有了广播事件的能力。
2. 一个简单的案例
我们先来看看如何在 Spring 中使用事件和监听器
我们下面就以两个事件来模拟, ContextRefreshedEvent 和 ContextClosedEvent,后面再解释他们是什么。
通过实现 ApplicationListener 接口
@Component
public class ContextRefreshedApplicationListener implements ApplicationListener<ContextRefreshedEvent> {
@Override
public void onApplicationEvent(ContextRefreshedEvent event) {
System.out.println("监听到了 ContextRefreshedEvent 事件");
}
}
通过 @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 ,即最后的意思。