SpringBoot启动过程:SpringApplication及SpringApplicationBuilder

Posted 恒奇恒毅

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了SpringBoot启动过程:SpringApplication及SpringApplicationBuilder相关的知识,希望对你有一定的参考价值。

SpringBoot应用的启动是基于SpringApplication类的,下面一步步地分析。

构造方法及初始化

其构造方法有二,都调用了initialize方法,完成注解源的配置。

	public SpringApplication(Object... sources) 
		initialize(sources);
	
    public SpringApplication(ResourceLoader resourceLoader, Object... sources) 
		this.resourceLoader = resourceLoader;
		initialize(sources);
	
	private void initialize(Object[] sources) 
		if (sources != null && sources.length > 0) 
			this.sources.addAll(Arrays.asList(sources));
		
		this.webEnvironment = deduceWebEnvironment();
		setInitializers((Collection) getSpringFactoriesInstances(
				ApplicationContextInitializer.class));
		setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
		this.mainApplicationClass = deduceMainApplicationClass();
	
  • initialize方法首先将注解源设置到成员变量this.sources中,然后deduceWebEnvironment方法中根据类路径是否有javax.servlet.Servlet、org.springframework.web.context.ConfigurableWebApplicationContext这两个类判断是否是web环境this.webEnvironment
  • 利用spring.factories机制机制加载ApplicationContextInitializerApplicationListener
  • 然后找到main方法所在类mainApplicationClass

run方法

run方法是程序运行的主要方法

	public ConfigurableApplicationContext run(String... args) 
		StopWatch stopWatch = new StopWatch();
		stopWatch.start();
		ConfigurableApplicationContext context = null;
		FailureAnalyzers analyzers = null;
		configureHeadlessProperty();
		SpringApplicationRunListeners listeners = getRunListeners(args);
		listeners.starting();
		try 
			ApplicationArguments applicationArguments = new DefaultApplicationArguments(
					args);
			ConfigurableEnvironment environment = prepareEnvironment(listeners,
					applicationArguments);
			Banner printedBanner = printBanner(environment);
			context = createApplicationContext();
			analyzers = new FailureAnalyzers(context);
			prepareContext(context, environment, listeners, applicationArguments,
					printedBanner);
			refreshContext(context);
			afterRefresh(context, applicationArguments);
			listeners.finished(context, null);
			stopWatch.stop();
			if (this.logStartupInfo) 
				new StartupInfoLogger(this.mainApplicationClass)
						.logStarted(getApplicationLog(), stopWatch);
			
			return context;
		
		catch (Throwable ex) 
			handleRunFailure(context, listeners, analyzers, ex);
			throw new IllegalStateException(ex);
		
	
  • SpringApplicationRunListeners加载,利用spring.factories机制加载SpringApplicationRunListener,封装到SpringApplicationRunListenersSpringApplicationRunListener定义了一些容器的生命周期方法,SpringApplicationRunListeners就是为了方便循环调用多个SpringApplicationRunListener的方法。在容器的各个生命周期时刻调用相应的方法。
public interface SpringApplicationRunListener 

	/**
	 * Called immediately when the run method has first started. Can be used for very
	 * early initialization.
	 */
	void starting();

	/**
	 * Called once the environment has been prepared, but before the
	 * @link ApplicationContext has been created.
	 * @param environment the environment
	 */
	void environmentPrepared(ConfigurableEnvironment environment);

	/**
	 * Called once the @link ApplicationContext has been created and prepared, but
	 * before sources have been loaded.
	 * @param context the application context
	 */
	void contextPrepared(ConfigurableApplicationContext context);

	/**
	 * Called once the application context has been loaded but before it has been
	 * refreshed.
	 * @param context the application context
	 */
	void contextLoaded(ConfigurableApplicationContext context);

	/**
	 * Called immediately before the run method finishes.
	 * @param context the application context or null if a failure occurred before the
	 * context was created
	 * @param exception any run exception or null if run completed successfully.
	 */
	void finished(ConfigurableApplicationContext context, Throwable exception);



  • 将主方法入参args封装为ApplicationArguments(默认实现DefaultApplicationArguments),方便获取命令行参数
public interface ApplicationArguments 

	/**
	 * Return the raw unprocessed arguments that were passed to the application.
	 * @return the arguments
	 */
	String[] getSourceArgs();

	/**
	 * Return then names of all option arguments. For example, if the arguments were
	 * "--foo=bar --debug" would return the values @code ["foo", "debug"].
	 * @return the option names or an empty set
	 */
	Set<String> getOptionNames();

	/**
	 * Return whether the set of option arguments parsed from the arguments contains an
	 * option with the given name.
	 * @param name the name to check
	 * @return @code true if the arguments contain an option with the given name
	 */
	boolean containsOption(String name);

	/**
	 * Return the collection of values associated with the arguments option having the
	 * given name.
	 * <ul>
	 * <li>if the option is present and has no argument (e.g.: "--foo"), return an empty
	 * collection (@code [])</li>
	 * <li>if the option is present and has a single value (e.g. "--foo=bar"), return a
	 * collection having one element (@code ["bar"])</li>
	 * <li>if the option is present and has multiple values (e.g. "--foo=bar --foo=baz"),
	 * return a collection having elements for each value (@code ["bar", "baz"])</li>
	 * <li>if the option is not present, return @code null</li>
	 * </ul>
	 * @param name the name of the option
	 * @return a list of option values for the given name
	 */
	List<String> getOptionValues(String name);

	/**
	 * Return the collection of non-option arguments parsed.
	 * @return the non-option arguments or an empty list
	 */
	List<String> getNonOptionArgs();


  • 准备ConfigurableEnvironment,其中configureEnvironment将命令行参数封装为SimpleCommandLinePropertySource添加到环境中,配置profile。
	private ConfigurableEnvironment prepareEnvironment(
			SpringApplicationRunListeners listeners,
			ApplicationArguments applicationArguments) 
		// Create and configure the environment
		ConfigurableEnvironment environment = getOrCreateEnvironment();
		configureEnvironment(environment, applicationArguments.getSourceArgs());
		listeners.environmentPrepared(environment);
		if (!this.webEnvironment) 
			environment = new EnvironmentConverter(getClassLoader())
					.convertToStandardEnvironmentIfNecessary(environment);
		
		return environment;
	
	protected void configureEnvironment(ConfigurableEnvironment environment,
			String[] args) 
		configurePropertySources(environment, args);
		configureProfiles(environment, args);
	

	/**
	 * Add, remove or re-order any @link PropertySources in this application's
	 * environment.
	 * @param environment this application's environment
	 * @param args arguments passed to the @code run method
	 * @see #configureEnvironment(ConfigurableEnvironment, String[])
	 */
	protected void configurePropertySources(ConfigurableEnvironment environment,
			String[] args) 
		MutablePropertySources sources = environment.getPropertySources();
		if (this.defaultProperties != null && !this.defaultProperties.isEmpty()) 
			sources.addLast(
					new MapPropertySource("defaultProperties", this.defaultProperties));
		
		if (this.addCommandLineProperties && args.length > 0) 
			String name = CommandLinePropertySource.COMMAND_LINE_PROPERTY_SOURCE_NAME;
			if (sources.contains(name)) 
				PropertySource<?> source = sources.get(name);
				CompositePropertySource composite = new CompositePropertySource(name);
				composite.addPropertySource(new SimpleCommandLinePropertySource(
						name + "-" + args.hashCode(), args));
				composite.addPropertySource(source);
				sources.replace(name, composite);
			
			else 
				sources.addFirst(new SimpleCommandLinePropertySource(args));
			
		
	

	/**
	 * Configure which profiles are active (or active by default) for this application
	 * environment. Additional profiles may be activated during configuration file
	 * processing via the @code spring.profiles.active property.
	 * @param environment this application's environment
	 * @param args arguments passed to the @code run method
	 * @see #configureEnvironment(ConfigurableEnvironment, String[])
	 * @see org.springframework.boot.context.config.ConfigFileApplicationListener
	 */
	protected void configureProfiles(ConfigurableEnvironment environment, String[] args) 
		environment.getActiveProfiles(); // ensure they are initialized
		// But these ones should go first (last wins in a property key clash)
		Set<String> profiles = new LinkedHashSet<String>(this.additionalProfiles);
		profiles.addAll(Arrays.asList(environment.getActiveProfiles()));
		environment.setActiveProfiles(profiles.toArray(new String[profiles.size()]));
	
  • createApplicationContext方法创建容器ConfigurableApplicationContext,根据签名的是否web环境决定是创建AnnotationConfigEmbeddedWebApplicationContext或者AnnotationConfigApplicationContext
  • 创建失败分析器FailureAnalyzers
  • prepareContext 准备环境
	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);
	
  1. 给容器设置环境
  2. postProcessApplicationContext:beanNameGenerator和resourceLoader
  3. applyInitializers应用容器初始化器ApplicationContextInitializer
  4. ApplicationArguments放到容器中
  5. Banner放到容器中
  6. load方法利用BeanDefinitionLoader加载注解源,此时容器就注册好了所有的注解源相关的BeanDefinition
  • refreshContext 刷新容器,这一步是最重要的进行了容器的刷新,并且注册了ShutdownHook
  • afterRefresh方法中完成所有runner的执行,包括ApplicationRunnerCommandLineRunner
  • 至此完成容器的创建和初始化

SpringApplicationBuilder

该类提供了配置SpringApplication的快捷方法。在外置容器创建及启动ApplicationContext过程中,我们自定义的SpringBootServletInitializer就可以利用SpringApplicationBuilder定制容器。

以上是关于SpringBoot启动过程:SpringApplication及SpringApplicationBuilder的主要内容,如果未能解决你的问题,请参考以下文章

main的 启动和配置文件的读取

springboot启动过程解析

Springboot启动过程

Dubbo: 在springboot中的启动过程

SpringBoot启动流程分析:IoC容器的初始化过程

SpringBoot:Spring容器的启动过程