參考版本:2.0.8.RELEASE
啓動方法run中運行監聽器的啓動
查找 jar包中META-INF/spring.factories中SpringApplicationRunListener的定義,如果只引入了springbootstarter包的話,這裏只定義了一個監聽器EventPublishingRunListener
# Run Listeners
org.springframework.boot.SpringApplicationRunListener=\
org.springframework.boot.context.event.EventPublishingRunListenerSpringApplicationRunListeners listeners = getRunListeners(args); listeners.starting();
SpringApplicationRunListeners:這個類是SpringApplicationRunListener的一個集合
public void starting() { for (SpringApplicationRunListener listener : this.listeners) { listener.starting(); } }
下面來看EventPublishingRunListener的starting方法
EventPublishingRunListener:繼承了SpringApplicationRunListener,使用初始化好的事件廣播器廣播Application啓動事件,這個類的接口實現主要有initialMulticaster來完成---裝飾器模式
@Override public void starting() { this.initialMulticaster.multicastEvent( new ApplicationStartingEvent(this.application, this.args)); }
接下來看是如果廣播事件的:spring中的事件監控--觀察者模式
SimpleApplicationEventMulticaster:是接口ApplicationEventMulticaster的一個簡單實現類,將所有的事件廣播給所有的監聽器,每個監聽器只關心自己要監聽的事件,不關心的就會忽略。默認在調用(當前)的線程中調用監聽器,如果定義了線程池就會在新的線程中調用。
@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); } } }
從代碼getApplicationListeners(event, type)的結果來看,有以下四種監聽器都監聽了ApplicationStartingEvent,接下來每種監聽器逐個執行
/** * Invoke the given listener with the given event. * @param listener the ApplicationListener to invoke * @param event the current event to propagate * @since 4.1 */ protected void invokeListener(ApplicationListener<?> listener, ApplicationEvent event) { ErrorHandler errorHandler = getErrorHandler(); if (errorHandler != null) { try { doInvokeListener(listener, event); } catch (Throwable err) { errorHandler.handleError(err); } } else { doInvokeListener(listener, event); } }
private void doInvokeListener(ApplicationListener listener, ApplicationEvent event) { try { listener.onApplicationEvent(event);//這句是重點啦,要到具體對應的監聽器中查看 } catch (ClassCastException ex) { String msg = ex.getMessage(); if (msg == null || matchesClassCastMessage(msg, event.getClass())) { // Possibly a lambda-defined listener which we could not resolve the generic event type for // -> let's suppress the exception and just log a debug message. Log logger = LogFactory.getLog(getClass()); if (logger.isDebugEnabled()) { logger.debug("Non-matching event type for listener: " + listener, ex); } } else { throw ex; } } }
接下來一個個看每個監聽器在監聽事件發生後都做了哪些事情 :
1、DelegatingApplicationListener:委派監聽器,委派給那些在環境屬性context.listener.classes指定的那些監聽器。
@Override public void onApplicationEvent(ApplicationEvent event) { if (event instanceof ApplicationEnvironmentPreparedEvent) { List<ApplicationListener<ApplicationEvent>> delegates = getListeners( ((ApplicationEnvironmentPreparedEvent) event).getEnvironment()); if (delegates.isEmpty()) { return; } this.multicaster = new SimpleApplicationEventMulticaster(); for (ApplicationListener<ApplicationEvent> listener : delegates) { this.multicaster.addApplicationListener(listener); } } if (this.multicaster != null) { this.multicaster.multicastEvent(event); } }
2、LiquibaseServiceLocatorApplicationListener
3、BackgroundPreinitializer:對於一些耗時的任務使用一個後臺線程儘早觸發它們開始執行初始化,這是Springboot的缺省行爲。這些初始化動作也可以叫做預初始化。 設置系統屬性spring.backgroundpreinitializer.ignore爲true可以禁用該機制。 該機制被禁用時,相應的初始化任務會發生在前臺線程。
看看都做了哪些初始化任務:
// 記錄預初始化任務是否已經在執行,初始值設置爲false,表示未開始
private static final AtomicBoolean preinitializationStarted = new AtomicBoolean(
false);// 記錄預初始化任務是否已經完成,1表示未完成,0表示完成
private static final CountDownLatch preinitializationComplete = new CountDownLatch(1);@Override public void onApplicationEvent(SpringApplicationEvent event) { if (event instanceof ApplicationStartingEvent && preinitializationStarted.compareAndSet(false, true)) { //如果當前事件是ApplicationStartingEvent,並且預初始化任務尚未執行 // 則 :將preinitializationStarted設置爲預初始化任務開始執行; // 開始執行預初始化任務; performPreinitialization(); } if ((event instanceof ApplicationReadyEvent || event instanceof ApplicationFailedEvent) && preinitializationStarted.get()) { try { preinitializationComplete.await(); } catch (InterruptedException ex) { Thread.currentThread().interrupt(); } } } private void performPreinitialization() { try { Thread thread = new Thread(new Runnable() { @Override public void run() { runSafely(new ConversionServiceInitializer()); runSafely(new ValidationInitializer()); runSafely(new MessageConverterInitializer()); runSafely(new MBeanFactoryInitializer()); runSafely(new JacksonInitializer()); runSafely(new CharsetInitializer()); preinitializationComplete.countDown(); } public void runSafely(Runnable runnable) { try { runnable.run(); } catch (Throwable ex) { // Ignore } } }, "background-preinit"); thread.start(); } catch (Exception ex) { // This will fail on GAE where creating threads is prohibited. We can safely // continue but startup will be slightly slower as the initialization will now // happen on the main thread. preinitializationComplete.countDown(); } }
4、LoggingApplicationListener:配置日誌系統。如果有logging.config配置文件,就使用它啓動日誌系統,如果沒有就使用默認配置。
@Override public void onApplicationEvent(ApplicationEvent event) { if (event instanceof ApplicationStartingEvent) { onApplicationStartingEvent((ApplicationStartingEvent) event); } else if (event instanceof ApplicationEnvironmentPreparedEvent) { onApplicationEnvironmentPreparedEvent( (ApplicationEnvironmentPreparedEvent) event); } else if (event instanceof ApplicationPreparedEvent) { onApplicationPreparedEvent((ApplicationPreparedEvent) event); } else if (event instanceof ContextClosedEvent && ((ContextClosedEvent) event) .getApplicationContext().getParent() == null) { onContextClosedEvent(); } else if (event instanceof ApplicationFailedEvent) { onApplicationFailedEvent(); } }
本文通過查看監聽器啓動方法瞭解到springboot的一些內置監聽器的作用,有一篇博客寫的非常詳細可參考:Springboot內置ApplicationListener