我自横刀向天笑,去留肝胆两昆仑。这篇文章主要讲述Spring源码之七registerListeners()及发布订阅模式相关的知识,希望能为你提供帮助。
Spring源码之七registerListeners()及发布订阅模式
大家好,我是程序员田同学。
今天带大家解读refresh()方法中的registerListeners()方法,也就是我们经常说的Spring的发布-订阅模式。文章首先举一个发布-订阅模式的样例,然后讲解了发布-订阅四个模式的原理,及对发布-订阅模式所依赖的观察者模式进行了举例,最后引出该模式在Springboot中的大量应用。
照例放一份refresh()方法的源码,registerListeners()方法位于该方法的第七个位置。
@Override
public void refresh() throws BeansException, IllegalStateException
synchronized (this.startupShutdownMonitor)
// Prepare this context for refreshing.
//1、刷新前的准备
prepareRefresh();
// Tell the subclass to refresh the internal bean factory.
//2、将会初始化 BeanFactory、加载 Bean、注册 Bean
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
// Prepare the bean factory for use in this context.
//3、设置 BeanFactory 的类加载器,添加几个 BeanPostProcessor,手动注册几个特殊的 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.
//执行BeanFactory后置处理器
invokeBeanFactoryPostProcessors(beanFactory);
// 5、Register bean processors that intercept bean creation.
//注册bean后置处理器
registerBeanPostProcessors(beanFactory);
// Initialize message source for this context.
//国际化
initMessageSource();
// Initialize event multicaster for this context.
initApplicationEventMulticaster();
// Initialize other special beans in specific context subclasses.
//6、模板方法--springboot实现了这个方法
onRefresh();
// Check for listener beans and register them.
//7、注册监听器
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.
// 首先注册静态的指定的监听器,注册的是特殊的事件监听器,而不是配置中的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!
// 这里不会初始化FactoryBean,我们需要保留所有的普通bean
// 不会实例化这些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...
// 现在有了事件广播组,发布之前的应用事件
Set<
ApplicationEvent>
earlyEventsToProcess = this.earlyApplicationEvents;
this.earlyApplicationEvents = null;
if (!CollectionUtils.isEmpty(earlyEventsToProcess))
for (ApplicationEvent earlyEvent : earlyEventsToProcess)
getApplicationEventMulticaster().multicastEvent(earlyEvent);
这个方法要做的很简单,就是两个循环遍历,把Spring通过硬编码定义的监听器注册到容器中,然后把我们自定义的监听器注册到容器中,通过这些直接叙述有一些苍白无力,我们写一个简单的发布-订阅的例子方便理解。
在发布订阅模式用需要四个角色:
ApplicationEvent:事件,每个实现类表示一类事件,可携带数据。抽象类。
ApplicationListener:事件监听器,用于接收事件处理时间。接口。
ApplicationEventMulticaster:事件管理者,可以注册(添加)/移除/发布事件。用于事件监听器的注册和事件的广播。接口。
ApplicationEventPublisher:事件发布者,委托事件管理者ApplicationEventMulticaster完成事件发布。
文章图片
事件:
@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);
事件监听器:
@Component
public class MyEventListener implements ApplicationListener<
MyEvent>
@EventListener //@EventListener注解实现事件监听
@Override
public void onApplicationEvent(MyEvent event)
Object msg = event.getSource();
System.out.println("自定义事件监听器(MyEventListener1)收到发布的消息: " + msg);
事件发布者:
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);
那么事件的管理器跑哪去了呢?
【Spring源码之七registerListeners()及发布订阅模式】我们在 registerListeners()方法上面打一个断点,看看我们自定义的事件是如何在Spring中起作用的。
文章图片
第一个循环是Spring默认的监听器默认情况下是空。
我们自定义的事件监听器在第二个循环里面被加载到了ApplicationEventMulticaster中,已经很明显了,ApplicationEventMulticaster就是我们的事件管理者。
我们在把他们之间的关系详细串一下。
文章图片
注:广播器和上面的管理者是一个意思
到这个阶段,事件管理者和监听器都在Spring容器里初始化和注册了,之后就可以实现监听者模式了,对事件的发布进行监听然后处理。
在就是ac.publishEvent(myEvent); 方法发布事件后进行的业务处理。
ApplicationContext ac =new AnnotationConfigApplicationContext(MyEventListener.class);
MyEvent myEvent=new MyEvent(new Object());
ac.publishEvent(myEvent);
事件监听机制实际就是主题-订阅模式(观察者模式)的实现,能够降低代码耦合。
本文顺便把观察者模式简要的叙述一下,方便读者可以更好的理解。
观察者(Observer)模式的定义:指多个对象间存在一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并被自动更新。
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);
//抽象目标:汇率
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);
//具体目标:人民币汇率
class RMBrate extends Rate
public void change(int number)
for (Company obs : companys)
((Company) obs).response(number);
//抽象观察者:公司
interface Company
void response(int number);
//具体观察者1:进口公司
class ImportCompany implements Company
public void response(int number)
if (number >
0)
System.out.println("人民币汇率升值" + number + "个基点,降低了进口产品成本,提升了进口公司利润率。");
else if (number <
0)
System.out.println("人民币汇率贬值" + (-number) + "个基点,提升了进口产品成本,降低了进口公司利润率。");
//具体观察者2:出口公司
class ExportCompany implements Company
public void response(int number)
if (number >
0)
System.out.println("人民币汇率升值" + number + "个基点,降低了出口产品收入,降低了出口公司的销售利润率。");
else if (number <
0)
System.out.println("人民币汇率贬值" + (-number) + "个基点,提升了出口产品收入,提升了出口公司的销售利润率。");
汇率变化就是一个事件,当该事件发生时,它的观察者出口公司和进口公司就要做相应的变化。
书归正传重新回到registerListeners()方法中,这时所有的监听器都注册到了容器中,只等publishEvent()发布事件以后,就会执行我们监听器中的业务逻辑。
据说在Springboot中应用了大量的发布订阅模式,抱着求知若渴的态度我们去Springboot中搂一眼,在registerListeners()上面打一个断点,看看和Spring上面的断点有什么区别。
文章图片
在Spirng中监听器默认是空,当时在Springboot中有15个之多,不愧是加强版的Spring。具体这些监听器是干嘛的我们就不深究了,在Springboot中会对其逐步拆解的。
好啦,今天对registerListeners()的方法的剖析也就结束啦。
推荐阅读
- Linux之traceroute命令
- C语言函数中的传值和传址
- Win10(64)专业版电脑如何修改时间
- linux网络第一周作业
- Shell流程控制
- Python代码的良好习惯
- 线上环境从0到1实践从传统运维架构到k8s(ACK)的转型
- #yyds干货盘点#K8S 之节点资源
- Linux第十二周(数据库主从复制,主主复制,xtrabackup实现备份,Mycat读写分离,ansible常用模块)