Spring源碼之七registerListeners()及發(fā)布訂閱模式
大家好,我是程序員田同學(xué)。
今天帶大家解讀refresh()方法中的registerListeners()方法,也就是我們經(jīng)常說的Spring的發(fā)布-訂閱模式。文章首先舉一個(gè)發(fā)布-訂閱模式的樣例,然后講解了發(fā)布-訂閱四個(gè)模式的原理,及對(duì)發(fā)布-訂閱模式所依賴的觀察者模式進(jìn)行了舉例,最后引出該模式在Springboot中的大量應(yīng)用。
照例放一份refresh()方法的源碼,registerListeners()方法位于該方法的第七個(gè)位置。
@Override
public void refresh() throws BeansException, IllegalStateException {
? synchronized (this.startupShutdownMonitor) {
? ?? // Prepare this context for refreshing.
? ?? //1、刷新前的準(zhǔn)備
? ?? prepareRefresh();
? ?? // Tell the subclass to refresh the internal bean factory.
? ?? //2、將會(huì)初始化 BeanFactory、加載 Bean、注冊(cè) Bean
? ?? ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
? ?? // Prepare the bean factory for use in this context.
? ?? //3、設(shè)置 BeanFactory 的類加載器,添加幾個(gè) BeanPostProcessor,手動(dòng)注冊(cè)幾個(gè)特殊的 bean
? ?? prepareBeanFactory(beanFactory);
? ?? try {
? ? ? ? //4、模板方法
? ? ? ? // Allows post-processing of the bean factory in context subclasses.
? ? ? ? postProcessBeanFactory(beanFactory);
? ? ? ? // Invoke factory processors registered as beans in the context.
? ? ? ? //執(zhí)行BeanFactory后置處理器
? ? ? ? invokeBeanFactoryPostProcessors(beanFactory);
? ? ? ? // 5、Register bean processors that intercept bean creation.
? ? ? ? //注冊(cè)bean后置處理器
? ? ? ? registerBeanPostProcessors(beanFactory);
? ? ? ? // Initialize message source for this context.
? ? ? ? //國(guó)際化
? ? ? ? initMessageSource();
? ? ? ? // Initialize event multicaster for this context.
? ? ? ? initApplicationEventMulticaster();
? ? ? ? // Initialize other special beans in specific context subclasses.
? ? ? ? //6、模板方法--springboot實(shí)現(xiàn)了這個(gè)方法
? ? ? ? onRefresh();
? ? ? ? // Check for listener beans and register them.
? ? ? ? //7、注冊(cè)監(jiān)聽器
? ? ? ? registerListeners();
? ? ? ? // Instantiate all remaining (non-lazy-init) singletons.
? ? ? ? //8、完成bean工廠的初始化**方法重要**********************************************
? ? ? ? finishBeanFactoryInitialization(beanFactory);
? ? ? ? //9、 Last step: publish corresponding event.
? ? ? ? finishRefresh();
? ?? }
? ?? catch (BeansException ex) {
? ? ? ? if (logger.isWarnEnabled()) {
? ? ? ? ?? 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;
? ?? }
先大致看一下registerListeners()方法的源碼。
protected void registerListeners() {
? ? ? ? // Register statically specified listeners first.
? ? ? ? // 首先注冊(cè)靜態(tài)的指定的監(jiān)聽器,注冊(cè)的是特殊的事件監(jiān)聽器,而不是配置中的bean
? ? ? ? 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!
? ? ? ? // 這里不會(huì)初始化FactoryBean,我們需要保留所有的普通bean
? ? ? ? // 不會(huì)實(shí)例化這些bean,讓后置處理器可以感知到它們
? ? ? ? String[] listenerBeanNames = getBeanNamesForType(ApplicationListener.class, true, false);
? ? ? ? for (String listenerBeanName : listenerBeanNames) {
? ? ? ? ? ? getApplicationEventMulticaster().addApplicationListenerBean(listenerBeanName);
? ? ? ? }
? ? ? ? // Publish early application events now that we finally have a multicaster...
? ? ? ? // 現(xiàn)在有了事件廣播組,發(fā)布之前的應(yīng)用事件
? ? ? ? Set earlyEventsToProcess = this.earlyApplicationEvents;
? ? ? ? this.earlyApplicationEvents = null;
? ? ? ? if (!CollectionUtils.isEmpty(earlyEventsToProcess)) {
? ? ? ? ? ? for (ApplicationEvent earlyEvent : earlyEventsToProcess) {
? ? ? ? ? ? ? ? getApplicationEventMulticaster().multicastEvent(earlyEvent);
? ? ? ? ? ? }
? ? ? ? }
? ? }
這個(gè)方法要做的很簡(jiǎn)單,就是兩個(gè)循環(huán)遍歷,把Spring通過硬編碼定義的監(jiān)聽器注冊(cè)到容器中,然后把我們自定義的監(jiān)聽器注冊(cè)到容器中,通過這些直接敘述有一些蒼白無力,我們寫一個(gè)簡(jiǎn)單的發(fā)布-訂閱的例子方便理解。
在發(fā)布訂閱模式用需要四個(gè)角色:
ApplicationEvent:事件,每個(gè)實(shí)現(xiàn)類表示一類事件,可攜帶數(shù)據(jù)。抽象類。ApplicationListener:事件監(jiān)聽器,用于接收事件處理時(shí)間。接口。ApplicationEventMulticaster:事件管理者,可以注冊(cè)(添加)/移除/發(fā)布事件。用于事件監(jiān)聽器的注冊(cè)和事件的廣播。接口。ApplicationEventPublisher:事件發(fā)布者,委托事件管理者ApplicationEventMulticaster完成事件發(fā)布。

事件:
@Component
public class MyEvent extends ApplicationEvent {
? ? private static final long serialVersionUID = 1L;
? ? /**
? ? * Create a new ApplicationEvent.
? ? *
? ? * @param source the object on which the event initially occurred (never {@code null})
? ? */
? ? public MyEvent(Object source) {
? ? ? ? super(source);
? ? }
}
事件監(jiān)聽器:
@Component
public class MyEventListener implements ApplicationListener<MyEvent> {
? ? @EventListener //@EventListener注解實(shí)現(xiàn)事件監(jiān)聽
? ? @Override
? ? public void onApplicationEvent(MyEvent event) {
? ? ? ? Object msg = event.getSource();
? ? ? ? System.out.println("自定義事件監(jiān)聽器(MyEventListener1)收到發(fā)布的消息: " + msg);
? ? }
}
事件發(fā)布者:
public static void main(String[] args) {
? ? ? ? System.out.println(1);
? ? ? ? ApplicationContext ac =new AnnotationConfigApplicationContext(MyEventListener.class);
? ? ? ? MyEvent myEvent=new MyEvent(new Object());
? ? ? ? ac.publishEvent(myEvent);
? ? }
那么事件的管理器跑哪去了呢?
我們?cè)?registerListeners()方法上面打一個(gè)斷點(diǎn),看看我們自定義的事件是如何在Spring中起作用的。

第一個(gè)循環(huán)是Spring默認(rèn)的監(jiān)聽器默認(rèn)情況下是空。
我們自定義的事件監(jiān)聽器在第二個(gè)循環(huán)里面被加載到了ApplicationEventMulticaster中,已經(jīng)很明顯了,ApplicationEventMulticaster就是我們的事件管理者。
我們?cè)诎阉麄冎g的關(guān)系詳細(xì)串一下。

注:廣播器和上面的管理者是一個(gè)意思
到這個(gè)階段,事件管理者和監(jiān)聽器都在Spring容器里初始化和注冊(cè)了,之后就可以實(shí)現(xiàn)監(jiān)聽者模式了,對(duì)事件的發(fā)布進(jìn)行監(jiān)聽然后處理。
在就是ac.publishEvent(myEvent);方法發(fā)布事件后進(jìn)行的業(yè)務(wù)處理。
ApplicationContext ac =new AnnotationConfigApplicationContext(MyEventListener.class);
? ? ? ? MyEvent myEvent=new MyEvent(new Object());
? ? ? ? ac.publishEvent(myEvent);
事件監(jiān)聽機(jī)制實(shí)際就是主題-訂閱模式(觀察者模式)的實(shí)現(xiàn),能夠降低代碼耦合。
本文順便把觀察者模式簡(jiǎn)要的敘述一下,方便讀者可以更好的理解。
觀察者(Observer)模式的定義:指多個(gè)對(duì)象間存在一對(duì)多的依賴關(guān)系,當(dāng)一個(gè)對(duì)象的狀態(tài)發(fā)生改變時(shí),所有依賴于它的對(duì)象都得到通知并被自動(dòng)更新。
public class RMBrateTest {
?? public static void main(String[] args) {
? ? ?? Rate rate = new RMBrate();
? ? ?? Company watcher1 = new ImportCompany();
? ? ?? Company watcher2 = new ExportCompany();
? ? ?? rate.add(watcher1);
? ? ?? rate.add(watcher2);
? ? ?? rate.change(10);
? ? ?? rate.change(-9);
?? }
}
//抽象目標(biāo):匯率
abstract class Rate {
?? protected List<Company> companys = new ArrayList<Company>();
?? //增加觀察者方法
?? public void add(Company company) {
? ? ?? companys.add(company);
?? }
?? //刪除觀察者方法
?? public void remove(Company company) {
? ? ?? companys.remove(company);
?? }
?? public abstract void change(int number);
}
//具體目標(biāo):人民幣匯率
class RMBrate extends Rate {
?? public void change(int number) {
? ? ?? for (Company obs : companys) {
? ? ? ? ?? ((Company) obs).response(number);
? ? ?? }
?? }
}
//抽象觀察者:公司
interface Company {
?? void response(int number);
}
//具體觀察者1:進(jìn)口公司
class ImportCompany implements Company {
?? public void response(int number) {
? ? ?? if (number > 0) {
? ? ? ? ?? System.out.println("人民幣匯率升值" + number + "個(gè)基點(diǎn),降低了進(jìn)口產(chǎn)品成本,提升了進(jìn)口公司利潤(rùn)率。");
? ? ?? } else if (number < 0) {
? ? ? ? ?? System.out.println("人民幣匯率貶值" + (-number) + "個(gè)基點(diǎn),提升了進(jìn)口產(chǎn)品成本,降低了進(jìn)口公司利潤(rùn)率。");
? ? ?? }
?? }
}
//具體觀察者2:出口公司
class ExportCompany implements Company {
?? public void response(int number) {
? ? ?? if (number > 0) {
? ? ? ? ?? System.out.println("人民幣匯率升值" + number + "個(gè)基點(diǎn),降低了出口產(chǎn)品收入,降低了出口公司的銷售利潤(rùn)率。");
? ? ?? } else if (number < 0) {
? ? ? ? ?? System.out.println("人民幣匯率貶值" + (-number) + "個(gè)基點(diǎn),提升了出口產(chǎn)品收入,提升了出口公司的銷售利潤(rùn)率。");
? ? ?? }
?? }
}
匯率變化就是一個(gè)事件,當(dāng)該事件發(fā)生時(shí),它的觀察者出口公司和進(jìn)口公司就要做相應(yīng)的變化。
書歸正傳重新回到registerListeners()方法中,這時(shí)所有的監(jiān)聽器都注冊(cè)到了容器中,只等publishEvent()發(fā)布事件以后,就會(huì)執(zhí)行我們監(jiān)聽器中的業(yè)務(wù)邏輯。
據(jù)說在Springboot中應(yīng)用了大量的發(fā)布訂閱模式,抱著求知若渴的態(tài)度我們?nèi)pringboot中摟一眼,在registerListeners()上面打一個(gè)斷點(diǎn),看看和Spring上面的斷點(diǎn)有什么區(qū)別。

在Spirng中監(jiān)聽器默認(rèn)是空,當(dāng)時(shí)在Springboot中有15個(gè)之多,不愧是加強(qiáng)版的Spring。具體這些監(jiān)聽器是干嘛的我們就不深究了,在Springboot中會(huì)對(duì)其逐步拆解的。
好啦,今天對(duì)registerListeners()的方法的剖析也就結(jié)束啦。