(一)SpringBoot启动过程的分析-启动流程概览

-- 如下内容均基于2.1.8.RELEASE版本java

经过粗粒度的分析SpringBoot启动过程当中执行的主要操做,能够很容易划分它的大流程,每一个流程只关注重要操做为后续深刻学习创建一个大纲。web


官方示例-使用SpringBoot快速构建一个Web服务

@RestController
@SpringBootApplication
public class Example {

	@RequestMapping("/")
	String home() {
		return "Hello World!";
	}

	public static void main(String[] args) {
		SpringApplication.run(Example.class, args);
	}

}

由代码可知SpringBoot应用程序入口为SpringApplication.java,由其run()方法开始。spring

SpringApplication.java

构造方法

public class 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 = WebApplicationType.deduceFromClasspath();
		setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
		setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
		this.mainApplicationClass = deduceMainApplicationClass();
	}
}

由代码可得知,它未实现接口、未继承其它类。由构造方法能够看出它主要作了以下几件事:bootstrap

  • 获取当前应用类型(NONE, SERVLET, REACTIVE其中之一)。
  • 经过SPI获取ApplicationContextInitializer接口的实现类,其配置在MATE-INF/spring.factories文件中。
  • 经过SPI获取ApplicationListener接口的实现类,同上。
  • 获取启动入口(main函数)

run(...args) 方法

整个应用的启动将会在run方法内部完成。去除那些枝叶,只取最主要的内容来看。数组

/**
	 * Run the Spring application, creating and refreshing a new
	 * {@link ApplicationContext}.
	 * @param args the application arguments (usually passed from a Java main method)
	 * @return a running {@link ApplicationContext}
	 */
	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;
	}

经过方法上面的注释描述能够看出它就是用于启动并刷新容器。在run方法内部经过SPI获取SpringApplicationRunListener接口的实现类,它用于触发全部的监听器。EventPublishingRunListener做为一个实现类,从名称上来看其主要用于运行时的事件发布。在SpringBoot的各个生命周期来触发相对的事件,调用处理事件的监听器来完成每一个阶段的操做。springboot

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

获取全部的SpringApplicationRunListener接口实例,此处的SpringApplicationRunListeners它包装了SpringApplicationRunListener对象。以下:app

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);
	}
	// 省略部分代码
}

接着调用了starting()方法,实际上仍是调用SpringApplicationRunListener的starting方法。以下:less

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

实际上SpringApplicationRunListener并不止这一个方法:ide

void starting();
运行一开始触发,属于最先期的事件,处理ApplicationStartingEvent事件函数

void environmentPrepared(ConfigurableEnvironment environment);
当环境准备好的时候调用,可是在ApplicationContext建立以前。

void contextPrepared(ConfigurableApplicationContext context);
当ApplicationContext准备好的时候调用,可是在sources加载以前。

void contextLoaded(ConfigurableApplicationContext context);
当ApplicationContext准备好的时候调用,可是在它refresh以前。

void started(ConfigurableApplicationContext context);
上下文已被刷新,应用程序已启动,但CommandLineRunners 和ApplicationRunner还没被调用。

void running(ConfigurableApplicationContext context);
在run方法结束以前,而且全部的CommandLineRunners 和ApplicationRunner都被调用的时候调用。

根据方法的注释能够得知他们执行的时机,并得知他们所处理的事件类型。接下来看看EventPublishingRunListener的staring方法内部作了什么操做:

public void starting() {
        this.initialMulticaster.multicastEvent(new ApplicationStartingEvent(this.application, this.args));
    }

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

此处它并无直接本身来操做这些监听器,而是在初始化的时候将全部监听器给了SimpleApplicationEventMulticaster,由它来执行触发,此处先不作深刻探讨,只须要知道它会触发事件就行。如今把关注点放在具体的事件触发上this.initialMulticaster.multicastEvent(new ApplicationStartingEvent(this.application, this.args));这行代码在处理事件的时候new了一个ApplicationStartingEvent,由此得知它的每个类型的处理方法都会传入一个指定的事件。

public void multicastEvent(ApplicationEvent event,  ResolvableType eventType) {
    ResolvableType type = eventType != null ? eventType : this.resolveDefaultEventType(event);
    Executor executor = this.getTaskExecutor();
    Iterator var5 = this.getApplicationListeners(event, type).iterator();
    while(var5.hasNext()) {
        ApplicationListener<?> listener = (ApplicationListener)var5.next();
        if (executor != null) {
            executor.execute(() -> {
                this.invokeListener(listener, event);
            });
        } else {
            this.invokeListener(listener, event);
        }
    }
}

能够看出在处理事件的方法内部作了两件事(multicastEvent方法内部标红的方法调用):

获取全部的监听器
调用监听器

获取全部的监听器

在获取监听器的过程当中,会循环判断监听器声明的事件类型是否和本次处理的事件类型相同,本次处理的类型为ApplicationStartingEvent,不符合事件类型的事件会被排除,只调用声明了此类型的监听器。
代码片断:

AbstractApplicationEventMulticaster.retrieveApplicationListeners(ResolvableType eventType, Class<?> sourceType, ListenerRetriever retriever)

for (ApplicationListener<?> listener : listeners) {
	if (supportsEvent(listener, eventType, sourceType)) {
		if (retriever != null) {
			retriever.applicationListeners.add(listener);
		}
		allListeners.add(listener);
	}
}

循环判断全部的监听器(ApplicationListener)判断其是否支持当前所处理的事件(ApplicationStartingEvent)。

protected boolean supportsEvent(
		ApplicationListener<?> listener, ResolvableType eventType, @Nullable Class<?> sourceType) {

	GenericApplicationListener smartListener = (listener instanceof GenericApplicationListener ?
			(GenericApplicationListener) listener : new GenericApplicationListenerAdapter(listener));
	return (smartListener.supportsEventType(eventType) && smartListener.supportsSourceType(sourceType));
}

在判断当前监听器是否支持指定事件以前,将当前监听器转换为了GenericApplicationListener
而后在进行判断,看看它转换为泛型监听器的时候做了什么。

public GenericApplicationListenerAdapter(ApplicationListener<?> delegate) {
    Assert.notNull(delegate, "Delegate listener must not be null");
    this.delegate = delegate;
    this.declaredEventType = resolveDeclaredEventType(this.delegate);
}

经过GenericApplicationListener的构造方法能够看出它获取了监听器声明的事件类型.

private static ResolvableType resolveDeclaredEventType(ApplicationListener<ApplicationEvent> listener) {
		ResolvableType declaredEventType = resolveDeclaredEventType(listener.getClass());
		if (declaredEventType == null || declaredEventType.isAssignableFrom(ApplicationEvent.class)) {
			Class<?> targetClass = AopUtils.getTargetClass(listener);
			if (targetClass != listener.getClass()) {
				declaredEventType = resolveDeclaredEventType(targetClass);
			}
		}
		return declaredEventType;
	}

根据代码能够看出它比较了当前处理的事件和监听器处理的事件是否相符

public boolean supportsEventType(ResolvableType eventType) {
	if (this.delegate instanceof SmartApplicationListener) {
		Class<? extends ApplicationEvent> eventClass = (Class<? extends ApplicationEvent>) eventType.resolve();
		return (eventClass != null && ((SmartApplicationListener) this.delegate).supportsEventType(eventClass));
	}
	else {
		return (this.declaredEventType == null || this.declaredEventType.isAssignableFrom(eventType));
	}
}

经过笔者分析代码时的应用程序执行状况来看,笔者的应用程序在处理一共捕获了以下监听器,他们都监听了ApplicationStartingEvent事件

笔者的处理ApplicationStartingEvent监听器列表:

LoggingApplicationListener
BackgroundPreinitializer
DelegatingApplicationListener
LiquibaseServiceLocatorApplicationListener

能够简单来看看这些监听器都有什么特色:

LoggingApplicationListener监听器,能够看出它内部声明了须要关注的事件类型数组包含ApplicationStartingEvent。

public class LoggingApplicationListener implements GenericApplicationListener
public boolean supportsEventType(ResolvableType resolvableType) {
    return this.isAssignableFrom(resolvableType.getRawClass(), EVENT_TYPES);
}

static {
    // 省略部分无关代码……
    EVENT_TYPES = new Class[]{ApplicationStartingEvent.class, ApplicationEnvironmentPreparedEvent.class, ApplicationPreparedEvent.class, ContextClosedEvent.class, ApplicationFailedEvent.class};
    SOURCE_TYPES = new Class[]{SpringApplication.class, ApplicationContext.class};
    shutdownHookRegistered = new AtomicBoolean(false);
}

BackgroundPreinitializer监听器:

public class BackgroundPreinitializer implements ApplicationListener<SpringApplicationEvent>

public void onApplicationEvent(SpringApplicationEvent event) {
    if (!Boolean.getBoolean("spring.backgroundpreinitializer.ignore") && event instanceof ApplicationStartingEvent && preinitializationStarted.compareAndSet(false, true)) {
        this.performPreinitialization();
    }

}

经过上面两个被筛选出来的处理ApplicationStartingEvent事件的监听器案例会发现他们一个实现了ApplicationListener接口,一个实现了GenericApplicationListener接口,后者继承了前者,多了两个判断事件类型的方法和默认的排序优先级(默认最低)

小结

由一个最早执行的Starting事件咱们能够得知SpringBoot是如何处理事件,以及事件的匹配是如何实现。后续的其余事件处理都是一样的方式。

在run方法的try catch代码块内部,开始处理有关上下文的一些流程。SpringBoot也设计了精简的流程来处理不一样的任务,具体来讲就是以下几个方法共同完成每一个阶段的任务。

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

在如上几个阶段中能够发如今准备环境和准备上下文的过程当中都传入了监听器,意味着它们会被调用。

环境准备阶段

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

在方法内部能够看到它建立了环境对象,并在建立完毕的时候调用了listeners.environmentPrepared(environment)方法,触发了ApplicationEnvironmentPreparedEvent事件。通知其余监听器环境信息准备完毕。

上下文准备阶段

建立ApplicationContext

根据当前应用类型建立指定的上下文容器,供后续准备上下文使用

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

准备上下文

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

在此环节下,将环境对象放入上下文中,初始化BeanNameGenerator、ResourceLoader、ConversionService。执行ApplicationContextInitializer接口的实现类中的initialize()方法。接着调用了listeners.contextPrepared(context)方法,此方法对应处理ApplicationContextInitializedEvent事件,通知其余注册了此事件的监听器。

protected void applyInitializers(ConfigurableApplicationContext context) {
   for (ApplicationContextInitializer initializer : getInitializers()) {
      Class<?> requiredType = GenericTypeResolver.resolveTypeArgument(initializer.getClass(),
            ApplicationContextInitializer.class);
      Assert.isInstanceOf(requiredType, context, "Unable to call initializer.");
      initializer.initialize(context);
   }
}

ApplicationContextInitializer接口也是Spring的重要扩展接口之一,著名的配置中心:携程Apollo中就有很棒的应用。能够展现一下代码片断:

public class ApolloApplicationContextInitializer implements ApplicationContextInitializer<ConfigurableApplicationContext> {
   
	private static final Logger logger = LoggerFactory.getLogger(ApolloApplicationContextInitializer.class);
    private static final Splitter NAMESPACE_SPLITTER = Splitter.on(",").omitEmptyStrings().trimResults();

    private final ConfigPropertySourceFactory configPropertySourceFactory = ApolloInjector
            .getInstance(ConfigPropertySourceFactory.class);

    @Override
    public void initialize(ConfigurableApplicationContext context) {
        ConfigurableEnvironment environment = context.getEnvironment();
        String enabled = environment.getProperty(PropertySourcesConstants.SURK_BOOTSTRAP_ENABLED, "false");
        if (!Boolean.valueOf(enabled)) {
            logger.debug("Apollo bootstrap config is not enabled for context {}, see property: ${{}}", context, PropertySourcesConstants.SURK_BOOTSTRAP_ENABLED);
            return;
        }
        logger.debug("Apollo bootstrap config is enabled for context {}", context);

        if (environment.getPropertySources().contains(PropertySourcesConstants.APOLLO_BOOTSTRAP_PROPERTY_SOURCE_NAME)) {
            //already initialized
            return;
        }

        String namespaces = environment.getProperty(PropertySourcesConstants.APOLLO_BOOTSTRAP_NAMESPACES, ConfigConsts.NAMESPACE_APPLICATION);
        logger.debug("Apollo bootstrap namespaces: {}", namespaces);
        List<String> namespaceList = NAMESPACE_SPLITTER.splitToList(namespaces);

        CompositePropertySource composite = new CompositePropertySource(PropertySourcesConstants.APOLLO_BOOTSTRAP_PROPERTY_SOURCE_NAME);
        for (String namespace : namespaceList) {
            Config config = ConfigService.getConfig(namespace);

            composite.addPropertySource(configPropertySourceFactory.getConfigPropertySource(namespace, config));
        }

        environment.getPropertySources().addFirst(composite);
    }
}

Apollo实现此接口的目的是为了实如今应用还未启动,容器还未刷新,Bean实例还未装载的时候就将配置获取到放入环境信息中,待使用这些配置的Bean真正建立的时候就能够直接使用,实现了优先加载配置的能力。

回到当前阶段的处理,完成了ApplicationContextInitializedEvent事件通知以后,开始加载BeanDefinition,此处不做分析,紧接着调用了listeners.contextLoaded(context)方法处理ApplicationPreparedEvent事件。完成了上下文准备工做。

刷新容器

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

底层仍是调用ApplicationContext的.refresh()方法,此处不做解读。刷新完毕以后触发ApplicationStartedEvent事件,通知其余监听器做相应处理。

最后调用Runner

private void callRunners(ApplicationContext context, ApplicationArguments args) {
	List<Object> runners = new ArrayList<>();
	runners.addAll(context.getBeansOfType(ApplicationRunner.class).values());
	runners.addAll(context.getBeansOfType(CommandLineRunner.class).values());
	AnnotationAwareOrderComparator.sort(runners);
	for (Object runner : new LinkedHashSet<>(runners)) {
		if (runner instanceof ApplicationRunner) {
			callRunner((ApplicationRunner) runner, args);
		}
		if (runner instanceof CommandLineRunner) {
			callRunner((CommandLineRunner) runner, args);
		}
	}
}

因而可知,Runner优先级最低。在容器刷新完毕以后才调用,能够实现一些容器加载完毕以后的逻辑。例如spring-batch就有一个JobLauncherCommandLineRunner用于批处理。至此run方法执行完毕。

总结:
经过简要的分析SpringBoot启动过程,能够发现,在应用启动过程当中涉及到多个事件,经过EventPublishingRunListener来触发他们,同时又调用了ApplicationContextInitializer接口完成一些特定操做。

大致步骤能够总结为:开始启动-> 准备环境 -> 准备上下文 -> 刷新上下文 -> 后置处理。经过监听容器启动相关的事件能够在容器启动的各个阶段进行功能扩展,同时也展现了Apollo是如何使用本文涉及到的扩展接口。

相关文章
相关标签/搜索