Spring中的ApplicationListener以及观察者模式
1.介绍
在之前的文章从SpringBoot启动过程分析到自定义一个springboot-starter中我们看到了Spring监听器在SpringApplication得启动过程中扮演了相当重要的角色,
在SpringApplication初始化和ConfigurableApplicationContext创建的不同阶段通过发布相应类型的事件,不同的ApplicationListener监听到相应的Event执行从而完成了自己对应的一些初始化操作。
2.spring内置的ApplicationListener分析
SpringBoot2.3.3中spring.factories中内置了如下的ApplicationListener:
# Application Listeners
org.springframework.context.ApplicationListener=\
org.springframework.boot.ClearCachesApplicationListener,\
org.springframework.boot.builder.ParentContextCloserApplicationListener,\
org.springframework.boot.cloud.CloudFoundryVcapEnvironmentPostProcessor,\
org.springframework.boot.context.FileEncodingApplicationListener,\
org.springframework.boot.context.config.AnsiOutputApplicationListener,\
org.springframework.boot.context.config.ConfigFileApplicationListener,\
org.springframework.boot.context.config.DelegatingApplicationListener,\
org.springframework.boot.context.logging.ClasspathLoggingApplicationListener,\
org.springframework.boot.context.logging.LoggingApplicationListener,\
org.springframework.boot.liquibase.LiquibaseServiceLocatorApplicationListener
来看下其中一些主要的listener的作用:
ApplicationListener | 作用 | 监听事件 |
---|---|---|
ClearCachesApplicationListener | 上下文环境context加载后清理缓存 | ContextRefreshedEvent |
ParentContextCloserApplicationListener | 如果父对象关闭,则关闭应用程序上下文 | ParentContextAvailableEvent |
FileEncodingApplicationListener | 当系统属性file.encoding与spring的强制编码属性spring.mandatory-file-encoding不一致时,打印信息,并抛出IllegalStateException异常 | ApplicationEnvironmentPreparedEvent |
AnsiOutputApplicationListener | 根据属性spring.output.ansi.enabled来配置ANSI输出 | ApplicationEnvironmentPreparedEvent |
ConfigFileApplicationListener | 搜索并加载配置文件,根据配置文件设置Environment和SpringApplication | ApplicationEnvironmentPreparedEvent & ApplicationPreparedEvent |
DelegatingApplicationListener | 类似于DelegatingApplicationContextInitializer的作用,加载并转发配置在context.listener.classes属性的其它ApplicationListener | ApplicationEvent |
ClasspathLoggingApplicationListener | 在DEBUG日志级别下,对ApplicationEnvironmentPreparedEvent和ApplicationFailedEvent打印相应的日志 | ApplicationEvent |
LoggingApplicationListener | 根据配置,在合适的时候初始化和配置系统日志 | ApplicationEvent |
3.ApplicationListener事件监听机制的使用
事件的发布和监听涉及到三个部分:
- 事件发布方:spring中的事件发布最终是由 SimpleApplicationEventMulticaster 类中的multicastEvent进行发布操作。一般使用时直接使用ApplicationContext发布即可。
- event事件:可以通过继承 ApplicationEvent 自定义要接收的event类型
- 事件接收方:自定义事件监听器listener来监听发布的事件从而执行相应的操作,以下两种方式可以定义listener:
- 实现ApplicationListener接口并指定要监听的事件类型
@Component public class MyApplicationListener implements ApplicationListener<MyApplicationEvent> { Logger log = LoggerFactory.getLogger(MyApplicationListener.class); public void onApplicationEvent(MyApplicationEvent event) { log.info("监听到event:{}",event); } }
- 使用 @EventListener设置监听方法,比较简便,无需单独创建监听类
@Component public class MyApplicationListener1 { Logger log = LoggerFactory.getLogger(MyApplicationListener1.class); @EventListener public void onApplicationEvent(MyApplicationEvent event) { log.info("监听到event:{}",event); } }
4.从观察者模式分析监听器机制
- 观察者模式:定义对象间的一种一对多依赖关系,使得每当一个对象状态发生改变时,其相关依赖对象皆得到通知并被自动更新 对象行为型模式
- 角色:
- abstractSubject: 抽象主题(抽象被观察者)
保持观察者的列表,有权增删观察者,发生变化时通知观察者 - concreteSubject: 具体被观察者 实现具体的改变,并在发生改变时 通知具体的观察者
- abstractObserver: 抽象观察者 定义update()接口,即收到主题通知后的操作
- concreteObserver: 具体观察者 实现update()接口,保持与主题的改变相符合的状态
- abstractSubject: 抽象主题(抽象被观察者)
- 推模型和拉模型
- 推模型: push 由subject主题对象主动向观察者推送改变的数据(存在的问题,主题对象不知道观察者需要哪些数据时,只能推送所有)
- 拉模型: pull 主题对象仅推送少量信息,通过传递subject引用给观察者,观察者自己选择需要获取的数据
- java中对观察者模式的支持
- Observer接口:
update()方法,当被观察者对象的状态发生变化时,被观察者对象的notifyObservers()方法就会调用这一方法 - Observable 类:被观察者类都是java.util.Observable类的子类
setChanged():被调用之后会设置一个内部标记变量,代表被观察者对象的状态发生了变化
notifyObservers():这个方法被调用时,会调用所有登记过的观察者对象的update()方法,使这些观察者对象可以更新自己。
- Observer接口:
- 角色: