springboot监听器

监听器模式介绍

springboot监听器

监听器模式的四要素

事件
监听器
广播器
触发机制

手动实现一个监听器(天气预报的模拟)

定义事件

//事件接口
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);

}
系统事件

springboot监听器

EventObject 事件对象
ApplicationEvent 应用事件
SpringApplicationEvent Spring中的系统事件
ApplicationPreparedEvent 该事件表示:应用已经准备好
ApplicationReadyEvent 该事件表示:应用已经就绪
ApplicationStartedEvent 该事件表示:容器已经启动
ApplicationFailedEvent 该事件表示:容器启动失败
ApplicationEnvironmentPreparedEvent 该事件表示:应用上下文环境已经准备完成
ApplicationContextInitializedEvent 该事件表示:应用启动完成

Spring系统事件发送顺序

springboot监听器

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) {
			/*......*/
		}
	}
获取监听列表

springboot监听器

通用触发条条件springboot监听器

自定义监听器实战

第一种: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接口区别
上一篇:Spring源码之事件管理与发布


下一篇:TcpListener(服务器)和TcpClient(客户端)