Spring事件監(jiān)聽器ApplicationListener的使用與原理分析
ApplicationEvent以及Listener是Spring為我們提供的一個(gè)事件監(jiān)聽、訂閱的實(shí)現(xiàn),內(nèi)部實(shí)現(xiàn)原理是觀察者設(shè)計(jì)模式,設(shè)計(jì)初衷也是為了系統(tǒng)業(yè)務(wù)邏輯之間的解耦,提高可擴(kuò)展性以及可維護(hù)性。事件發(fā)布者并不需要考慮誰去監(jiān)聽,監(jiān)聽具體的實(shí)現(xiàn)內(nèi)容是什么,發(fā)布者的工作只是為了發(fā)布事件而已。
Spring提供的內(nèi)置事件:
- ContextRefreshedEvent:容器刷新事件
- ContextStartedEvent:容器啟動事件
- ContextStoppedEvent:容器停止事件
- ContextClosedEvent:容器關(guān)閉事件
如何使用
監(jiān)聽容器的刷新事件
自定義一個(gè)ApplicationListener,指定監(jiān)聽的事件類型ContextRefreshedEvent:
package com.morris.spring.listener;
import org.springframework.context.ApplicationListener;
import org.springframework.context.event.ContextRefreshedEvent;
public class ContextRefreshedListener implements ApplicationListener<ContextRefreshedEvent> {
@Override
public void onApplicationEvent(ContextRefreshedEvent event) {
System.out.println("context refresh");
}
}注入到容器中:
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(); applicationContext.register(ContextRefreshedListener.class); applicationContext.refresh();
applicationContext.refresh()內(nèi)部會發(fā)送容器刷新的事件。
自定義事件
自定義的事件需要繼承ApplicationEvent:
package com.morris.spring.event;
import org.springframework.context.ApplicationEvent;
public class CustomEvent extends ApplicationEvent {
public CustomEvent(Object source) {
super(source);
}
}監(jiān)聽的時(shí)候使用ApplicationEvent的子類CustomEvent:
package com.morris.spring.listener;
import com.morris.spring.event.CustomEvent;
import org.springframework.context.ApplicationListener;
public class CustomEventListener implements ApplicationListener<CustomEvent> {
@Override
public void onApplicationEvent(CustomEvent event) {
System.out.println("custom event: " + event.getSource());
}
}可以使用AnnotationConfigApplicationContext發(fā)布事件:
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext();
applicationContext.register(CustomEventListener.class);
applicationContext.refresh();
applicationContext.publishEvent(new CustomEvent("custom event"));
可以向bean中注入一個(gè)ApplicationEventPublisher來發(fā)布事件:
package com.morris.spring.service;
import com.morris.spring.event.CustomEvent;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationEventPublisher;
public class CustomEventService {
@Autowired
private ApplicationEventPublisher applicationEventPublisher;
public void publishEvent() {
applicationEventPublisher.publishEvent(new CustomEvent("自定義事件"));
}
}可以通過實(shí)現(xiàn)ApplicationEventPublisherAware接口注入ApplicationEventPublisher來發(fā)布事件:
package com.morris.spring.service;
import com.morris.spring.event.CustomEvent;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.context.ApplicationEventPublisherAware;
public class CustomEventService2 implements ApplicationEventPublisherAware {
private ApplicationEventPublisher applicationEventPublisher;
public void publishEvent() {
applicationEventPublisher.publishEvent(new CustomEvent("自定義事件"));
}
@Override
public void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher) {
this.applicationEventPublisher = applicationEventPublisher;
}
}由于ApplicationContext實(shí)現(xiàn)了ApplicationEventPublisher接口,也可以直接注入ApplicationContext來發(fā)布事件。
使用@EventListener監(jiān)聽事件
在監(jiān)聽事件時(shí),由于類需要實(shí)現(xiàn)ApplicationListener接口,對代碼有很大的侵入性,可以使用@EventListener注解隨時(shí)隨地監(jiān)聽事件,這樣一個(gè)Service中可以監(jiān)聽多個(gè)事件:
package com.morris.spring.listener;
import com.morris.spring.event.CustomEvent;
import org.springframework.context.ApplicationListener;
import org.springframework.context.event.ContextRefreshedEvent;
import org.springframework.context.event.EventListener;
public class CustomEventListener2 {
@EventListener
public void listenContextRefreshedEvent(ContextRefreshedEvent event) {
System.out.println("context refresh");
}
@EventListener
public void listenCustomEvent(CustomEvent event) {
System.out.println("custom event: " + event.getSource());
}
}還可以在@EventListener注解上指定監(jiān)聽的事件類型:
package com.morris.spring.listener;
import com.morris.spring.event.CustomEvent;
import org.springframework.context.ApplicationEvent;
import org.springframework.context.event.ContextRefreshedEvent;
import org.springframework.context.event.EventListener;
public class CustomEventListener3 {
@EventListener({ContextRefreshedEvent.class, CustomEvent.class})
public void listenEvent(ApplicationEvent event) {
System.out.println(event);
}
}異步發(fā)送消息
spring消息的發(fā)送默認(rèn)都是同步的,如果要異步發(fā)送消息,首先要在配置類上開啟異步功能@EnableAsync:
package com.morris.spring.config;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.EnableAsync;
@Configuration
@EnableAsync // 開啟異步
public class EventListenerConfig {
}在監(jiān)聽的方法上加上@Async:
package com.morris.spring.listener;
import com.morris.spring.event.CustomEvent;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.ApplicationEvent;
import org.springframework.context.event.ContextRefreshedEvent;
import org.springframework.context.event.EventListener;
import org.springframework.scheduling.annotation.Async;
@Slf4j
public class AsyncCustomEventListener {
@EventListener({ContextRefreshedEvent.class, CustomEvent.class})
@Async // 異步
public void listenEvent(ApplicationEvent event) {
log.info("receive event: {}", event);
}
}也可以自定義執(zhí)行異步消息的線程池(默認(rèn)就是SimpleAsyncTaskExecutor):
@Bean
public TaskExecutor executor() {
return new SimpleAsyncTaskExecutor("eventListen-");
}
異步消息只是借用spring的異步執(zhí)行機(jī)制,在方法上加上@Async注解,方法都會異步執(zhí)行。
ApplicationListener原理分析
發(fā)布消息的入口
發(fā)布消息入口:
org.springframework.context.support.AbstractApplicationContext#publishEvent(org.springframework.context.ApplicationEvent)
public void publishEvent(ApplicationEvent event) {
publishEvent(event, null);
}
protected void publishEvent(Object event, @Nullable ResolvableType eventType) {
...
/**
* @see SimpleApplicationEventMulticaster#multicastEvent(org.springframework.context.ApplicationEvent, org.springframework.core.ResolvableType)
*/
// 發(fā)布消息
getApplicationEventMulticaster().multicastEvent(applicationEvent, eventType);
}
... ...
}然后調(diào)用SimpleApplicationEventMulticaster來進(jìn)行廣播消息:
// org.springframework.context.event.SimpleApplicationEventMulticaster#multicastEvent(org.springframework.context.ApplicationEvent, org.springframework.core.ResolvableType)
public void multicastEvent(final ApplicationEvent event, @Nullable ResolvableType eventType) {
ResolvableType type = (eventType != null ? eventType : resolveDefaultEventType(event));
// 如果有線程池,將會異步執(zhí)行
Executor executor = getTaskExecutor();
for (ApplicationListener<?> listener : getApplicationListeners(event, type)) {
if (executor != null) {
executor.execute(() -> invokeListener(listener, event));
}
else {
invokeListener(listener, event);
}
}
}
protected void invokeListener(ApplicationListener<?> listener, ApplicationEvent event) {
ErrorHandler errorHandler = getErrorHandler();
if (errorHandler != null) {
try {
doInvokeListener(listener, event);
}
catch (Throwable err) {
errorHandler.handleError(err);
}
}
else {
// ApplicationListener.調(diào)用onApplicationEvent
doInvokeListener(listener, event);
}
}
private void doInvokeListener(ApplicationListener listener, ApplicationEvent event) {
try {
listener.onApplicationEvent(event);
}
catch (ClassCastException ex) {
String msg = ex.getMessage();
if (msg == null || matchesClassCastMessage(msg, event.getClass())) {
// Possibly a lambda-defined listener which we could not resolve the generic event type for
// -> let's suppress the exception and just log a debug message.
Log logger = LogFactory.getLog(getClass());
if (logger.isTraceEnabled()) {
logger.trace("Non-matching event type for listener: " + listener, ex);
}
}
else {
throw ex;
}
}
}何時(shí)注入SimpleApplicationEventMulticaster
從上面的源碼可以發(fā)現(xiàn)spring是通過SimpleApplicationEventMulticaster事件多播器來發(fā)布消息的,那么這個(gè)類是何時(shí)注入的呢?容器refresh()時(shí)。
org.springframework.context.support.AbstractApplicationContext#initApplicationEventMulticaster
protected void initApplicationEventMulticaster() {
ConfigurableListableBeanFactory beanFactory = getBeanFactory();
if (beanFactory.containsLocalBean(APPLICATION_EVENT_MULTICASTER_BEAN_NAME)) {
this.applicationEventMulticaster =
beanFactory.getBean(APPLICATION_EVENT_MULTICASTER_BEAN_NAME, ApplicationEventMulticaster.class);
if (logger.isTraceEnabled()) {
logger.trace("Using ApplicationEventMulticaster [" + this.applicationEventMulticaster + "]");
}
}
else {
// 直接new,然后放入到spring一級緩存中
this.applicationEventMulticaster = new SimpleApplicationEventMulticaster(beanFactory);
beanFactory.registerSingleton(APPLICATION_EVENT_MULTICASTER_BEAN_NAME, this.applicationEventMulticaster);
if (logger.isTraceEnabled()) {
logger.trace("No '" + APPLICATION_EVENT_MULTICASTER_BEAN_NAME + "' bean, using " +
"[" + this.applicationEventMulticaster.getClass().getSimpleName() + "]");
}
}
}何時(shí)注入ApplicationListener
spring在發(fā)布消息時(shí),會從SimpleApplicationEventMulticaster中拿出所有的ApplicationListener,那么這些ApplicationListener何時(shí)被注入的呢?容器refresh()時(shí)。
org.springframework.context.support.AbstractApplicationContext#registerListeners
protected void registerListeners() {
// Register statically specified listeners first.
for (ApplicationListener<?> listener : getApplicationListeners()) {
getApplicationEventMulticaster().addApplicationListener(listener);
}
// Do not initialize FactoryBeans here: We need to leave all regular beans
// uninitialized to let post-processors apply to them!
String[] listenerBeanNames = getBeanNamesForType(ApplicationListener.class, true, false);
for (String listenerBeanName : listenerBeanNames) {
// 添加到SimpleApplicationEventMulticaster中
getApplicationEventMulticaster().addApplicationListenerBean(listenerBeanName);
}
// Publish early application events now that we finally have a multicaster...
Set<ApplicationEvent> earlyEventsToProcess = this.earlyApplicationEvents;
this.earlyApplicationEvents = null;
if (!CollectionUtils.isEmpty(earlyEventsToProcess)) {
for (ApplicationEvent earlyEvent : earlyEventsToProcess) {
getApplicationEventMulticaster().multicastEvent(earlyEvent);
}
}
}@EventListener的原理
@EventListener注解的功能是通過EventListenerMethodProcessor來實(shí)現(xiàn)的,EventListenerMethodProcessor這個(gè)類在AnnotationConfigApplicationContext的構(gòu)造方法中被注入。
EventListenerMethodProcessor主要實(shí)現(xiàn)了兩個(gè)接口:SmartInitializingSingleton和BeanFactoryPostProcessor。
先來看看BeanFactoryPostProcessor的postProcessBeanFactory(),這個(gè)方法主要是保存beanFactory和eventListenerFactories,后面的方法將會使用到:
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) {
// 保存beanFactory
this.beanFactory = beanFactory;
/**
* EventListenerFactory[DefaultEventListenerFactory]在何處被注入?
* @see AnnotationConfigUtils#registerAnnotationConfigProcessors(org.springframework.beans.factory.support.BeanDefinitionRegistry, java.lang.Object)
*/
Map<String, EventListenerFactory> beans = beanFactory.getBeansOfType(EventListenerFactory.class, false, false);
List<EventListenerFactory> factories = new ArrayList<>(beans.values());
AnnotationAwareOrderComparator.sort(factories);
// 保存eventListenerFactories
this.eventListenerFactories = factories;
}再來看看SmartInitializingSingleton的afterSingletonsInstantiated()方法,這個(gè)方法會在所有的bean初始化完后執(zhí)行。
public void afterSingletonsInstantiated() {
ConfigurableListableBeanFactory beanFactory = this.beanFactory;
Assert.state(this.beanFactory != null, "No ConfigurableListableBeanFactory set");
String[] beanNames = beanFactory.getBeanNamesForType(Object.class);
for (String beanName : beanNames) {
if (!ScopedProxyUtils.isScopedTarget(beanName)) {
// 目標(biāo)類的類型
Class<?> type = null;
try {
type = AutoProxyUtils.determineTargetClass(beanFactory, beanName);
}
catch (Throwable ex) {
// An unresolvable bean type, probably from a lazy bean - let's ignore it.
if (logger.isDebugEnabled()) {
logger.debug("Could not resolve target class for bean with name '" + beanName + "'", ex);
}
}
if (type != null) {
if (ScopedObject.class.isAssignableFrom(type)) {
try {
Class<?> targetClass = AutoProxyUtils.determineTargetClass(
beanFactory, ScopedProxyUtils.getTargetBeanName(beanName));
if (targetClass != null) {
type = targetClass;
}
}
catch (Throwable ex) {
// An invalid scoped proxy arrangement - let's ignore it.
if (logger.isDebugEnabled()) {
logger.debug("Could not resolve target bean for scoped proxy '" + beanName + "'", ex);
}
}
}
try {
// 處理目標(biāo)對象
processBean(beanName, type);
}
catch (Throwable ex) {
throw new BeanInitializationException("Failed to process @EventListener " +
"annotation on bean with name '" + beanName + "'", ex);
}
}
}
}
}
private void processBean(final String beanName, final Class<?> targetType) {
if (!this.nonAnnotatedClasses.contains(targetType) &&
AnnotationUtils.isCandidateClass(targetType, EventListener.class) &&
!isSpringContainerClass(targetType)) {
Map<Method, EventListener> annotatedMethods = null;
try {
// 獲得類中所有的帶有@EventListener注解的方法
annotatedMethods = MethodIntrospector.selectMethods(targetType,
(MethodIntrospector.MetadataLookup<EventListener>) method ->
AnnotatedElementUtils.findMergedAnnotation(method, EventListener.class));
}
catch (Throwable ex) {
// An unresolvable type in a method signature, probably from a lazy bean - let's ignore it.
if (logger.isDebugEnabled()) {
logger.debug("Could not resolve methods for bean with name '" + beanName + "'", ex);
}
}
if (CollectionUtils.isEmpty(annotatedMethods)) {
this.nonAnnotatedClasses.add(targetType);
if (logger.isTraceEnabled()) {
logger.trace("No @EventListener annotations found on bean class: " + targetType.getName());
}
}
else {
// Non-empty set of methods
ConfigurableApplicationContext context = this.applicationContext;
Assert.state(context != null, "No ApplicationContext set");
List<EventListenerFactory> factories = this.eventListenerFactories;
Assert.state(factories != null, "EventListenerFactory List not initialized");
for (Method method : annotatedMethods.keySet()) {
for (EventListenerFactory factory : factories) {
if (factory.supportsMethod(method)) {
Method methodToUse = AopUtils.selectInvocableMethod(method, context.getType(beanName));
// 使用factory創(chuàng)建一個(gè)ApplicationListener
ApplicationListener<?> applicationListener =
factory.createApplicationListener(beanName, targetType, methodToUse);
if (applicationListener instanceof ApplicationListenerMethodAdapter) {
((ApplicationListenerMethodAdapter) applicationListener).init(context, this.evaluator);
}
// 將ApplicationListener添加到Spring容器中
context.addApplicationListener(applicationListener);
break;
}
}
}
if (logger.isDebugEnabled()) {
logger.debug(annotatedMethods.size() + " @EventListener methods processed on bean '" +
beanName + "': " + annotatedMethods);
}
}
}
}SpringBoot中的事件
SpringBoot在啟動時(shí)會按以下順序發(fā)送消息:
- ApplicationStartingEvent:在運(yùn)行開始時(shí)發(fā)送 ,但在進(jìn)行任何處理之前(偵聽器和初始化程序的注冊除外)發(fā)送
- ApplicationEnvironmentPreparedEvent:當(dāng)被發(fā)送Environment到中已知的上下文中使用,但是在創(chuàng)建上下文之前
- ApplicationContextInitializedEvent:在ApplicationContext準(zhǔn)備好且已調(diào)用ApplicationContextInitializers之后但任何bean定義未加載之前發(fā)送
- ApplicationPreparedEvent:在刷新開始之前但在加載bean定義之后發(fā)送
- ApplicationStartedEvent:上下文已被刷新后發(fā)送,但是任何應(yīng)用程序和命令行都被調(diào)用前
- ApplicationReadyEvent:在所有的命令行應(yīng)用啟動后發(fā)送此事件,可以處理請求
- ApplicationFailedEvent:在啟動時(shí)異常發(fā)送
到此這篇關(guān)于Spring事件監(jiān)聽器ApplicationListener的使用與源碼分析的文章就介紹到這了,更多相關(guān)Spring ApplicationListener使用內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Java 日期時(shí)間工具包–java.time的使用
這篇文章主要介紹了Java 日期時(shí)間工具包–java.time的使用,幫助大家更好的理解和學(xué)習(xí)使用Java,感興趣的朋友可以了解下2021-04-04
Java因項(xiàng)目配置不當(dāng)而引發(fā)的數(shù)據(jù)泄露
這篇文章主要介紹了Java因項(xiàng)目配置不當(dāng)而引發(fā)的數(shù)據(jù)泄露解決辦法,本文給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2021-09-09
面試官:java ThreadLocal真的會造成內(nèi)存泄露嗎
ThreadLocal,java面試過程中的“釘子戶”,在網(wǎng)上也充斥著各種有關(guān)ThreadLocal內(nèi)存泄露的問題,本文換個(gè)角度,先思考ThreadLocal體系中的ThreadLocalMap為什么要設(shè)計(jì)成弱引用2021-08-08
Spring MVC攔截器(Interceptor)的定義和配置過程
這篇文章主要介紹了Spring MVC攔截器(Interceptor)的定義和配置過程,具有很好的參考價(jià)值,希望對大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2025-03-03
spring boot 實(shí)現(xiàn)阿里云視頻點(diǎn)播功能(刪除視頻)
這篇文章主要介紹了spring boot 實(shí)現(xiàn)阿里云視頻點(diǎn)播(刪除視頻功能),本文通過實(shí)例代碼給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2020-12-12

