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.factories
use and function of this document.
About spring.factories
spring.factories
Is a properties file, which is located in classpath:/META-INF/
the directory inside each jar package can have spring.factories
a file. Spring provides tools SpringFactoriesLoader
responsible for loading, parsing the file, such as spring-boot-2.2.0.RELEASE.jar
inside the META-INF
directory will have spring.factories
the 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.factories
What do I need to know?
spring.factories
It is a properties filespring.factories
The key-value pairs in a comma-separated value完整类名列表
spring.factories
key in the key-value pair is完整接口名称
spring.factories
The key value is the key to the implementation classspring.factories
BySpringFactoriesLoader
loading toolsspring.factories
Locatedclasspath:/META-INF/
directorySpringFactoriesLoader
Loads inside the jar packagespring.factories
file and merge
I know spring.factories
the concept, continue to analyze SpringBoot start.
SpringApplicaiton initialization
Entrance Java program in the main
method SpringBoot also can be main
ways to start, only a small amount of code with @SpringBootApplication
comments, 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 SpringApplication
constructor:
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 SpringApplication
the 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 ApplicationContext
type of review is by enumeration WebApplicationType
of deduceFromClasspath
static 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;
}
复制代码
WebApplicationType
Enumeration 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.xml
the document introduction spring-boot-starter-web
that there will classpath org.springframework.web.context.ConfigurableWebApplicationContext
and javax.servlet.Servlet
thus determine the program category, ApplicationContext
type WebApplicationType.SERVLET
.
Load ApplicationContextInitializer
ApplicationContextInitializer
It will be performed before the refresh context, generally used to make some extra initialization projects such as: add PropertySource
, set ContextId
it only a work initialize
method:
public interface ApplicationContextInitializer<C extends ConfigurableApplicationContext> {
void initialize(C applicationContext);
}
复制代码
SpringBoot by SpringFactoriesLoader
loading spring.factories
the read key for the configuration org.springframework.context.ApplicationContextInitializer
of the value, mentioned earlier spring.factoies
in 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.jar
included in the configuration, the other jar package may also be configured org.springframework.context.ApplicationContextInitializer
to implement additional initialization.
Load ApplicationListener
ApplicationListener
Used to monitor ApplicationEvent
the event, it's loaded with the initial loading process ApplicationContextInitializer
is similar in spring.factories
the 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
复制代码
ApplicationListener
The loading process with ApplicationContextInitializer
similar through all SpringFactoriesLoader
loaded.
summary
Upon completion of the initialization phase, you can know the following information:
- ApplicationContext or other types of Web
- SpringApplication there are some
ApplicationContextInitializer
implementation class - SpringApplication there are some
ApplicationListener
implementation 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 run
method, 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 main
the method of args
passing incoming parameters, SpringBoot establish a preparation phase DefaultApplicationArguments
for parsing, save command line parameters. As --spring.profiles.active=dev
will be SpringBoot the spring.profiles.active
property to dev.
public ConfigurableApplicationContext run(String... args) {
...
ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
...
}
复制代码
SpringBoot command line arguments will also receive to put Environment
in, to provide a unified property abstract.
Creating Environment
Create a code environment is relatively simple, according to the previously mentioned WebApplicationType
different 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 SpringApplication
the prepareEnvironment
method:
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;
}
复制代码
prepareEnvironment
The 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 SpringApplicaton
The main attribute is connected to an existing SpringApplicaton
instance, such spring.main.banner-mode
properties will correspond to the bannerMode
instance attribute, attribute this step, there are three sources (without custom):
- Environment Variables
- Command line parameters
- JVM system properties
SpringBoot will be prefixed with spring.main
the property is bound to SpringApplicaton
instance:
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
WebApplicationType
creation environment enumeration - Setting
ConversionService
variables for converting properties - The command line parameters
args
added to the environment - Adding Profiles settings to an external environment
- Binding SprinngApplicaiton property
- Send environmental
Prepared
event
ApplicationContext to initialize
Some of the steps mentioned earlier to prepare most of ApplicationContext
the work done, ApplicationContext
providing load Bean, loading resources, sending events, SpringBoot created during startup, configured ApplicationContext
do not need to do extra work to develop both (convenience it ~ ~).
This article will not go into ApplicationContext
, as with ApplicationContext
many related classes, not one or two articles written by complete, it is recommended by the module point of view, and finally integrating see ApplicationContext
the source code .
Creating ApplicationContext
Creating ApplicationContext
process to create the environment substantially similar to the mold, according to WebApplicationType
create 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 WebApplicationType
there are three members (SERVLET, REACTIVE, NONE), corresponding to different types of context:
- SERVLET: AnnotationConfigServletWebServerApplicationContext
- REACTIVE: AnnotationConfigReactiveWebServerApplicationContext
- NONE: AnnotationConfigApplicationContext
Ready ApplicationContext
You've created ApplicationContext
after 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
ApplicationContext
set up the environment (environment created earlier) - Base setting operation setting BeanNameGenerator, ResourceLoader, ConversionService etc.
- Execution
ApplicationContextInitializer
of theinitialize
method (ApplicationContextInitializer are acquired during the initialization phase) - Sign up command-line arguments (springApplicationArguments)
- Registration Banner (springBootBanner)
- Registration sources (by the @Configuration annotated classes)
Preparation ApplicationContext
code 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 注册sources
this 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 ApplicationContext
inside.
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 ApplicationRunner
and CommandLineRunner
, both interfaces have only a function similar run
methods differ only in the received parameters. They may be custom boot module, such as by implementing the boot dubbo
, gRPC
and the like.
ApplicationRunner
And CommandLineRunner
call 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);
}
}
}
复制代码
callRunners
After 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.