龙空技术网

源码阅读的内功心法,SpringBoot里的观察者设计模式

Java成长路 292

前言:

此刻大家对“观察者模式spring”大概比较注重,大家都需要知道一些“观察者模式spring”的相关文章。那么小编在网上搜集了一些对于“观察者模式spring””的相关内容,希望你们能喜欢,各位老铁们快快来学习一下吧!

观察者模式直白解释:可理解为订阅和通知的过程,即订阅者(或者监听者、观察者)向注册和通知中心注册自己以及关注的事件,发布者(或者被观察者、通知者)向注册和通知中心发布事件消息,委托注册和通知中心向关注了该事件的观察者发布通知。通过注册和通知中心解耦了观察者和发布者,观察者不需要关心发布者是谁,只需关注自己对什么感兴趣;发布者不需关心观察者是谁,只需关注自己及时的发布通知。相关类图不再赘述,网上有很多,下图是观察者模式的交互方式,旨在便于理解。

观察者模式交互方式

Spring中的事件编程模型就是观察者设计模式的实现,SpringBoot正是利用了Spring的事件编程模型来实现了Application启动过程中的相关事件的广播。

Spring中观察者模式核心类如下:

ApplicationListener:应用监听者,即观察者,继承了JDK中EventListener,当监听的事件发生后,该类中唯一方法onApplication会被回调;ApplicationEvent:事件抽象类,继承自JDK的EventObject,Spring及SpringBoot中所有事件都是该类的子类;ApplicationEventMulticaster:事件注册和广播中心,用于事件监听器的注册和事件的广播;ApplicationContext:Spring的IOC容器,同时也是发布者,因为ApplicationContext继承了ApplicationEventPublisher,通过publishEvent(Object event)方法发布事件。

分析SpringBoot的启动过程会发现,大量的代码都是在做事件通知和扩展点设置,他们近乎喧宾夺主,占据了SpringBoot启动过程的大半江山,抛去这些扩展点,剩下的核心逻辑无非只是初始化并准备ApplicationContext容器了。关于SpringBoot的启动过程和其他扩展点解析,笔者会在后续发文中逐一解析,现在暂且关注事件模型。

SpringBoot启动核心代码,其中注释标注出了事件通知代码

	public ConfigurableApplicationContext run(String... args) {		StopWatch stopWatch = new StopWatch();		stopWatch.start();		ConfigurableApplicationContext context = null;		Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();		configureHeadlessProperty();    //向注册和通知中心注册SpringBoot监听者,并返回注册和通知中心实例		SpringApplicationRunListeners listeners = getRunListeners(args);    //向注册和通知中心发布应用正在启动的事件通知		listeners.starting();		try {			ApplicationArguments applicationArguments = new DefaultApplicationArguments(					args);      //准备配置环境,并向通知和注册中心发布环境准备好了的事件通知			ConfigurableEnvironment environment = prepareEnvironment(listeners,					applicationArguments);			configureIgnoreBeanInfo(environment);			Banner printedBanner = printBanner(environment);			context = createApplicationContext();			exceptionReporters = getSpringFactoriesInstances(					SpringBootExceptionReporter.class,					new Class[] { ConfigurableApplicationContext.class }, context);			prepareContext(context, environment, listeners, applicationArguments,					printedBanner);			refreshContext(context);			afterRefresh(context, applicationArguments);			stopWatch.stop();			if (this.logStartupInfo) {				new StartupInfoLogger(this.mainApplicationClass)						.logStarted(getApplicationLog(), stopWatch);			}      //向通知和注册中心发布应用启动启动完毕的事件通知			listeners.started(context);			callRunners(context, applicationArguments);		}		catch (Throwable ex) {      //处理运行失败异常,并向通知和注册中心发布应用启动失败事件通知			handleRunFailure(context, listeners, exceptionReporters, ex);			throw new IllegalStateException(ex);		}    //向通知和注册中心发布引用正在运行的事件通知		listeners.running(context);		return context;	}

其中getRunListeners方法,会借助SpringFactoriesLoader机制从spring.factories文件中加载SpringApplicationRunListener类型的实现类,该类为SpringBoot提供的用于监听SpringBoot启动状态的观察者类,其唯一实现为EventPublishingRunListener,位于spring-boot-2.0.0.RELEASE.jar包中META-INF下spring.factories核心代码如下:

# Run Listenersorg.springframework.boot.SpringApplicationRunListener=\org.springframework.boot.context.event.EventPublishingRunListener

SpringFactoriesLoader机制正是SpringBoot自动装配的核心逻辑,关于其原理可以阅读笔者的这篇关于starter的原理解析:拒绝花里胡哨,极简springboot starter及原理,那么SpringBoot中的监听者注册就是通过这种方式注册到注册和通知中心的。

SpringBoot的注册和通知中心为SpringApplicationRunListeners,其实这个名字不够语义化,实际上该类包含了一个SpringApplicationRunListener列表和事件通知方法。

SpingBoot的发布者为SpringApplication实体,SpringBoot的启动核心流程均定义在该类下。

SpringBoot从ApplicationEvent扩展出了SpringApplicationEvent事件抽象类,其子类有ApplicationStartingEvent、ApplicationReadyEvent等,均代表了应用启动状态

那么SpringBoot的事件机制是怎样利用Spring的事件模型的呢,答案就在EventPublishingRunListener这个SpringBoot的监听者中,原来当SpringApplication向注册和通知中心发布启动状态事件后,事件会通知到该观察者,但是该观察者接收到事件后并没做其他事情,仅仅是将该事件通过Spring中的广播者SimpleApplicationEventMulticaster再将事件广播到Spring中的监听者,源码及注释如下:

	@Override	public void starting() {    //initialMulticaster即为Spring中的广播者		this.initialMulticaster.multicastEvent(				new ApplicationStartingEvent(this.application, this.args));	}

那么这些Spring监听者是在什么时候被注册进去的呢,其实在SpringBoot启动之初,会构建SpringApplication对象,这里同样是借助SpringFactoriesLoader机制,来加载SpringBoot提供的一些ApplicationListener的监听者,源码及注释如下:

//SpringApplication构造方法	public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {		this.resourceLoader = resourceLoader;		Assert.notNull(primarySources, "PrimarySources must not be null");		this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));		this.webApplicationType = deduceWebApplicationType();		setInitializers((Collection) getSpringFactoriesInstances(				ApplicationContextInitializer.class));    //通过SpringFactoriesLoader机制注册Spring的监听者		setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));		this.mainApplicationClass = deduceMainApplicationClass();	}

位于spring-boot-2.0.0.RELEASE.jar包中META-INF下spring.factories核心代码如下:

# Application Listenersorg.springframework.context.ApplicationListener=\org.springframework.boot.ClearCachesApplicationListener,\org.springframework.boot.builder.ParentContextCloserApplicationListener,\org.springframework.boot.context.FileEncodingApplicationListener,\org.springframework.boot.context.config.AnsiOutputApplicationListener,\org.springframework.boot.context.config.ConfigFileApplicationListener,\org.springframework.boot.context.config.DelegatingApplicationListener,\org.springframework.boot.context.logging.ClasspathLoggingApplicationListener,\org.springframework.boot.context.logging.LoggingApplicationListener,\org.springframework.boot.liquibase.LiquibaseServiceLocatorApplicationListener

至此,SpringBoot中的观察者模式原理就讲完了,SpringBoot中的监听者可以自定义,并通过SpringFactoriesLoader机制注册,您可以试下以检验自己的理解程度。

如果您熟悉观察者模式这一内功心法,当在研究SpringBoot及Spring源码招式的时候,相信会理解起来事半功倍,希望这篇文章对您理解原理有所帮助。

标签: #观察者模式spring