深刻理解Spring IOC之擴展篇(七)、Spring中的event以及自定義event

本篇說的是Spring中event的概念以及對它的擴展,順便說說它的原理。spring

Spring中事件的頂層類是ApplicationEvent,咱們自定義事件只須要繼承這個類便可(更好的選擇實際上是繼承ApplicationContextEvent),好,咱們那就按照這個思路自定義一下咱們本身的事件:編程

public class MyEvent1 extends ApplicationEvent {

    public MyEvent1(ApplicationContext source) {
        super(source);
    }

    public void doSomething(){
        System.out.println("此處發生了一件詭異的事情");
    }
}
複製代碼

此時,咱們本身定義的這個MyEvent1在Spring容器中是沒有對應的監聽器的,也就是說此時Spring還不認識咱們這個事件,咱們還須要定義咱們本身的監聽器,定義監聽器的姿式有三種,咱們先來看看第一種:設計模式

姿式1: 實現SmartApplicationListener接口緩存

@Component
public class MyEvent1Listener1 implements SmartApplicationListener {

    @Override
    public void onApplicationEvent(ApplicationEvent event) {
        MyEvent1 event1 = (MyEvent1) event;
        System.out.println("Listener1 發現了一件詭異的事情");
        event1.doSomething();
    }

    // 必須重寫這個邏輯,這個決定咱們這個監聽器是否是能識別到咱們這個事件
    @Override
    public boolean supportsEventType(Class<? extends ApplicationEvent> eventType) {
        // eventType 是接收到的事件的屬性
        return MyEvent1.class.equals(eventType);
    }
}
複製代碼

姿式2:實現GenericApplicationListener接口bash

@Component
public class MyEvent1Listener2 implements GenericApplicationListener {

    @Override
    public void onApplicationEvent(ApplicationEvent event) {
        MyEvent1 event1 = (MyEvent1) event;
        System.out.println("Listener2 發現了一件詭異的事情");
        event1.doSomething();
    }

    // 必須重寫這個方法,ResolvableType中的type屬性就是接收到的事件的類型
    @Override
    public boolean supportsEventType(ResolvableType eventType) {
        // 接收到的事件的屬性
        return MyEvent1.class.equals(eventType.getType());
    }
}
複製代碼

姿式3:實現ApplicationListener接口多線程

// 注意這個泛型哈,這個是決定能不能接收到的條件
@Component
public class MyEvent1Listener3 implements ApplicationListener<MyEvent1> {

    @Override
    public void onApplicationEvent(MyEvent1 event) {
        MyEvent1 event1 = (MyEvent1) event;
        System.out.println("Listener3 發現了一件詭異的事情");
        event1.doSomething();
    }
}
複製代碼

配置類併發

@Configuration
@ComponentScan(basePackages = "com.example.demo.external7")
public class Config {
}
複製代碼

測試代碼:app

public static void main(String[] args) {
        AnnotationConfigApplicationContext annotationConfigApplicationContext = new AnnotationConfigApplicationContext(Config.class);
        annotationConfigApplicationContext.publishEvent(new MyEvent1(annotationConfigApplicationContext));
}
複製代碼

結果:ide

咱們能夠看到,姿式1和姿式2的本質都是同樣的,只是在實現supportsEventType時這裏傳的參數不同,姿式3看起來是最簡單。它們監聽的原理是什麼呢,咱們來具體的看一看源碼:post

咱們先來看以前的refresh方法,注意看其中的7和9就行,這兩處主要是事件廣播器的初始化

public void refresh() throws BeansException, IllegalStateException {
	synchronized (this.startupShutdownMonitor) {
		// 1.加載前的準備工做
		prepareRefresh();
		// 2.獲取一個全新的beanFactory實例
		ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
		// 3.初始化beanFactory,給它設置各類
		prepareBeanFactory(beanFactory);
		try {
			// (空方法,讓子類重寫的)容許對beanFactory中的東西作一些前置的修改,能夠是增長個BeanFactoryPostProcessors
			// 這種的,也能夠給增長個BeanDefinition,也能夠增長一些讓beanFactory在自動裝配時候忽略掉的接口,也能夠增長一些特定場景使用的bean,
			// 好比有的後代就增長了新的scope bean 等等。可是重點是,我剛纔說的這些都是基於一種具體場景的,所以這個抽象類裏,
			// 這個方法是空的(不弄成抽象的緣由是不強迫子類去實現)
			postProcessBeanFactory(beanFactory);

			// 4.觸發調用全部的BeanFactoryPostProcessors
			invokeBeanFactoryPostProcessors(beanFactory);

			// 5.註冊全部的BeanPostProcessor
			registerBeanPostProcessors(beanFactory);

			// 6.初始化支持國際化的東東
			initMessageSource();

			// 7. 初始化事件廣播器
			initApplicationEventMulticaster();

			// 8. 初始化其餘特殊的bean
			onRefresh();

			// 9. 註冊監聽器
			registerListeners();

			// 10. 實例化bean( 重點 )
			finishBeanFactoryInitialization(beanFactory);

			finishRefresh();
		} catch (BeansException ex) {
			logger.warn("Exception encountered during context initialization - cancelling refresh attempt", ex);
			// Destroy already created singletons to avoid dangling resources.
			destroyBeans();
			// Reset 'active' flag.
			cancelRefresh(ex);
			// Propagate exception to caller.
			throw ex;
		}
	}
}
複製代碼

第7處的源碼以下:

// 整個方法是爲了初始化事件廣播器的
protected void initApplicationEventMulticaster() {
        // 獲取前面已經建立好的BeanFactory實例
	ConfigurableListableBeanFactory beanFactory = getBeanFactory();
	
	if (beanFactory.containsLocalBean(APPLICATION_EVENT_MULTICASTER_BEAN_NAME)) {
	    // this.applicationEventMulticaster就是spring容器中的事件廣播器
	    // 若是容器中已經有事件廣播器,就用容器中的
	    // 從這裏能夠看出來,若是想用本身的事件廣播器,bean的名字就必須爲applicationEventMulticaster
		this.applicationEventMulticaster =
				beanFactory.getBean(APPLICATION_EVENT_MULTICASTER_BEAN_NAME, ApplicationEventMulticaster.class);
		if (logger.isTraceEnabled()) {
			logger.trace("Using ApplicationEventMulticaster [" + this.applicationEventMulticaster + "]");
		}
	}else {
	// 若是容器中沒有,則初始化事件廣播器SimpleApplicationEventMulticaster
	// 其實就是new 了一下
		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() + "]");
		}
	}
}
複製代碼

第9處的源碼:

protected void registerListeners() {
	// 一、將容器中的全部監聽器放到事件廣播器中
	//  注意,此時容器全部的bean尚未初始化,所以,通常來講getApplicationListeners方法返回的都是空集合
	for (ApplicationListener<?> listener : getApplicationListeners()) {
		getApplicationEventMulticaster().addApplicationListener(listener);
	}

	// 二、把全部的listener的bean的名稱加到事件廣播器裏面
	// 注意此時是不初始化的,由於還沒到初始化的時候,初始化bean應該在refresh方法調用finishBeanFactoryInitialization時
	String[] listenerBeanNames = getBeanNamesForType(ApplicationListener.class, true, false);
	for (String listenerBeanName : listenerBeanNames) {
		getApplicationEventMulticaster().addApplicationListenerBean(listenerBeanName);
	}

	// 發佈事件廣播器初始化結束事件(若是有的話。。說實話我沒找到在哪)
	Set<ApplicationEvent> earlyEventsToProcess = this.earlyApplicationEvents;
	this.earlyApplicationEvents = null;
	if (earlyEventsToProcess != null) {
		for (ApplicationEvent earlyEvent : earlyEventsToProcess) {
			getApplicationEventMulticaster().multicastEvent(earlyEvent);
		}
	}
}
複製代碼

咱們看了上面的代碼以後發現第2步只是把全部的listener的bean放到事件廣播器中,可是並無初始化,那麼是何時對linstener進行初始化的呢?在refresh方法中有調用一個prepareBeanFactory方法,其中有一句代碼以下:

咱們能夠看到,prepareBeanFactory方法中給beanFactory添加了一個ApplicationListenerDetector,這玩意本質上是一個BeanPostProcessor,那它是作什麼的呢?咱們直接來看它的源碼:

我這裏僅僅列核心方法的代碼了哈

@Override
public Object postProcessAfterInitialization(Object bean, String beanName) {
        // 若是實例是個ApplicationListener的實例
	if (bean instanceof ApplicationListener) {
	        // 這裏的this.singletonNames的key是beanName,value爲key對應的bean是不是單例,是個Boolean類型的值
		Boolean flag = this.singletonNames.get(beanName);
		
		// 若是是單例,則以listener的方式添加到容器中,注意這裏也同時會添加到事件廣播器中
		if (Boolean.TRUE.equals(flag)) {
			// singleton bean (top-level or inner): register on the fly
			this.applicationContext.addApplicationListener((ApplicationListener<?>) bean);
		// 若是不是單例,那就不能添加到容器中,listener必須是單例,否則一個事件就有被多個相同的listener屢次監聽的危險。。
		}else if (Boolean.FALSE.equals(flag)) {
			if (logger.isWarnEnabled() && !this.applicationContext.containsBean(beanName)) {
				// ....打日誌代碼省略....
			}
			this.singletonNames.remove(beanName);
		}
	}
	return bean;
}
複製代碼

初始化事件監聽器的邏輯說完了,咱們來看看發佈事件的源碼,看完這塊,你會明白上面三種姿式的不一樣之處。

我須要先說明的是,spring的事件監聽機制就是一個很簡單的監聽者模式,我不會在這裏說這個設計模式,不知道的請自行百度。

這些代碼位於AbstractApplicationContex中,注意由於ApplicationContex也繼承了ApplicationEventPublisher,所以它也是個事件廣播器。

@Override
// 調用的是最下面的方法
public void publishEvent(ApplicationEvent event) {
	publishEvent(event, null);
}

@Override
// 調用的是最下面的方法
public void publishEvent(Object event) {
	publishEvent(event, null);
}

// 上面的兩個方法都是調用的這裏的方法
protected void publishEvent(Object event, @Nullable ResolvableType eventType) {
        // 要發佈的事件必須不能爲null
        // 能夠學習學習這個處理null的方式
	Assert.notNull(event, "Event must not be null");

	// Decorate event as an ApplicationEvent if necessary
	ApplicationEvent applicationEvent;
	// 若是event是spring中的ApplicationEvent的實例,則把Object類型轉成ApplicationEvent
	if (event instanceof ApplicationEvent) {
		applicationEvent = (ApplicationEvent) event;
	} else {
	        // 這裏的封裝,主要是爲了獲取類型,由於PayloadApplicationEvent中的ResolvableType就是event的實際類型
		applicationEvent = new PayloadApplicationEvent<>(this, event);
		// 方法參數傳過來的eventType是null時就用剛剛解析出來的類型
		if (eventType == null) {
			eventType = ((PayloadApplicationEvent<?>) applicationEvent).getResolvableType();
		}
	}

	// this.earlyApplicationEvents 到這裏通常都是空
	if (this.earlyApplicationEvents != null) {
		this.earlyApplicationEvents.add(applicationEvent);
	} else {
	// 使用咱們剛剛建立的事件廣播器來發布事件
		getApplicationEventMulticaster().multicastEvent(applicationEvent, eventType);
	}

	// 若是父IOC容器也存在的話,同時調用父容器發佈事件
	if (this.parent != null) {
		if (this.parent instanceof AbstractApplicationContext) {
			((AbstractApplicationContext) this.parent).publishEvent(event, eventType);
		} else {
			this.parent.publishEvent(event);
		}
	}
}
複製代碼

咱們來看一下事件廣播器SimpleApplicationEventMulticaster 中 廣播事件的 multicastEvent方法:(這裏是個監聽者模式)

@Override
public void multicastEvent(final ApplicationEvent event, @Nullable ResolvableType eventType) {
    // 這時候傳過來的eventType已經不是null了
	ResolvableType type = (eventType != null ? eventType : resolveDefaultEventType(event));
	// 獲取執行器,說白了就是個線程池
	Executor executor = getTaskExecutor();
	// 一、獲取該eventType對應的listener,並循環調用listener監聽該事件的方法
	for (ApplicationListener<?> listener : getApplicationListeners(event, type)) {
	    // 若是有多線程線程池就併發的去執行,若是沒有就一個一個來
		if (executor != null) {
			executor.execute(() -> invokeListener(listener, event));
		}
		else {
			invokeListener(listener, event);
		}
	}
}
複製代碼

上面代碼塊中的第1處調用的getApplicationListeners方法,會返回這個eventType對應的listener,這個方法中是怎樣獲取的呢?注意這裏很是重要,咱們一塊兒來看看:

protected Collection<ApplicationListener<?>> getApplicationListeners(
			ApplicationEvent event, ResolvableType eventType) {
    // 獲取事件源,這裏通常都是ApplicationContext
	Object source = event.getSource();
	Class<?> sourceType = (source != null ? source.getClass() : null);
	// 構建拿緩存的key
	ListenerCacheKey cacheKey = new ListenerCacheKey(eventType, sourceType);

	// 先從上面構造的key從緩存中拿
	ListenerRetriever retriever = this.retrieverCache.get(cacheKey);
	// 若是緩存中有則返回
	if (retriever != null) {
		return retriever.getApplicationListeners();
	}
    // 能走到這裏說明緩存中是沒有的,因此須要咱們
    // if裏的條件總的來講是在檢查event對應的class有沒有被加載
	if (this.beanClassLoader == null ||
			(ClassUtils.isCacheSafe(event.getClass(), this.beanClassLoader) &&
					(sourceType == null || ClassUtils.isCacheSafe(sourceType, this.beanClassLoader)))) {
		// 加鎖進行操做
		synchronized (this.retrievalMutex) {
		    // 加上鎖以後再獲取一次,由於可能別的線程在上面的獲取到加鎖的這段操做已經設置好了緩存
		    // 這個也是併發編程中須要注意的一個很重要的點
			retriever = this.retrieverCache.get(cacheKey);
			// 緩存中有則返回
			if (retriever != null) {
				return retriever.getApplicationListeners();
			}
			retriever = new ListenerRetriever(true);
			// 一、這裏纔是真正的去拿listener的邏輯
			Collection<ApplicationListener<?>> listeners =
					retrieveApplicationListeners(eventType, sourceType, retriever);
			// 把剛剛拿出來的listener放到緩存中
			this.retrieverCache.put(cacheKey, retriever);
			return listeners;
		}
	}
	else {
		// No ListenerRetriever caching -> no synchronization necessary
		return retrieveApplicationListeners(eventType, sourceType, null);
	}
}
複製代碼

咱們來看看1處的這個retrieveApplicationListeners方法:

private Collection<ApplicationListener<?>> retrieveApplicationListeners(
			ResolvableType eventType, @Nullable Class<?> sourceType, @Nullable ListenerRetriever retriever) {
        // 表明結果的集合
	List<ApplicationListener<?>> allListeners = new ArrayList<>();
	Set<ApplicationListener<?>> listeners;
	Set<String> listenerBeans;
	synchronized (this.retrievalMutex) {
	// 添加默認的listener
		listeners = new LinkedHashSet<>(this.defaultRetriever.applicationListeners);
		listenerBeans = new LinkedHashSet<>(this.defaultRetriever.applicationListenerBeans);
	}
	for (ApplicationListener<?> listener : listeners) {
	// 這裏的supportsEvent方法,是使用GenericApplicationListener或SmartApplicationListener中的supportsEventType來進行判斷的,若是不是這兩個的實例,就會根據泛型找(取決於listener是哪一個的實例)
		if (supportsEvent(listener, eventType, sourceType)) {
			if (retriever != null) {
				retriever.applicationListeners.add(listener);
			}
			allListeners.add(listener);
		}
	}
	if (!listenerBeans.isEmpty()) {
		BeanFactory beanFactory = getBeanFactory();
		for (String listenerBeanName : listenerBeans) {
			try {
				Class<?> listenerType = beanFactory.getType(listenerBeanName);
				// 注意這裏這個supportsEvent方法,是爲了判斷當前bean是否是一個支持監聽事件的listener,和上面的判斷方法不同
				if (listenerType == null || supportsEvent(listenerType, eventType)) {
					ApplicationListener<?> listener =
							beanFactory.getBean(listenerBeanName, ApplicationListener.class);
					// 這個判斷邏輯和上面同樣
					if (!allListeners.contains(listener) && supportsEvent(listener, eventType, sourceType)) {
						if (retriever != null) {
							if (beanFactory.isSingleton(listenerBeanName)) {
								retriever.applicationListeners.add(listener);
							}
							else {
								retriever.applicationListenerBeans.add(listenerBeanName);
							}
						}
						allListeners.add(listener);
					}
				}
			}
			catch (NoSuchBeanDefinitionException ex) {
				// Singleton listener instance (without backing bean definition) disappeared -
				// probably in the middle of the destruction phase
			}
		}
	}
	
	// 排序並加入結果集合
	AnnotationAwareOrderComparator.sort(allListeners);
	if (retriever != null && retriever.applicationListenerBeans.isEmpty()) {
		retriever.applicationListeners.clear();
		retriever.applicationListeners.addAll(allListeners);
	}
	return allListeners;
}
複製代碼

咱們能夠看出來,其實三種姿式實現的listener也就是在第一次監聽事件的時候性能不同,由於泛型是須要反射去拿的,後邊再使用的時候就同樣了,由於已經有緩存了,其餘的都是同樣的。

相關文章
相關標籤/搜索