How SpringBoot is started?

This article is by looking at the source code SpringBoot roughly sorted out SpringBoot start the process, the overall general direction is simply a starting point, do not say too many complex things, internal implementation details of the paper does not buckle because everyone's deep thinking and understanding are not the same, I personal understanding with everyone watching to see certainly not the same, foggy expression of that time out is also futile.

First, I will start the process of SpringBoot organized into the following phases:

  • SpringApplicaiton initialization
    • Review ApplicationContext type
    • Load ApplicationContextInitializer
    • Load ApplicationListener
  • Environment initialization
    • Parse command line arguments
    • Creating Environment
    • Configuration Environment
    • Configuration SpringApplication
  • ApplicationContext to initialize
    • Creating ApplicationContext
    • Set ApplicationContext
    • Refresh ApplicationContext
  • Run the program entry

Eliminating some of the details do not affect the main flow, before viewing SpringBoot source code, you have to mention the spring.factoriesuse and function of this document.

About spring.factories

spring.factoriesIs a properties file, which is located in classpath:/META-INF/the directory inside each jar package can have spring.factoriesa file. Spring provides tools SpringFactoriesLoaderresponsible for loading, parsing the file, such as spring-boot-2.2.0.RELEASE.jarinside the META-INFdirectory will have spring.factoriesthe file:

# PropertySource Loaders
org.springframework.boot.env.PropertySourceLoader=\
org.springframework.boot.env.PropertiesPropertySourceLoader,\
org.springframework.boot.env.YamlPropertySourceLoader

# Run Listeners
org.springframework.boot.SpringApplicationRunListener=\
org.springframework.boot.context.event.EventPublishingRunListener
...
复制代码

About spring.factoriesWhat do I need to know?

  • spring.factoriesIt is a properties file
  • spring.factoriesThe key-value pairs in a comma-separated value完整类名列表
  • spring.factorieskey in the key-value pair is完整接口名称
  • spring.factoriesThe key value is the key to the implementation class
  • spring.factoriesBy SpringFactoriesLoaderloading tools
  • spring.factoriesLocated classpath:/META-INF/directory
  • SpringFactoriesLoaderLoads inside the jar package spring.factoriesfile and merge

I know spring.factoriesthe concept, continue to analyze SpringBoot start.

SpringApplicaiton initialization

Entrance Java program in the mainmethod SpringBoot also can be mainways to start, only a small amount of code with @SpringBootApplicationcomments, very easy to start SpringBoot:

@SpringBootApplication
@Slf4j
public class SpringEnvApplication {
	public static void main(String[] args) {
		ConfigurableApplicationContext context = SpringApplication.run(SpringEnvApplication.class, args);
 	}

}
复制代码

SpringApplicaiton initialization located SpringApplicationconstructor:

    public SpringApplication(Class<?>... primarySources) {
		this(null, primarySources);
	}
 
	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();
	}
复制代码

Simply put, under SpringApplicationthe constructor did some Han:

  • Base variable assignment (resourceLoader, primarySources, ...)
  • Review ApplicationContext type such as (Web, Reactive, Standard)
  • Load ApplicationContextInitializer
  • Load ApplicationListener
  • Review of startup class (class main method)

Then again one by one analysis of these steps.

Review ApplicationContext type

SpringBoot will review the initialization phase ApplicationContexttype of review is by enumeration WebApplicationTypeof deduceFromClasspathstatic methods:

	static WebApplicationType deduceFromClasspath() {
		if (ClassUtils.isPresent(WEBFLUX_INDICATOR_CLASS, null) && !ClassUtils.isPresent(WEBMVC_INDICATOR_CLASS, null)
				&& !ClassUtils.isPresent(JERSEY_INDICATOR_CLASS, null)) {
			return WebApplicationType.REACTIVE;
		}
		for (String className : SERVLET_INDICATOR_CLASSES) {
			if (!ClassUtils.isPresent(className, null)) {
				return WebApplicationType.NONE;
			}
		}
		return WebApplicationType.SERVLET;
	}
复制代码

WebApplicationTypeEnumeration procedure used to mark whether the Web program, which has three values:

  • NONE: not a web application
  • SERVLET: Servlet-based Web program
  • REACTIVE: Based Reactive Web program

This method is simple to determine whether a Web program by classpath, method of constant is a complete class class name:

private static final String[] SERVLET_INDICATOR_CLASSES = { "javax.servlet.Servlet","org.springframework.web.context.ConfigurableWebApplicationContext" };
private static final String WEBMVC_INDICATOR_CLASS = "org.springframework.web.servlet.DispatcherServlet";
private static final String WEBFLUX_INDICATOR_CLASS = "org.springframework.web.reactive.DispatcherHandler";
private static final String JERSEY_INDICATOR_CLASS = "org.glassfish.jersey.servlet.ServletContainer";
private static final String SERVLET_APPLICATION_CONTEXT_CLASS = "org.springframework.web.context.WebApplicationContext";
private static final String REACTIVE_APPLICATION_CONTEXT_CLASS = "org.springframework.boot.web.reactive.context.ReactiveWebApplicationContext";
复制代码

For example, by pom.xmlthe document introduction spring-boot-starter-webthat there will classpath org.springframework.web.context.ConfigurableWebApplicationContextand javax.servlet.Servletthus determine the program category, ApplicationContexttype WebApplicationType.SERVLET.

Load ApplicationContextInitializer

ApplicationContextInitializerIt will be performed before the refresh context, generally used to make some extra initialization projects such as: add PropertySource, set ContextIdit only a work initializemethod:

public interface ApplicationContextInitializer<C extends ConfigurableApplicationContext> {
	void initialize(C applicationContext);
}
复制代码

SpringBoot by SpringFactoriesLoaderloading spring.factoriesthe read key for the configuration org.springframework.context.ApplicationContextInitializerof the value, mentioned earlier spring.factoiesin the configuration of the key value are implementation class:

org.springframework.context.ApplicationContextInitializer=\
org.springframework.boot.context.ConfigurationWarningsApplicationContextInitializer,\
org.springframework.boot.context.ContextIdApplicationContextInitializer,\
org.springframework.boot.context.config.DelegatingApplicationContextInitializer,\
org.springframework.boot.rsocket.context.RSocketPortInfoApplicationContextInitializer,\
org.springframework.boot.web.context.ServerPortInfoApplicationContextInitializer
复制代码

Listed above are spring-boot-2.2.0.RELEASE.jarincluded in the configuration, the other jar package may also be configured org.springframework.context.ApplicationContextInitializerto implement additional initialization.

Load ApplicationListener

ApplicationListenerUsed to monitor ApplicationEventthe event, it's loaded with the initial loading process ApplicationContextInitializeris similar in spring.factoriesthe configuration will be a higher number of priority ApplicationListener:

# Application Listeners
org.springframework.context.ApplicationListener=\
org.springframework.boot.ClearCachesApplicationListener,\
org.springframework.boot.builder.ParentContextCloserApplicationListener,\
org.springframework.boot.context.FileEncodingApplicationListener,\
org.springframework.boot.context.config.AnsiOutputApplicationListener,\
org.springframework.boot.context.config.ConfigFileApplicationListener,\
org.springframework.boot.context.config.DelegatingApplicationListener,\
org.springframework.boot.context.logging.ClasspathLoggingApplicationListener,\
org.springframework.boot.context.logging.LoggingApplicationListener,\
org.springframework.boot.liquibase.LiquibaseServiceLocatorApplicationListener
复制代码

ApplicationListenerThe loading process with ApplicationContextInitializersimilar through all SpringFactoriesLoaderloaded.

summary

Upon completion of the initialization phase, you can know the following information:

  • ApplicationContext or other types of Web
  • SpringApplication there are some ApplicationContextInitializerimplementation class
  • SpringApplication there are some ApplicationListenerimplementation class

Environment initialization

SpringBoot would do a lot of things after the initial work is completed to prepare for running the program, SpringBoot start most of the core code is located SpringApplication instance runmethod, environment initialization rough start process includes:

  • Parse command line arguments
  • Preparing the Environment (Environment)
  • Set the environment

Of course there will be some other operations such as:

  • Examples of SpringApplicationRunListeners
  • Banner Printing
  • Setting Exception Report
  • ...

These are not important to explain the operation will not be reading the article then carefully studied.

Parse command line arguments

Command-line arguments by mainthe method of argspassing incoming parameters, SpringBoot establish a preparation phase DefaultApplicationArgumentsfor parsing, save command line parameters. As --spring.profiles.active=devwill be SpringBoot the spring.profiles.activeproperty to dev.

public ConfigurableApplicationContext run(String... args) {
    ...
	ApplicationArguments applicationArguments = new DefaultApplicationArguments(args); 
	...
}
复制代码

SpringBoot command line arguments will also receive to put Environmentin, to provide a unified property abstract.

Creating Environment

Create a code environment is relatively simple, according to the previously mentioned WebApplicationTypedifferent environments to instantiate:

private ConfigurableEnvironment getOrCreateEnvironment() {
	if (this.environment != null) {
		return this.environment;
	}
	switch (this.webApplicationType) {
	case SERVLET:
		return new StandardServletEnvironment();
	case REACTIVE:
		return new StandardReactiveWebEnvironment();
	default:
		return new StandardEnvironment();
	}
}
复制代码

Preparation Environment

Environment (Environment) Profile and PropertyResolver substantially the composition:

  • Profile is a logical grouping BeanDefinition, you can specify the Profile enabled the definition of Bean will decide whether to register Bean Bean according to Profile of SpringBoot at runtime
  • PropertyResolver is designed to resolve property, SpringBoot loaded attribute configuration file, the system variables at startup

SpringBoot in preparation for the environment calls SpringApplicationthe prepareEnvironmentmethod:

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);
    ...
	return environment;
}
复制代码

prepareEnvironmentThe method generally do the following:

  • Create an environment
  • Configuration Environment
  • Set SpringApplication properties

Configuration Environment

We will do some simple configuration for the Environment After creating the environment:

protected void configureEnvironment(ConfigurableEnvironment environment, String[] args) {
	if (this.addConversionService) {
		ConversionService conversionService = ApplicationConversionService.getSharedInstance();
		environment.setConversionService((ConfigurableConversionService) conversionService);
	}
	configurePropertySources(environment, args);
	configureProfiles(environment, args);
}

protected void configurePropertySources(ConfigurableEnvironment environment, String[] args) {
	
	if (this.addCommandLineProperties && args.length > 0) {
		    ...
			sources.addFirst(new SimpleCommandLinePropertySource(args));
			...
		}
	}
	protected void configureProfiles(ConfigurableEnvironment environment, String[] args) {
		Set<String> profiles = new LinkedHashSet<>(this.additionalProfiles);
		profiles.addAll(Arrays.asList(environment.getActiveProfiles()));
		environment.setActiveProfiles(StringUtils.toStringArray(profiles));
	}
	
复制代码

Limited space eliminating nonessential code, configuration environment is mainly used for:

  • Provided ConversionService: Properties for converting
  • Add the command-line arguments to the environment
  • Add extra ActiveProfiles

SpringApplicaton property settings

Configuration SpringApplicatonThe main attribute is connected to an existing SpringApplicatoninstance, such spring.main.banner-modeproperties will correspond to the bannerModeinstance attribute, attribute this step, there are three sources (without custom):

  • Environment Variables
  • Command line parameters
  • JVM system properties

SpringBoot will be prefixed with spring.mainthe property is bound to SpringApplicatoninstance:

protected void bindToSpringApplication(ConfigurableEnvironment environment) {
	try {
		Binder.get(environment).bind("spring.main", Bindable.ofInstance(this));
	}
	catch (Exception ex) {
		throw new IllegalStateException("Cannot bind to SpringApplication", ex);
	}
}
复制代码

Environment initialization Summary

Summarizes the work environment at approximately preparation phase:

  • According WebApplicationTypecreation environment enumeration
  • Setting ConversionServicevariables for converting properties
  • The command line parameters argsadded to the environment
  • Adding Profiles settings to an external environment
  • Binding SprinngApplicaiton property
  • Send environmental Preparedevent

ApplicationContext to initialize

Some of the steps mentioned earlier to prepare most of ApplicationContextthe work done, ApplicationContextproviding load Bean, loading resources, sending events, SpringBoot created during startup, configured ApplicationContextdo not need to do extra work to develop both (convenience it ~ ~).

This article will not go into ApplicationContext, as with ApplicationContextmany related classes, not one or two articles written by complete, it is recommended by the module point of view, and finally integrating see ApplicationContextthe source code .

Creating ApplicationContext

Creating ApplicationContextprocess to create the environment substantially similar to the mold, according to WebApplicationTypecreate different types of programs is determined 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);
}
复制代码

As mentioned earlier WebApplicationTypethere are three members (SERVLET, REACTIVE, NONE), corresponding to different types of context:

  • SERVLET: AnnotationConfigServletWebServerApplicationContext
  • REACTIVE: AnnotationConfigReactiveWebServerApplicationContext
  • NONE: AnnotationConfigApplicationContext

Ready ApplicationContext

You've created ApplicationContextafter the next need to initialize it, set the environment, application ApplicationContextInitializer, registration Source, etc., the process of preparing Context SpringBoot can be summarized as follows:

  • To ApplicationContextset up the environment (environment created earlier)
  • Base setting operation setting BeanNameGenerator, ResourceLoader, ConversionService etc.
  • Execution ApplicationContextInitializerof the initializemethod (ApplicationContextInitializer are acquired during the initialization phase)
  • Sign up command-line arguments (springApplicationArguments)
  • Registration Banner (springBootBanner)
  • Registration sources (by the @Configuration annotated classes)

Preparation ApplicationContextcode as follows:

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);
	}
	if (this.lazyInitialization) {
		context.addBeanFactoryPostProcessor(new LazyInitializationBeanFactoryPostProcessor());
	}
	// 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);
}
复制代码

Note that 注册sourcesthis step, sources are annotated classes SpringBoot registered @Configuration Bean provided according to sources, the basic principle is by parsing metadata annotation, and then create BeanDefinition then register it into the ApplicationContextinside.

Refresh ApplicationContext

If SpringBoot of a car, and that the operation is done in front of the door, wearing seat belts and other basic operations, and refresh ApplicationContext is the ignition, and has not updated ApplicationContext just saved the definition of a Bean, after what the processor does not really run stand up. Refresh ApplicationContext this content is very important to understand the source ApplicationContext depend refresh operation, where the first column about simple basic steps:

  • Ready to refresh (validation property, set the listener)
  • Initialization BeanFactory
  • Execution BeanFactoryPostProcessor
  • Registration BeanPostProcessor
  • Initialization MessageSource
  • Init Event Broadcast
  • Registration ApplicationListener
  • ...

Refresh process steps are more associated libraries are relatively complex, it is recommended to read other auxiliary libraries will look refreshed source code, will be more effective.

Run the program entry

After the completion of the refresh context Spring container can be completely used, the next SpringBoot performs ApplicationRunnerand CommandLineRunner, both interfaces have only a function similar runmethods differ only in the received parameters. They may be custom boot module, such as by implementing the boot dubbo, gRPCand the like.

ApplicationRunnerAnd CommandLineRunnercall code is as follows:

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);
		}
	}
}
复制代码

callRunnersAfter the implementation, SpringBoot the startup process is complete.

to sum up

SpringApplication by looking at the source code and found SpringBoot better understand the source of start, mainly to provide an initial entry for the ApplicationContext, eliminating the need for developers to configure the ApplicationContext work. SpringBoot core function is to automatically configure the next lower SpringBoot Autoconfig source code analysis, to fully understand SpringBoot see the source code is less.

After reading SpringApplication source still have some questions worth considering:

  • SpringBoot is to start the process of Tomcat
  • SpringBoot automatic configuration principle
  • SpringBoot Starter Custom
  • BeanFactoryPostProcessor and realization of the principle BeanPostProcessor
  • ...


"Architecture Digest" a heavy field of architecture good paper every day, covering all popular Internet companies in the field of front-line application architecture (high availability, high performance, high stability), big data and machine learning.

Guess you like

Origin juejin.im/post/5dc0d0435188255f727b9c0c