最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Spring中的事件發(fā)布機制原理解析

 更新時間:2023年11月09日 08:29:17   作者:留蘭香丶  
這篇文章主要介紹了Spring中的事件發(fā)布機制原理解析,當我們關心spring容器什么時候刷新,或者想在spring容器刷新的時候做一些事情,監(jiān)聽關心的事件,主要就是在ApplicationListener中寫對應的事件,需要的朋友可以參考下

Spring事件發(fā)布機制原理

在 IoC 容器啟動流程中有一個 finishRefresh 方法,具體實現(xiàn)如下:

	protected void finishRefresh() {
		clearResourceCaches();
		initLifecycleProcessor();
		getLifecycleProcessor().onRefresh();
		// 向所有監(jiān)聽 ContextRefreshedEvent 事件的監(jiān)聽者發(fā)布事件
		publishEvent(new ContextRefreshedEvent(this));
		LiveBeansView.registerApplicationContext(this);
	}

這里我們只關注 publishEvent 方法,這個方法用于發(fā)布 IoC 刷新完成事件,事件時如何發(fā)布的呢?下面我們一起來看一下。

一、原理分析

	@Override
	public void publishEvent(ApplicationEvent event) {
		publishEvent(event, null);
	}

	protected void publishEvent(Object event, @Nullable ResolvableType eventType) {
		Assert.notNull(event, "Event must not be null");

		// Decorate event as an ApplicationEvent if necessary
		ApplicationEvent applicationEvent;
		// 根據(jù)事件類型進行包裝
		if (event instanceof ApplicationEvent) {
			applicationEvent = (ApplicationEvent) event;
		} else {
			applicationEvent = new PayloadApplicationEvent<>(this, event);
			if (eventType == null) {
				eventType = ((PayloadApplicationEvent<?>) applicationEvent).getResolvableType();
			}
		}

		// Multicast right now if possible - or lazily once the multicaster is initialized
		if (this.earlyApplicationEvents != null) {
			this.earlyApplicationEvents.add(applicationEvent);
		} else {
			// 獲取 ApplicationEventMulticaster,將事件廣播出去
			getApplicationEventMulticaster().multicastEvent(applicationEvent, eventType);
		}

		// Publish event via parent context as well...
		// 判斷是否存在父容器,如果存在則將事件也發(fā)布到父容器的監(jiān)聽者
		if (this.parent != null) {
			if (this.parent instanceof AbstractApplicationContext) {
				((AbstractApplicationContext) this.parent).publishEvent(event, eventType);
			} else {
				this.parent.publishEvent(event);
			}
		}
	}

通過上面方法可以看出 Spring 的事件是通過 ApplicationEventMulticaster 廣播出去的,這個 ApplicationEventMulticaster 在 IoC 啟動流程 initApplicationEventMulticaster 方法中初始化。如果該容器還存在父容器,那也會以同樣的形式將事件發(fā)布給父容器的監(jiān)聽者。

	@Override
	public void multicastEvent(final ApplicationEvent event, @Nullable ResolvableType eventType) {
		// 解析事件類型
		ResolvableType type = (eventType != null ? eventType : resolveDefaultEventType(event));
		// 根據(jù)事件與事件類型獲取所有監(jiān)聽者
		for (final ApplicationListener<?> listener : getApplicationListeners(event, type)) {
			// 獲取異步執(zhí)行器
			Executor executor = getTaskExecutor();
			if (executor != null) {
				// 如果執(zhí)行器部位 null,則異步執(zhí)行將事件發(fā)布給每一個監(jiān)聽者
				executor.execute(() -> invokeListener(listener, event));
			}
			else {
				// 同步發(fā)布事件
				invokeListener(listener, event);
			}
		}
	}

發(fā)布事件前會先獲取所有已注冊的監(jiān)聽器,而監(jiān)聽器早已在 IoC 啟動流程的 registerListeners 方法中注冊。獲取到所有事件監(jiān)聽器之后,就可以進行事件發(fā)布了。發(fā)布的時候分為異步執(zhí)行與順序執(zhí)行,默認情況下 executor 是沒有初始化的,因此是順序執(zhí)行。

	protected void invokeListener(ApplicationListener<?> listener, ApplicationEvent event) {
		// 獲取錯誤處理機制
		ErrorHandler errorHandler = getErrorHandler();
		if (errorHandler != null) {
			try {
				// 事件發(fā)布
				doInvokeListener(listener, event);
			}
			catch (Throwable err) {
				errorHandler.handleError(err);
			}
		}
		else {
			doInvokeListener(listener, event);
		}
	}

	private void doInvokeListener(ApplicationListener listener, ApplicationEvent event) {
		try {
			// 執(zhí)行監(jiān)聽器的 onApplicationEvent 方法
			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;
			}
		}
	}

到這里 Spring 的事件通知機制流程就結束了,總的來說還是比較好理解的。

二、事件通知 demo

了解了事件通知機制的基本原理后,下面我們來寫個 demo 體驗一下監(jiān)聽器是如何使用的。

// 定義一個 Event
public class EventDemo extends ApplicationEvent {

    private static final long serialVersionUID = -8363050754445002832L;

    private String message;

    public EventDemo(Object source, String message) {
        super(source);
        this.message = message;
    }

    public String getMessage() {
        return message;
    }
}

// 定義一個監(jiān)聽器1
public class EventDemo1Listener implements ApplicationListener<EventDemo> {
    public void onApplicationEvent(EventDemo event) {
        System.out.println(this + " receiver " + event.getMessage());
    }
}

// 定義一個監(jiān)聽器2
public class EventDemo2Listener implements ApplicationListener<EventDemo> {
    public void onApplicationEvent(EventDemo event) {
        System.out.println(this + " receiver " + event.getMessage());
    }
}

// 定義一個事件發(fā)布者
public class EventDemoPublish {
    public void publish(ApplicationEventPublisher applicationEventPublisher, String message) {
        EventDemo eventDemo = new EventDemo(this, message);
        applicationEventPublisher.publishEvent(eventDemo);
    }
}

在 XML 中配置 bean:

    <bean id="eventDemoPublish" class="com.jas.mess.event.EventDemoPublish"/>
    <bean id="eventDemo1Listener" class="com.jas.mess.event.EventDemo1Listener"/>
    <bean id="eventDemo2Listener" class="com.jas.mess.event.EventDemo2Listener"/>

編寫測試類:

    @Test
    public void eventTest() {
        ApplicationContext applicationContext = new ClassPathXmlApplicationContext(configLocation);
        applicationContext.getBean("eventDemoPublish", EventDemoPublish.class).publish(applicationContext, "hello world");
    }

控制臺輸出:

在這里插入圖片描述

不知道你有沒有注意到,在發(fā)布事件的時候我們傳的發(fā)布者是 applicationContext,applicationContext 本身繼承自 ApplicationEventPublisher 接口,因此它本身也是一個事件發(fā)布者。

到此這篇關于Spring中的事件發(fā)布機制原理解析的文章就介紹到這了,更多相關Spring事件發(fā)布機制原理內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!

相關文章

  • javaweb實戰(zhàn)之商城項目開發(fā)(二)

    javaweb實戰(zhàn)之商城項目開發(fā)(二)

    這篇文章主要針對javaweb商城項目開發(fā)進行實戰(zhàn)演習,利用mybatis創(chuàng)建DAO層,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2016-02-02
  • SpringBoot中定義Bean的方法總結

    SpringBoot中定義Bean的方法總結

    在Spring Boot應用程序中,定義Bean是非常常見的操作,它是構建應用程序的基礎,pring Boot提供了多種方式來定義Bean,每種方式都有其適用的場景和優(yōu)勢,本文將介紹Spring Boot中定義Bean的幾種常見方式,需要的朋友可以參考下
    2023-12-12
  • java實現(xiàn)同態(tài)加密算法的實例代碼

    java實現(xiàn)同態(tài)加密算法的實例代碼

    這篇文章主要給大家介紹了關于java實現(xiàn)同態(tài)加密算法的相關資料,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧
    2020-12-12
  • springboot手動動態(tài)注入controller和service方式

    springboot手動動態(tài)注入controller和service方式

    這篇文章主要介紹了springboot手動動態(tài)注入controller和service方式,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2022-03-03
  • SpringBoot集成Redis—使用RedisRepositories詳解

    SpringBoot集成Redis—使用RedisRepositories詳解

    這篇文章主要介紹了SpringBoot集成Redis—使用RedisRepositories詳解,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2022-03-03
  • 利用Java截取String字符串的幾種常用方法

    利用Java截取String字符串的幾種常用方法

    在開發(fā)應用中經(jīng)常會使用到java string,這篇文章主要給大家介紹了關于利用Java截取String字符串的幾種常用方法,文中通過代碼介紹的非常詳細,需要的朋友可以參考下
    2024-05-05
  • swagger配置正式環(huán)境中不可訪問的問題

    swagger配置正式環(huán)境中不可訪問的問題

    這篇文章主要介紹了swagger配置正式環(huán)境中不可訪問的問題,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2022-06-06
  • SpringBoot快速集成Logback日志組件

    SpringBoot快速集成Logback日志組件

    本文主要介紹了SpringBoot快速集成Logback日志組件,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧
    2022-08-08
  • Java十道入門易踩坑題分析前篇

    Java十道入門易踩坑題分析前篇

    這篇文章總結分析了Java入門容易碰到的幾點易進坑的題目,對于新手小白剛開始學Java非常有益處,讓你少走避開彎路,感興趣的朋友快來看看吧
    2022-01-01
  • 深入探討Java?SPI機制及其應用場景

    深入探討Java?SPI機制及其應用場景

    本文深入介紹了Java?SPI機制的原理和實現(xiàn)方式,以及在Java中如何使用SPI實現(xiàn)可插拔的組件化架構,包括示例代碼和應用場景分析,幫助讀者更好地理解和應用該機制
    2023-04-04

最新評論

宝鸡市| 涿鹿县| 泉州市| 凤城市| 周宁县| 秭归县| 社旗县| 普陀区| 宜宾县| 张家港市| 阿勒泰市| 莆田市| 县级市| 东山县| 仪陇县| 酉阳| 焦作市| 通道| 嫩江县| 麻城市| 奉节县| 聊城市| 宜州市| 金堂县| 奇台县| 平谷区| 天祝| 安泽县| 通河县| 宁德市| 安远县| 云霄县| 南陵县| 百色市| 荔浦县| 南宫市| 文化| 尚义县| 临夏县| 宜君县| 昔阳县|