1. 程式人生 > >SpringBoot啟動過程之初始化器initializer和監聽器listeners

SpringBoot啟動過程之初始化器initializer和監聽器listeners

Listener類圖

SpringApplication構造的時候內部會呼叫一個private方法initialize:

public SpringApplication(Object... sources) {
initialize(sources); // sources目前是一個MyApplication的class物件
}
private void initialize(Object[] sources) {
if (sources != null && sources.length > 0) {
this.sources.addAll(Arrays.asList(sources)); // 把sources設定到SpringApplication的sources屬性中,目前只是一個MyApplication類物件
}
this.webEnvironment = deduceWebEnvironment(); // 判斷是否是web程式(javax.servlet.Servlet和org.springframework.web.context.ConfigurableWebApplicationContext都必須在類載入器中存在),並設定到webEnvironment屬性中
// 從spring.factories檔案中找出key為ApplicationContextInitializer的類並例項化後設置到SpringApplication的initializers屬性中。這個過程也就是找出所有的應用程式初始化器
setInitializers((Collection) getSpringFactoriesInstances(
ApplicationContextInitializer.class));
// 從spring.factories檔案中找出key為ApplicationListener的類並例項化後設置到SpringApplication的listeners屬性中。這個過程就是找出所有的應用程式事件監聽器
setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
// 找出main類,這裡是MyApplication類
this.mainApplicationClass = deduceMainApplicationClass();
}

ApplicationContextInitializer,應用程式初始化器,做一些初始化的工作:

public interface ApplicationContextInitializer<C extends ConfigurableApplicationContext> {
void initialize(C applicationContext);
}
類名
org.springframework.boot.context.config.DelegatingApplicationContextInitializer
org.springframework.boot.context.ContextIdApplicationContextInitializer
org.springframework.boot.context.ConfigurationWarningsApplicationContextInitializer
org.springframework.boot.context.embedded.ServerPortInfoApplicationContextInitializer
org.springframework.boot.autoconfigure.SharedMetadataReaderFactoryContextInitializer
org.springframework.boot.autoconfigure.logging.AutoConfigurationReportLoggingInitializer

功能描述:

  • DelegatingApplicationContextInitializer: 委派處理ApplicationContext初始化器,其需要委派處理的初始化器來自Spring環境中的context.initializer.classes屬性,該屬性可以使用逗號分隔多個初始化器。
  • ContextIdApplicationContextInitializer:為ApplicationContext設定id。根據以下的配置順序來設定,spring.application.name、vcap.application.name、spring.config.name,如果環境配置中都沒有這些配置,則預設使用“application”來表示,另外還會將profiles也加入到id中去。
  • ConfigurationWarningsApplicationContextInitializer:輸出警告日誌資訊。
  • ServerPortInfoApplicationContextInitializer:新增一個EmbeddedServletContainerInitializedEvent事件監聽,觸發設定嵌入的WEB服務啟動埠。通過屬性local.[namespace].port來設定啟動埠,其中namespace為ApplicationContext指定的名稱空間,如果名稱空間為空,則使用local.server.port屬性來表示配置的埠。
  • SharedMetadataReaderFactoryContextInitializer:和Spring Boot共享CachingMetadataReaderFactory。
  • AutoConfigurationReportLoggingInitializer:新增一個通用的事件監聽Springboot自動配置的報表日誌輸出。

在上面的初始化器分析中,我們可以看到,通過一些屬性的讀取可以設定Spring Boot啟動過程中的一些引數。初始化器的功能可以在Spring Boot啟動前對ApplicationContext進行一些自定義操作。初始化器是在準備上下文階段呼叫的。

private void prepareContext(ConfigurableApplicationContext context,
		ConfigurableEnvironment environment, SpringApplicationRunListeners listeners,
		ApplicationArguments applicationArguments, Banner printedBanner) {
	context.setEnvironment(environment);
	postProcessApplicationContext(context);
        // 呼叫初始化器
	applyInitializers(context);
	listeners.contextPrepared(context);
	if (this.logStartupInfo) {
		logStartupInfo(context.getParent() == null);
		logStartupProfileInfo(context);
	}

	// Add boot specific singleton beans
	context.getBeanFactory().registerSingleton("springApplicationArguments",
			applicationArguments);
	if (printedBanner != null) {
		context.getBeanFactory().registerSingleton("springBootBanner", printedBanner);
	}

	// Load the sources
	Set<Object> sources = getSources();
	Assert.notEmpty(sources, "Sources must not be empty");
	load(context, sources.toArray(new Object[sources.size()]));
	listeners.contextLoaded(context);
}

這裡的應用程式事件(ApplicationEvent)有應用程式啟動事件(ApplicationStartedEvent),失敗事件(ApplicationFailedEvent),準備事件(ApplicationPreparedEvent)等。

public interface ApplicationListener<E extends ApplicationEvent> extends EventListener {
void onApplicationEvent(E event);
}

應用程式事件監聽器跟監聽事件是繫結的。比如ConfigServerBootstrapApplicationListener只跟ApplicationEnvironmentPreparedEvent事件繫結,LiquibaseServiceLocatorApplicationListener只跟ApplicationStartedEvent事件繫結,LoggingApplicationListener跟所有事件繫結等。

key為ApplicationListener的有:

  1. org.springframework.boot.context.config.ConfigFileApplicationListener 重要(讀取載入springboot配置檔案) 轉向環境和配置檔案的載入
  2. org.springframework.boot.context.config.AnsiOutputApplicationListener(引數spring.output.ansi.enabled) 
    在springboot環境準備完成以後執行, 
    如果你的終端支援ANSI,設定彩色輸出會讓日誌更具可讀性。
  3. org.springframework.boot.logging.LoggingApplicationListener  根據配置初始化日誌系統log
  4. org.springframework.boot.logging.ClasspathLoggingApplicationListener  程式啟動時,講classpath列印到debug日誌,啟動失敗時classpath列印到info日誌
  5. org.springframework.boot.autoconfigure.BackgroundPreinitializer
  6. org.springframework.boot.context.config.DelegatingApplicationListener(引數context.listener.classes) 
    把Listener轉發給配置的這些class處理,這樣可以支援外圍程式碼不去寫spring.factories中的org.springframework.context.ApplicationListener相關配置,保持springboot原來程式碼的穩定
  7. org.springframework.boot.builder.ParentContextCloserApplicationListener  容器關閉時發出通知,如果父容器關閉,那麼自容器也一起關閉
  8. org.springframework.boot.context.FileEncodingApplicationListener(引數spring.mandatory-file-encoding) 
    在springboot環境準備完成以後執行,獲取環境中的系統環境引數,檢測當前系統環境的file.encoding和spring.mandatory-file-encoding設定的值是否一樣,如果不一樣則丟擲異常 
    如果不配置spring.mandatory-file-encoding則不檢查
  9. org.springframework.boot.liquibase.LiquibaseServiceLocatorApplicationListener                                                      (引數liquibase.servicelocator.ServiceLocator) 
    如果存在,則使用springboot相關的版本進行替代

SpringApplication的執行

public ConfigurableApplicationContext run(String... args) {
StopWatch stopWatch = new StopWatch();
		stopWatch.start();
		ConfigurableApplicationContext context = null;
		FailureAnalyzers analyzers = null;
		configureHeadlessProperty();
                //從Spirng.factories中獲取key為SpringApplicationListeners的類(目前只有一個實現類EventPublishRunListener)並作為listenners屬性的一個listener
		SpringApplicationRunListeners listeners = getRunListeners(args);
		listeners.starting();
...
}
private SpringApplicationRunListeners getRunListeners(String[] args) {
		Class<?>[] types = new Class<?>[] { SpringApplication.class, String[].class };
                //將SpringApplication物件傳入,也就傳入了SpringApplication的listeners屬性
		return new SpringApplicationRunListeners(logger, getSpringFactoriesInstances(
				SpringApplicationRunListener.class, types, this, args));
	}

分析run方法之前,先看一下SpringApplication中的一些事件和監聽器概念。

首先是SpringApplicationRunListeners類和SpringApplicationRunListener類的介紹。

SpringApplicationRunListeners內部持有SpringApplicationRunListener集合和1個Log日誌類。用於SpringApplicationRunListener監聽器的批量執行。

SpringApplicationRunListener看名字也知道用於監聽SpringApplication的run方法的執行。

它定義了5個步驟:

  1. starting(run方法執行的時候立馬執行;對應事件的型別是ApplicationStartedEvent)
  2. environmentPrepared(ApplicationContext建立之前並且環境資訊準備好的時候呼叫;對應事件的型別是ApplicationEnvironmentPreparedEvent)
  3. contextPrepared(ApplicationContext建立好並且在source載入之前呼叫一次;沒有具體的對應事件)
  4. contextLoaded(ApplicationContext建立並載入之後並在refresh之前呼叫;對應事件的型別是ApplicationPreparedEvent)
  5. finished(run方法結束之前呼叫;對應事件的型別是ApplicationReadyEvent或ApplicationFailedEvent)

SpringApplicationRunListener目前只有一個實現類EventPublishingRunListener,它把監聽的過程封裝成了SpringApplicationEvent事件並讓內部屬性(intialMulticaster)ApplicationEventMulticaster介面的實現類SimpleApplicationEventMulticaster廣播出去,廣播出去的事件物件會被SpringApplication中的listeners屬性進行處理。

所以說SpringApplicationRunListener和ApplicationListener之間的關係是通過ApplicationEventMulticaster廣播出去的SpringApplicationEvent所聯絡起來的。

SpringApplicationRunListenners

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<SpringApplicationRunListener>(listeners);
	}

	public void starting() {
		for (SpringApplicationRunListener listener : this.listeners) {
			listener.starting();
		}
	}

	public void environmentPrepared(ConfigurableEnvironment environment) {
		for (SpringApplicationRunListener listener : this.listeners) {
			listener.environmentPrepared(environment);
		}
	}

	public void contextPrepared(ConfigurableApplicationContext context) {
		for (SpringApplicationRunListener listener : this.listeners) {
			listener.contextPrepared(context);
		}
	}

	public void contextLoaded(ConfigurableApplicationContext context) {
		for (SpringApplicationRunListener listener : this.listeners) {
			listener.contextLoaded(context);
		}
	}

	public void finished(ConfigurableApplicationContext context, Throwable exception) {
		for (SpringApplicationRunListener listener : this.listeners) {
			callFinishedListener(listener, context, exception);
		}
	}

	private void callFinishedListener(SpringApplicationRunListener listener,
			ConfigurableApplicationContext context, Throwable exception) {
		try {
			listener.finished(context, exception);
		}
		catch (Throwable ex) {
			if (exception == null) {
				ReflectionUtils.rethrowRuntimeException(ex);
			}
			if (this.log.isDebugEnabled()) {
				this.log.error("Error handling failed", ex);
			}
			else {
				String message = ex.getMessage();
				message = (message == null ? "no error message" : message);
				this.log.warn("Error handling failed (" + message + ")");
			}
		}
	}

}

EventPublishingRunListener

public 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 int getOrder() {
		return 0;
	}

	@Override
	@SuppressWarnings("deprecation")
	public void starting() {
		this.initialMulticaster
				.multicastEvent(new ApplicationStartedEvent(this.application, this.args));
	}

	@Override
	public void environmentPrepared(ConfigurableEnvironment environment) {
		this.initialMulticaster.multicastEvent(new ApplicationEnvironmentPreparedEvent(
				this.application, this.args, environment));
	}

	@Override
	public void contextPrepared(ConfigurableApplicationContext context) {

	}

	@Override
	public void contextLoaded(ConfigurableApplicationContext context) {
		for (ApplicationListener<?> listener : this.application.getListeners()) {
			if (listener instanceof ApplicationContextAware) {
				((ApplicationContextAware) listener).setApplicationContext(context);
			}
			context.addApplicationListener(listener);
		}
		this.initialMulticaster.multicastEvent(
				new ApplicationPreparedEvent(this.application, this.args, context));
	}

	@Override
	public void finished(ConfigurableApplicationContext context, Throwable exception) {
		SpringApplicationEvent event = getFinishedEvent(context, exception);
		if (context != null && context.isActive()) {
			// Listeners have been registered to the application context so we should
			// use it at this point if we can
			context.publishEvent(event);
		}
		else {
			// An inactive context may not have a multicaster so we use our multicaster to
			// call all of the context's listeners instead
			if (context instanceof AbstractApplicationContext) {
				for (ApplicationListener<?> listener : ((AbstractApplicationContext) context)
						.getApplicationListeners()) {
					this.initialMulticaster.addApplicationListener(listener);
				}
			}
			if (event instanceof ApplicationFailedEvent) {
				this.initialMulticaster.setErrorHandler(new LoggingErrorHandler());
			}
			this.initialMulticaster.multicastEvent(event);
		}
	}

	private SpringApplicationEvent getFinishedEvent(
			ConfigurableApplicationContext context, Throwable exception) {
		if (exception != null) {
			return new ApplicationFailedEvent(this.application, this.args, context,
					exception);
		}
		return new ApplicationReadyEvent(this.application, this.args, context);
	}

	private static class LoggingErrorHandler implements ErrorHandler {

		private static Log logger = LogFactory.getLog(EventPublishingRunListener.class);

		@Override
		public void handleError(Throwable throwable) {
			logger.warn("Error calling ApplicationEventListener", throwable);
		}

	}

}