1. 程式人生 > >探索 SpringBoot (三) 啟動流程詳解(下)

探索 SpringBoot (三) 啟動流程詳解(下)

探索 SpringBoot (三) 啟動流程詳解(下)

文章目錄

4 SpringBoot 執行階段

接上文 我們看看 SpringBoot 到底幹了啥吧。

  • 我們繼續回到初始的地方 看看 run 方法
new SpringApplication(primarySources).run(args)

...

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();
		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, ex, exceptionReporters, listeners);
			throw new IllegalStateException(ex);
		}

		try {
			listeners.running(context);
		}
		catch (Throwable ex) {
			handleRunFailure(context, ex, exceptionReporters, null);
			throw new IllegalStateException(ex);
		}
		return context;
	}

4.1 Spring 應用執行監聽者的載入和執行

SpringApplicationRunListeners listeners = getRunListeners(args);
listeners.starting();

...

listeners.started(context);

...

listeners.running(context);
  • 這裡就是初始化了應用監聽者。在應用的不同階段 呼叫了不同的方法。我們看看 getRunListeners 幹了啥
private SpringApplicationRunListeners getRunListeners(String[] args) {
	Class<?>[] types = new Class<?>[] { SpringApplication.class, String[].class };
	return new SpringApplicationRunListeners(logger, getSpringFactoriesInstances(
			SpringApplicationRunListener.class, types, this, args));
}


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);
	}

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

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

	...
}
getSpringFactoriesInstances(SpringApplicationRunListener.class, ...)

這個方法上面講過了 就是載入 spring.factory 下面的實現類,目前

key 為 SpringApplicationRunListener 的實現類就 一個 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
	public void starting() {
		this.initialMulticaster.multicastEvent(
				new ApplicationStartingEvent(this.application, this.args));
	}

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

...

反正就記住 通過 getRunListeners 方法 初始化了一個監聽者 EventPublishingRunListener,當呼叫 listeners.starting(); 的時候相當於呼叫了 EventPublishingRunListener 的 

@Override
public void starting() {
	this.initialMulticaster.multicastEvent(
			new ApplicationStartingEvent(this.application, this.args));
}
	
這個方法, spring 這樣做也是便於擴充套件。
  • 我們接著往下看看之後發生了什麼

  • initialMulticaster 其實就是一個廣播者,負責廣播事件。 我們看看 multicastEvent 方法

@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);
		}
	}
}

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);
	}
}

@SuppressWarnings({"unchecked", "rawtypes"})
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;
		}
	}
}


最終呼叫到這個方法上面了,

listener.onApplicationEvent(event);

你肯定好奇這個 listener 是什麼,其實就是我們上一篇載入的 ApplicationListener 的實現類,去執行 listener 的方法。

我們梳理一下:

1 SpringApplicationRunListeners listeners = getRunListeners(args); 載入監聽者 EventPublishingRunListener

2 EventPublishingRunListener 內部維護一個廣播者,在構造的時候從上下文獲取監聽者(ApplicationListener 的實現類) 並且放入廣播列表 

3 當 spring 執行到對應生命週期的時候 呼叫對應方法,比如 listeners.starting(); 經過  EventPublishingRunListener 內部的廣播者 最終呼叫到 listener.onApplicationEvent(event); (ApplicationListener 的實現類) 

4 最後 listener 不同的實現類 進行不同的時間處理邏輯

好了 關於 spring 的 事件/監聽器 模型就講完了。

4.2 解析輸入引數 構建執行環境

// 這裡就是講 run 方法傳入的引數 以 key-value 的形式解析 沒有太大的難度
ApplicationArguments applicationArguments = new DefaultApplicationArguments(
					args);

// 這裡的作用就是根據輸入引數 解析出那些事可用的配置,監聽者對應事件的通知
ConfigurableEnvironment environment = prepareEnvironment(listeners,
					applicationArguments);
					
...

private ConfigurableEnvironment prepareEnvironment(
			SpringApplicationRunListeners listeners,
			ApplicationArguments applicationArguments) {
	// Create and configure the environment
	ConfigurableEnvironment environment = getOrCreateEnvironment();
	configureEnvironment(environment, applicationArguments.getSourceArgs());
	listeners.environmentPrepared(environment);
	bindToSpringApplication(environment);
	if (!this.isCustomEnvironment) {
		environment = new EnvironmentConverter(getClassLoader())
				.convertEnvironmentIfNecessary(environment, deduceEnvironmentClass());
	}
	ConfigurationPropertySources.attach(environment);
	return environment;
}

4.3 上下文建立

因為這一塊內容比較多 也比較難 所以樓主也就簡單介紹一下

context = createApplicationContext();

...

prepareContext(context, environment, listeners, applicationArguments,
		printedBanner);
refreshContext(context);
afterRefresh(context, applicationArguments);

4.3.1 上下文的建立 createApplicationContext

protected ConfigurableApplicationContext createApplicationContext() {
	Class<?> contextClass = this.applicationContextClass;
	if (contextClass == null) {
		try {
			switch (this.webApplicationType) {
			case SERVLET:
				contextClass = Class.forName(DEFAULT_SERVLET_WEB_CONTEXT_CLASS);
				break;
			case REACTIVE:
				contextClass = Class.forName(DEFAULT_REACTIVE_WEB_CONTEXT_CLASS);
				break;
			default:
				contextClass = Class.forName(DEFAULT_CONTEXT_CLASS);
			}
		}
		catch (ClassNotFoundException ex) {
			throw new IllegalStateException(
					"Unable create a default ApplicationContext, "
							+ "please specify an ApplicationContextClass",
					ex);
		}
	}
	return (ConfigurableApplicationContext) BeanUtils.instantiateClass(contextClass);
}
  • 這裡就是根據上一篇中推斷出來的 webApplicationType ,載入對應的類,並且例項化上下文物件,如果是我們場景的 servlet 容器的話 實現類是這個
org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext

4.3.2 上下文環境預處理 prepareContext

prepareContext(context, environment, listeners, applicationArguments,
					printedBanner);

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
	ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
	beanFactory.registerSingleton("springApplicationArguments", applicationArguments);
	if (printedBanner != null) {
		beanFactory.registerSingleton("springBootBanner", printedBanner);
	}
	if (beanFactory instanceof DefaultListableBeanFactory) {
		((DefaultListableBeanFactory) beanFactory)
				.setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding);
	}
	// Load the sources
	Set<Object> sources = getAllSources();
	Assert.notEmpty(sources, "Sources must not be empty");
	load(context, sources.toArray(new Object[0]));
	listeners.contextLoaded(context);
}

主要看看這個方法, 這個方法載入了上下文

load(context, sources.toArray(new Object[0]));

protected void load(ApplicationContext context, Object[] sources) {
	if (logger.isDebugEnabled()) {
		logger.debug(
				"Loading source " + StringUtils.arrayToCommaDelimitedString(sources));
	}
	BeanDefinitionLoader loader = createBeanDefinitionLoader(
			getBeanDefinitionRegistry(context), sources);
	if (this.beanNameGenerator != null) {
		loader.setBeanNameGenerator(this.beanNameGenerator);
	}
	if (this.resourceLoader != null) {
		loader.setResourceLoader(this.resourceLoader);
	}
	if (this.environment != null) {
		loader.setEnvironment(this.environment);
	}
	loader.load();
}

...

private BeanDefinitionRegistry getBeanDefinitionRegistry(ApplicationContext context) {
	if (context instanceof BeanDefinitionRegistry) {
		return (BeanDefinitionRegistry) context;
	}
	if (context instanceof AbstractApplicationContext) {
		return (BeanDefinitionRegistry) ((AbstractApplicationContext) context)
				.getBeanFactory();
	}
	throw new IllegalStateException("Could not locate BeanDefinitionRegistry");
}
...

BeanDefinitionLoader(BeanDefinitionRegistry registry, Object... sources) {
	Assert.notNull(registry, "Registry must not be null");
	Assert.notEmpty(sources, "Sources must not be empty");
	this.sources = sources;
	this.annotatedReader = new AnnotatedBeanDefinitionReader(registry);
	this.xmlReader = new XmlBeanDefinitionReader(registry);
	if (isGroovyPresent()) {
		this.groovyReader = new GroovyBeanDefinitionReader(registry);
	}
	this.scanner = new ClassPathBeanDefinitionScanner(registry);
	this.scanner.addExcludeFilter(new ClassExcludeFilter(sources));
}

這裡根據傳入的註冊資訊用多種方式去載入 

4.3.3 重新整理上下文

refreshContext(context);

...

private void refreshContext(ConfigurableApplicationContext context) {
		refresh(context);
	if (this.registerShutdownHook) {
		try {
			context.registerShutdownHook();
		}
		catch (AccessControlException ex) {
			// Not allowed in some environments.
		}
	}
}

...

protected void refresh(ApplicationContext applicationContext) {
	Assert.isInstanceOf(AbstractApplicationContext.class, applicationContext);
	((AbstractApplicationContext) applicationContext).refresh();
}

...

public void refresh() throws BeansException, IllegalStateException {
		synchronized (this.startupShutdownMonitor) {
		// Prepare this context for refreshing.
		prepareRefresh();

		// Tell the subclass to refresh the internal bean factory.
		ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();

		// Prepare the bean factory for use in this context.
		prepareBeanFactory(beanFactory);

		try {
			// Allows post-processing of the bean factory in context subclasses.
			postProcessBeanFactory(beanFactory);

			// Invoke factory processors registered as beans in the context.
			invokeBeanFactoryPostProcessors(beanFactory);

			// Register bean processors that intercept bean creation.
			registerBeanPostProcessors(beanFactory);

			// Initialize message source for this context.
			initMessageSource();

			// Initialize event multicaster for this context.
			initApplicationEventMulticaster();

			// Initialize other special beans in specific context subclasses.
			onRefresh();

			// Check for listener beans and register them.
			registerListeners();

			// Instantiate all remaining (non-lazy-init) singletons.
			finishBeanFactoryInitialization(beanFactory);

			// 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;
		}

		finally {
			// Reset common introspection caches in Spring's core, since we
			// might not ever need metadata for singleton beans anymore...
			resetCommonCaches();
		}
	}
}


這裡就是容器 和 容器裡面的 bean 的各種操作和處理了,樓主水平有限,各位看官自己慢慢研究吧。。。

4.3.4 重新整理上下文之後

afterRefresh(context, applicationArguments);

...

protected void afterRefresh(ConfigurableApplicationContext context,
			ApplicationArguments args) {
}

  • 你沒有看錯 就是一片空白,這個估計就是 spring 留給我們自己去實現的。

5 總結

好不容易寫完了,但是感覺意猶未盡,很多東西都沒有說清楚,像 內建容器的啟動, ioc 過程, aop 過程。也是樓主水平有限,這裡就相當於拋磚引玉吧。如果發現文章中的錯誤歡迎指正哈。