上一节咱们相熟了SpringApplication的创立和run办法的脉络。这一节就来先剖析下脉络的中第一个比拟有意思的扩大点—SpringApplicationRunListeners。
如下:
SpringApplicationRunListeners在run办法中地位
在之前的run办法中,很多容器(context)相干的办法,配置(Environment)相干的办法,SpringApplicationRunListeners的代码比拟扩散。
之前咱们说过要抓大放小,咱们剖析SpringApplicationRunListeners,你能够概括run办法代码如下:
public ConfigurableApplicationContext run(String... args) { StopWatch stopWatch = new StopWatch(); stopWatch.start(); ConfigurableApplicationContext context = null; Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>(); configureHeadlessProperty(); SpringApplicationRunListeners listeners = getRunListeners(args); listeners.starting(); //一些逻辑 return context;}
如下图:
再进行抓大放小,后面两步不重要,变成如下:
public ConfigurableApplicationContext run(String... args) { //一些逻辑 SpringApplicationRunListeners listeners = getRunListeners(args); listeners.starting(); //一些逻辑 listeners.started(context); //一些逻辑 listeners.running(context); //一些逻辑 return context;}
晓得了SpringApplicationRunListeners在run办法的地位逻辑后,根本能够看出它是通过一个办法获取到所有的listeners,之后执行listeners的不同办法。
如下图所示:
那它扩大是如何设计的,有什么亮点呢?让咱们来认真看看吧。
SpringApplicationRunListeners的扩大是如何设计的?
首先SpringApplicationRunListeners扩大是一个列表,那listeners是怎么获取的呢?代码如下:
private SpringApplicationRunListeners getRunListeners(String[] args) { Class<?>[] types = new Class<?>[] { SpringApplication.class, String[].class }; return new SpringApplicationRunListeners(logger, getSpringFactoriesInstances(SpringApplicationRunListener.class, types, this, args)); } SpringApplicationRunListeners(Log log, Collection<? extends SpringApplicationRunListener> listeners) { this.log = log; this.listeners = new ArrayList<>(listeners); }
你发现还是依据之前的工具办法,getSpringFactoriesInstances获取的。这个工具办法,能够通过classLoader获取classPath指定地位某个接口所有实现类的实例对象列表。
这里显著获取的就是SpringApplicationRunListener这个接口的实现列表。之后创立了一个对象SpringApplicationRunListeners,存储了这个列表。如下图所示:
依照上图,ClassLoader会扫描所有的资源文件,包含jar中的,只有存在这个META-INF目录,并且在spring-factories中存在无关SpringApplicationRunListeners的定义的类,就会被加载到。比方,SpringBoot的jar中,就有如下的定义:
尽管本次例子中只找到一个SpringApplicationRunListeners实现的定义EventPublishingRunListener,然而这个查找思路和办法是很值得咱们借鉴的。
因为ClassLoader次要就是负责ClassPath下的资源和class文件的加载的,封装一个工具,就能够指定查问某个接口实现类。
SpringApplicationRunListeners这个类的除了存储listeners这List之外,其余的办法脉络清一色的,如下:
class SpringApplicationRunListeners { private final Log log; private final List<SpringApplicationRunListener> listeners; SpringApplicationRunListeners(Log log, Collection<? extends SpringApplicationRunListener> listeners) { this.log = log; this.listeners = new ArrayList<>(listeners); } void starting() { for (SpringApplicationRunListener listener : this.listeners) { listener.starting(); } } void environmentPrepared(ConfigurableEnvironment environment) { for (SpringApplicationRunListener listener : this.listeners) { listener.environmentPrepared(environment); } } void contextPrepared(ConfigurableApplicationContext context) { for (SpringApplicationRunListener listener : this.listeners) { listener.contextPrepared(context); } } void contextLoaded(ConfigurableApplicationContext context) { for (SpringApplicationRunListener listener : this.listeners) { listener.contextLoaded(context); } } void started(ConfigurableApplicationContext context) { for (SpringApplicationRunListener listener : this.listeners) { listener.started(context); } } void running(ConfigurableApplicationContext context) { for (SpringApplicationRunListener listener : this.listeners) { listener.running(context); } } void failed(ConfigurableApplicationContext context, Throwable exception) { for (SpringApplicationRunListener listener : this.listeners) { callFailedListener(listener, context, exception); } }
思考点:扩大点SpringApplicationRunListeners,扩大点的外围思路,是封装一个Listener接口实现列表到一个类中,这个类通过不同的办法遍历这个接口实现列表,从而实现不同阶段的扩大。
这个思路是十分值得咱们学习。
别离通过如下办法做了不同的扩大
1)对启动流程做扩大starting、started、failed、running办法
2)对配置文件做扩大environmentPrepared办法
3)对容器做扩大contextPrepared、contextLoaded办法
下面整体如下图:
很多人跟我说,SpringBoot的代码简单,不好了解。其实如果把你想成SpringBoot的开发者,这些其实就开发设计的惯例思维而已,你一步一步拆解脉络和细节,其实也就没有什么。当你有这种心态,你就不会感觉它简单或者难了。
默认的EventPublishingRunListener又做了那些扩大设计?
既然获取到了的SpringApplicationRunListeners,只有一个EventPublishingRunListener,那这个做了怎么样的扩大设计和实现呢?
EventPublishingRunListener从名字上其实就能看进去,EventPublishing,它的设计实现和事件公布相干。让咱们来一起看下:
//EventPublishingRunListener.javapublic class EventPublishingRunListener implements SpringApplicationRunListener, Ordered { private final SpringApplication application; private final String[] args; private final SimpleApplicationEventMulticaster initialMulticaster; public EventPublishingRunListener(SpringApplication application, String[] args) { this.application = application; this.args = args; this.initialMulticaster = new SimpleApplicationEventMulticaster(); for (ApplicationListener<?> listener : application.getListeners()) { this.initialMulticaster.addApplicationListener(listener); } } @Override public void starting() { this.initialMulticaster.multicastEvent( new ApplicationStartingEvent(this.application, this.args)); } //省略其余扩大办法逻辑}//SimpleApplicationEventMulticaster.java @Override public void multicastEvent(ApplicationEvent event) { multicastEvent(event, resolveDefaultEventType(event)); } @Override public void multicastEvent(final ApplicationEvent event, @Nullable ResolvableType eventType) { ResolvableType type = (eventType != null ? eventType : resolveDefaultEventType(event)); for (final ApplicationListener<?> listener : getApplicationListeners(event, type)) { Executor executor = getTaskExecutor(); if (executor != null) { executor.execute(() -> invokeListener(listener, event)); } else { invokeListener(listener, event); } } }
下面的扩大其实大体能够看进去:
1)封装了一个组件SimpleApplicationEventMulticaster,事件播送器
2)通过播送器执行multicastEvent办法进行不同事件的播送,播送理论就是依据事件的类型判断是否要执行事件。
比方:ApplicationStartingEvent 能够有如下判断:
if( Event instanceof ApplicationStartingEvent){ //doSomething}
3)既然是播送,就必定会告诉多集体,所以每个事件其实播送给一个List<ApplicationListener>, 每一个ApplicationListener都会有相似2)步骤的判断,是否执行这个事件,做一些操作。另外这个List<ApplicationListener>获取办法,也同样是用工具类从Classpath获取到的列表,不过基于事件类型做一个筛选而已
整体过程如下图所示:
能够看出事件播送中有很多亮点设计:比方查问缓存的设计、ClassLoader查找类、List汇合封装扩大操作,SpringApplicationRunListeners对立分类管理扩大操作
另外,这里简略提一下,获取到ApplicationStartingEvent 相干ApplicationListener次要有4个,它们做的事件不是很重要,这里简略提一下就好了。
1)LoggingApplicationListener:初始化日志零碎组件,默认应用logback
2)BackgroundPreinitializer:
启动后盾线程初始化转换器、校验器,Json序列化器,字符集器等
ConversionServiceInitializer
ValidationInitializer
MessageConverterInitializer
MBeanFactoryInitializer
JacksonInitializer
CharsetInitializer
3)DelegatingApplicationListener 执行context.listener.classes零碎属性中委托的listener,默认没有,什么都不做
4)LiquibaseServiceLocatorApplicationListener 替换ServiceLocator为Spring boot可执行的版本(不晓得什么意思,不太重要)
小结
其实这一节,咱们次要理解了SpringBoot的一个扩大点设计SpringApplicationRunListeners。并没有找到咱们想要找到的Spring容器创立和web容器启动、主动拆卸配置的这些外围性能。
不过,你应该学到了很多设计思路,对SpringApplication整体的流程,有了更清晰的意识。咱们下一节持续来剖析run办法,看可不可以找到咱们想找的逻辑。
咱们下一节再见!
本文由博客一文多发平台 OpenWrite 公布!