监听器模式介绍
监听器模式的四要素
事件
监听器
广播器
触发机制
手动实现一个监听器(天气预报的模拟)
定义事件
//事件接口
public interface WeatherEvent {
String getWeather();
}
//下雨事件
public class RainWeatherEvent implements WeatherEvent {
@Override
public String getWeather() {
return "rain";
}
}
//下雪事件
public class SnowWeatherEvent implements WeatherEvent {
@Override
public String getWeather() {
return "snow";
}
}
定义监听器
//监听器接口
public interface WeatherListener {
void doWeatherListener(WeatherEvent event);
}
//下雨监听器
public class RainWeatherListener implements WeatherListener {
@Override
public void doWeatherListener(WeatherEvent event) {
if (event instanceof RainWeatherEvent) {
System.out.println(this + " --- the weather is " + event.getWeather());
}
}
}
//下雪监听器
public class SnowWeatherListener implements WeatherListener {
@Override
public void doWeatherListener(WeatherEvent event) {
if (event instanceof SnowWeatherEvent) {
System.out.println( this + " --- the weather is " + event.getWeather());
}
}
}
定义广播器
//事件广播器接口
public interface EventBroadcast {
//事件广播
void broadcast(WeatherEvent event);
//注册监听器
void addListener(WeatherListener listener);
//溢出监听器
void removeListener(WeatherListener listener);
}
//模板方法 定义抽象的事件广播器
public abstract class AbstractEventBroadcast implements EventBroadcast {
//注册的所有的时间监听器
public List<WeatherListener> weatherListenerList;
@Override
public void broadcast(WeatherEvent event){
doStart(event);
if (null != weatherListenerList && weatherListenerList.size() > 0) {
weatherListenerList.stream().forEach(listener -> listener.doWeatherListener(event));
}
doEnd(event);
}
@Override
public void addListener(WeatherListener listener) {
if (null == weatherListenerList) {
weatherListenerList = Lists.newArrayList();
}
weatherListenerList.add(listener);
}
@Override
public void removeListener(WeatherListener listener) {
if (null != weatherListenerList) {
weatherListenerList.remove(listener);
}
}
//广播前动作
abstract void doStart(WeatherEvent event);
//广播后动作
abstract void doEnd(WeatherEvent event);
}
//实现抽象的广播器模板方法
public class WeatherEventBroadcast extends AbstractEventBroadcast {
@Override
void doStart(WeatherEvent event) {
System.out.println("开始向所有监听器广播天气事件--------------start");
}
@Override
void doEnd(WeatherEvent event) {
System.out.println("完成向所有监听器广播天气事件--------------end");
}
}
SpringBoot系统监听器介绍
监听器ApplicationListener的类介绍
/**
* 由应用程序事件侦听器实现的接口。
*
* <p>基于标准的{@code java.util.EventListener}接口
* 用于观察者设计模式。
*
* <p>从 Spring 3.0 开始,一个 {@code ApplicationListener} 可以一般地声明
* 它感兴趣的事件类型。当用 Spring 注册时
* {@code ApplicationContext},事件将被相应地过滤,使用仅为匹配事件对象调用侦听器。
*/
@FunctionalInterface
public interface ApplicationListener<E extends ApplicationEvent> extends EventListener {
/**
* 处理应用程序事件。
* @param event 要响应的事件
*/
void onApplicationEvent(E event);
}
系统广播器ApplicationEventMulticaster的类介绍
/**
* 由可以管理多个对象的对象实现的接口
* {@link ApplicationListener} 对象并向它们发布事件。
*
* <p>一个{@link org.springframework.context.ApplicationEventPublisher},通常是
* 一个 Spring {@link org.springframework.context.ApplicationContext},可以使用一个
* {@code ApplicationEventMulticaster} 作为实际发布事件的委托。
*/
public interface ApplicationEventMulticaster {
/**
* 添加一个监听器来接收所有事件的通知。
* @param listener 要添加的监听器
*/
void addApplicationListener(ApplicationListener<?> listener);
/**
* 添加一个侦听器 bean 以接收所有事件的通知。
* @param listenerBeanName 要添加的监听器 bean 的名称
*/
void addApplicationListenerBean(String listenerBeanName);
/**
* 从通知列表中删除一个监听器。
* @param listener 要删除的监听器
*/
void removeApplicationListener(ApplicationListener<?> listener);
/**
* 从通知列表中删除侦听器 bean。
* @param listenerBeanName 要删除的侦听器 bean 的名称
*/
void removeApplicationListenerBean(String listenerBeanName);
/**
* 从注册的集合中删除所有匹配的监听器
* {@code ApplicationListener} 实例(包括适配器类
* 如 {@link ApplicationListenerMethodAdapter},例如 为注释
* {@link EventListener} 方法)。
* <p>注意:这仅适用于实例注册,不适用于侦听器
* 通过 bean 名称注册。
* @param predicate 谓词,用于识别要删除的侦听器实例,
*/
void removeApplicationListeners(Predicate<ApplicationListener<?>> predicate);
/**
* 从注册的集合中删除所有匹配的侦听器 bean
* 侦听器 bean 名称(指的是 bean 类,它们依次
* 直接实现 {@link ApplicationListener} 接口)。
* <p>注意:这仅适用于 bean 名称注册,不适用于
* 以编程方式注册的 {@code ApplicationListener} 实例。
* @param predicate 谓词,用于识别要删除的侦听器 bean 名称
*/
void removeApplicationListenerBeans(Predicate<String> predicate);
/**
* 删除所有注册到此多播器的侦听器。
* <p>remove 调用后,多播器将不执行任何操作
* 在事件通知上,直到注册新的侦听器。
*/
void removeAllListeners();
/**
* 将给定的应用程序事件多播给适当的侦听器。
* <p>考虑使用 {@link #multicastEvent(ApplicationEvent, ResolvableType)}
* 如果可能,因为它为基于泛型的事件提供了更好的支持。
* @param event 要多播的事件
*/
void multicastEvent(ApplicationEvent event);
/**
* 将给定的应用程序事件多播给适当的侦听器。
* <p>如果{@code eventType}为{@code null},则构建默认类型
* 基于 {@code event} 实例。
* @param event 要多播的事件
* @param eventType 事件类型(可以是{@code null})
*/
void multicastEvent(ApplicationEvent event, @Nullable ResolvableType eventType);
}
系统事件
EventObject 事件对象
ApplicationEvent 应用事件
SpringApplicationEvent Spring中的系统事件
ApplicationPreparedEvent 该事件表示:应用已经准备好
ApplicationReadyEvent 该事件表示:应用已经就绪
ApplicationStartedEvent 该事件表示:容器已经启动
ApplicationFailedEvent 该事件表示:容器启动失败
ApplicationEnvironmentPreparedEvent 该事件表示:应用上下文环境已经准备完成
ApplicationContextInitializedEvent 该事件表示:应用启动完成
Spring系统事件发送顺序
starting: 环境一启动就发出
environmentPrepared: 环境已准备妥当,即 已将系统和自定义的的一些属性已加载到容器内
contextInitialized:springboot已经启动,并且准备好了上下文,这个是在加载bean之前发布的
prepared:应用上下文已经创建完毕,但bean还没有完全加载完成
started: springboot已经将bean实例化完成了,但是还没调用CommandLineRunner和ApplicationRunner 这两个接口
ready:CommandLineRunner和ApplicationRunner 调用完毕后发出
failed:启动运行中如果发生错误会发送该事件
系统事件监听器的注册
此处和系统初始化器中的注册的方式完全一致,唯一不同的是:
@SuppressWarnings({ "unchecked", "rawtypes" })
public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
/*......*/
//此方法调用是 注册系统初始化器
setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
//此方法调用是 注册系统监听器
setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
/*......*/
}
监听事件触发机制
- 1
SpringApplication.run(MystuflinkApplication.class, args);
- 2
public static ConfigurableApplicationContext run(Class<?> primarySource, String... args) {
return run(new Class<?>[] { primarySource }, args);
}
- 3
public static ConfigurableApplicationContext run(Class<?>[] primarySources, String[] args) {
return new SpringApplication(primarySources).run(args);
}
- 4
public ConfigurableApplicationContext run(String... args) {
StopWatch stopWatch = new StopWatch();
stopWatch.start();
DefaultBootstrapContext bootstrapContext = createBootstrapContext();
ConfigurableApplicationContext context = null;
configureHeadlessProperty();
SpringApplicationRunListeners listeners = getRunListeners(args);
//start事件发布
listeners.starting(bootstrapContext, this.mainApplicationClass);
/*......*/
}
- 5
void starting(ConfigurableBootstrapContext bootstrapContext, Class<?> mainApplicationClass) {
//调用 (EventPublishingRunListener)listener.starting 方法
doWithListeners("spring.boot.application.starting", (listener) -> listener.starting(bootstrapContext),
(step) -> {
if (mainApplicationClass != null) {
step.tag("mainApplicationClass", mainApplicationClass.getName());
}
});
}
- 6
@Override
public void starting(ConfigurableBootstrapContext bootstrapContext) {
//调用SimpleApplicationEventMulticaster的multicastEvent进行广播事件
this.initialMulticaster
//new 好一个ApplicationStartingEvent事件对象,调用multicastEvent进行广播
.multicastEvent(new ApplicationStartingEvent(bootstrapContext, this.application, this.args));
}
- 7
@Override
public void multicastEvent(ApplicationEvent event) {
multicastEvent(event, resolveDefaultEventType(event));
}
- 8
@Override
public void multicastEvent(final ApplicationEvent event, @Nullable ResolvableType eventType) {
ResolvableType type = (eventType != null ? eventType : resolveDefaultEventType(event));
Executor executor = getTaskExecutor();
//getApplicationListeners 方法获取对该event事件感兴趣的监听器,并循环
for (ApplicationListener<?> listener : getApplicationListeners(event, type)) {
if (executor != null) {
//invokeListener方法进行对循环到的指定的监听器进行广播
executor.execute(() -> invokeListener(listener, event));
}
else {
invokeListener(listener, event);
}
}
}
- 8.1
//获得对event事件感兴趣的监听器
protected Collection<ApplicationListener<?>> getApplicationListeners(
ApplicationEvent event, ResolvableType eventType) {
//获得该事件的自定义key
ListenerCacheKey cacheKey = new ListenerCacheKey(eventType, sourceType);
//查看是否有缓存
CachedListenerRetriever existingRetriever = this.retrieverCache.get(cacheKey);
if (existingRetriever != null) {
Collection<ApplicationListener<?>> result = existingRetriever.getApplicationListeners();
if (result != null) {
//有的话直接返回
return result;
}
}
//否则继续寻找
return retrieveApplicationListeners(eventType, sourceType, newRetriever);
}
- 8.2
private Collection<ApplicationListener<?>> retrieveApplicationListeners(
ResolvableType eventType, @Nullable Class<?> sourceType, @Nullable CachedListenerRetriever retriever) {
/*......*/
Set<ApplicationListener<?>> listeners;
Set<String> listenerBeans;
synchronized (this.defaultRetriever) {
listeners = new LinkedHashSet<>(this.defaultRetriever.applicationListeners);
listenerBeans = new LinkedHashSet<>(this.defaultRetriever.applicationListenerBeans);
}
/*......*/
for (ApplicationListener<?> listener : listeners) {
//supportsEvent 判断该监听器是否对该事件感兴趣
if (supportsEvent(listener, eventType, sourceType)) {
if (retriever != null) {
filteredListeners.add(listener);
}
allListeners.add(listener);
}
}
/*......*/
//一般listenerBeans为空,此处不再拓展
if (!listenerBeans.isEmpty()) {
/*......*/
}
}
- 8.3
protected boolean supportsEvent(
ApplicationListener<?> listener, ResolvableType eventType, @Nullable Class<?> sourceType) {
//注:GenericApplicationListenerAdapter 是 GenericApplicationListener的接口实现
//注:GenericApplicationListener接口继承自SmartApplicationListener接口
//注:SmartApplicationListener接口继承自 ApplicationListener<ApplicationEvent>, Ordered 两个接口
GenericApplicationListener smartListener = (listener instanceof GenericApplicationListener ?
(GenericApplicationListener) listener : new GenericApplicationListenerAdapter(listener));
return (smartListener.supportsEventType(eventType) && smartListener.supportsSourceType(sourceType));
}
- 8.4
@Override
@SuppressWarnings("unchecked")
public boolean supportsEventType(ResolvableType eventType) {
if (this.delegate instanceof GenericApplicationListener) {
return ((GenericApplicationListener) this.delegate).supportsEventType(eventType);
}
else if (this.delegate instanceof SmartApplicationListener) {
Class<? extends ApplicationEvent> eventClass = (Class<? extends ApplicationEvent>) eventType.resolve();
return (eventClass != null && ((SmartApplicationListener) this.delegate).supportsEventType(eventClass));
}
else {
return (this.declaredEventType == null || this.declaredEventType.isAssignableFrom(eventType));
}
}
- 9
//使用指定的事件触发指定的监听器
protected void invokeListener(ApplicationListener<?> listener, ApplicationEvent event) {
ErrorHandler errorHandler = getErrorHandler();
if (errorHandler != null) {
try {
//触发调用
doInvokeListener(listener, event);
}
catch (Throwable err) {
errorHandler.handleError(err);
}
}
else {
doInvokeListener(listener, event);
}
}
- 10
private void doInvokeListener(ApplicationListener listener, ApplicationEvent event) {
try {
//监听器onApplicationEvent方法的调用
listener.onApplicationEvent(event);
}
catch (ClassCastException ex) {
/*......*/
}
}
获取监听列表
通用触发条条件
自定义监听器实战
第一种:spring.factories中配置
FirstListener类
import org.springframework.boot.context.event.ApplicationStartedEvent;
import org.springframework.context.ApplicationListener;
import org.springframework.core.annotation.Order;
@Order(1)
public class FirstListener implements ApplicationListener<ApplicationStartedEvent> {
@Override
public void onApplicationEvent(ApplicationStartedEvent event) {
System.out.println("FirstListener : hello " + event.getClass());
}
}
resources目录下新建 : META-INF/spring.factories
org.springframework.context.ApplicationListener=com.wwy.myspringboot.customListener.FirstListener
第二种:启动类中配置
SecondListener类
import org.springframework.boot.context.event.ApplicationStartedEvent;
import org.springframework.context.ApplicationListener;
import org.springframework.core.annotation.Order;
@Order(2)
public class SecondListener implements ApplicationListener<ApplicationStartedEvent> {
@Override
public void onApplicationEvent(ApplicationStartedEvent event) {
System.out.println("SecondListener : hello2 " + event.getClass());
}
}
启动类
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication springApplication = new SpringApplication(Application.class);
springApplication.addListeners(new SecondListener());
springApplication.run(args);
}
}
第三种:application.properties中配置
ThridListener类
import org.springframework.boot.context.event.ApplicationStartedEvent;
import org.springframework.context.ApplicationListener;
import org.springframework.core.annotation.Order;
@Order(3)
public class ThridListener implements ApplicationListener<ApplicationStartedEvent> {
@Override
public void onApplicationEvent(ApplicationStartedEvent event) {
System.out.println("ThridListener : hello " + event.getClass());
}
}
application.properties
context.listener.classes=com.wwy.myspringboot.customListener.ThridListener
第四种:继承SmartApplicationListener,并application.properties中配置
FourthListener类
import org.springframework.boot.context.event.ApplicationPreparedEvent;
import org.springframework.boot.context.event.ApplicationStartedEvent;
import org.springframework.context.ApplicationEvent;
import org.springframework.context.event.SmartApplicationListener;
import org.springframework.core.annotation.Order;
@Order(4)
public class FourthListener implements SmartApplicationListener {
//注册感兴趣的事件
@Override
public boolean supportsEventType(Class<? extends ApplicationEvent> eventType) {
return ApplicationStartedEvent.class.isAssignableFrom(eventType) ||
ApplicationPreparedEvent.class.isAssignableFrom(eventType);
}
//接收到事件后的动作
@Override
public void onApplicationEvent(ApplicationEvent event) {
System.out.println("FourthListener : hello " + event.getClass());
}
}
application.properties
context.listener.classes=com.wwy.myspringboot.customListener.ThridListener,com.wwy.myspringboot.customListener.FourthListener
Console
. ____ _ __ _ _
/\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
\\/ ___)| |_)| | | | | || (_| | ) ) ) )
' |____| .__|_| |_|_| |_\__, | / / / /
=========|_|==============|___/=/_/_/_/
:: Spring Boot :: (v2.5.5)
run FirstInitializer
2021-09-30 23:42:32.480 WARN 3713 --- [ main] o.s.boot.StartupInfoLogger : InetAddress.getLocalHost().getHostName() took 5006 milliseconds to respond. Please verify your network configuration (macOS machines may need to add entries to /etc/hosts).
2021-09-30 23:42:37.491 INFO 3713 --- [ main] c.w.m.customListener.Application : Starting Application using Java 1.8.0_231 on wangwenyong with PID 3713 (/Users/wangwenyong/ideaProject/mystuflink/target/classes started by wangwenyong in /Users/wangwenyong/ideaProject/mystuflink)
2021-09-30 23:42:37.492 INFO 3713 --- [ main] c.w.m.customListener.Application : No active profile set, falling back to default profiles: default
FourthListener : hello class org.springframework.boot.context.event.ApplicationPreparedEvent
2021-09-30 23:42:38.028 INFO 3713 --- [ main] .s.d.r.c.RepositoryConfigurationDelegate : Multiple Spring Data modules found, entering strict repository configuration mode!
2021-09-30 23:42:38.030 INFO 3713 --- [ main] .s.d.r.c.RepositoryConfigurationDelegate : Bootstrapping Spring Data Redis repositories in DEFAULT mode.
2021-09-30 23:42:38.048 INFO 3713 --- [ main] .s.d.r.c.RepositoryConfigurationDelegate : Finished Spring Data repository scanning in 7 ms. Found 0 Redis repository interfaces.
2021-09-30 23:42:38.394 INFO 3713 --- [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat initialized with port(s): 8080 (http)
2021-09-30 23:42:38.402 INFO 3713 --- [ main] o.apache.catalina.core.StandardService : Starting service [Tomcat]
2021-09-30 23:42:38.402 INFO 3713 --- [ main] org.apache.catalina.core.StandardEngine : Starting Servlet engine: [Apache Tomcat/9.0.53]
2021-09-30 23:42:38.470 INFO 3713 --- [ main] o.a.c.c.C.[Tomcat].[localhost].[/] : Initializing Spring embedded WebApplicationContext
2021-09-30 23:42:38.470 INFO 3713 --- [ main] w.s.c.ServletWebServerApplicationContext : Root WebApplicationContext: initialization completed in 927 ms
2021-09-30 23:42:39.129 INFO 3713 --- [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat started on port(s): 8080 (http) with context path ''
2021-09-30 23:42:39.138 INFO 3713 --- [ main] c.w.m.customListener.Application : Started Application in 22.035 seconds (JVM running for 27.73)
ThridListener : hello class org.springframework.boot.context.event.ApplicationStartedEvent
FourthListener : hello class org.springframework.boot.context.event.ApplicationStartedEvent
FirstListener : hello class org.springframework.boot.context.event.ApplicationStartedEvent
SecondListener : hello2 class org.springframework.boot.context.event.ApplicationStartedEvent
TIPS
实现ApplicationListener接口针对单一事件监听
实现SmartApplicationListener接口针对多事件监听
Order值越小越先执行
application.properties中定义的优于其他方式
面试问题
- 介绍下监听器模式
- SpringBoot关于监听器相关的实现类有哪些
- SpringBoot框架有哪些框架事件以及他们的顺序
- 介绍下监听事件触发机制
- 如何自定义实现系统监听器及注意事项
- 实现ApplicationListener接口与SmartApplicationListener接口区别