This article is participating in “Java Theme Month – Java Development Practice”, for details: juejin.cn/post/696719…
This is the 7th day of my participation in Gwen Challenge
start
In Spring Boot, we always start our application with one line of code:
SpringApplication.run(???.class, args);
Copy the code
The actual method we call is:
/** In SpringApplication */
public static ConfigurableApplicationContext run(Class
[] primarySources, String[] args) {
return new SpringApplication(primarySources).run(args);
}
/ / -- -- -- -- -- -- -- -- call
public SpringApplication(Class
... primarySources) {
this(null, primarySources);
}
/ / -- -- -- -- -- call
public static ConfigurableApplicationContext run(Class
[] primarySources, String[] args) {
return new SpringApplication(primarySources).run(args);
}
Copy the code
These include:
- Initialize SpringApplication
- SpringApplication.run
Before reading the source code, as someone whose knowledge of Spring is limited to use, I felt that for Spring, I wanted it to do the following when it started:
- Inject configuration items
- Generate and put the objects I need into the corresponding references
- Connect to the database, redis, and configuration in other configuration files
- .
So let’s look at how to do this with our purpose.
- Frameworks are proposed to solve repetitive tasks, so the lowest level implementation is not necessarily complex.
Initialize the
Next we parse the constructor of the SpringApplication.
The lowest level code for the constructor called is as follows:
/** In 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();
}
Copy the code
Since the argument we brought in is NULL, the resourceLoader here is NULL.
In this step we initialize:
- primarySources
- webApplicationType
- mainApplicationClass
And wakes up registered initializers and listeners. Next, we analyze the methods involved.
Determine the application type
Let’s take a look:
WebApplicationType.deduceFromClasspath()
What does this part of the code do?
The source code of this part is as follows:
static WebApplicationType deduceFromClasspath(a) {
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)) {
returnWebApplicationType.NONE; }}return WebApplicationType.SERVLET;
}
Copy the code
This section determines the type of startup, which in this case is SERVLET.
- With the Spring-wrapped classUtils, use reflection to see if the classes started are certain.
Set the initializer
This part from
setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class)); Copy the code
To enter. We need to parse:
- setInitializer
- getSpringFactoriesInstances
getSpringFactoriesInstances
This part of the code is as follows:
private <T> Collection<T> getSpringFactoriesInstances(Class<T> type) {
return getSpringFactoriesInstances(type, newClass<? > [] {}); }//
private <T> Collection<T> getSpringFactoriesInstances(Class
type, Class
[] parameterTypes, Object... args)
{
ClassLoader classLoader = getClassLoader();
// Use names and ensure unique to protect against duplicates
Set<String> names = new LinkedHashSet<>(SpringFactoriesLoader.loadFactoryNames(type, classLoader));
List<T> instances = createSpringFactoriesInstances(type, parameterTypes, classLoader, args, names);
AnnotationAwareOrderComparator.sort(instances);
return instances;
}
Copy the code
The process is as follows:
-
Access to this
- This step gets the classLoader from the current thread.
-
Fetch the factory name from the obtained classLoader.
-
Corresponding method of this step:
SpringFactoriesLoader.loadFactoryNames(type, classLoader)
-
-
Initialize the factory based on the factory name obtained in the previous step.
-
According to the AnnotationAwareOrderComparator, ordered to get factory instance
-
Return to the factory instance.
setInitializer
It’s really just a simple assignment.
ApplicationContextInitializer
//todo
Setting up listeners
This section is similar to the above, corresponding to the code:
setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
Copy the code
SetListeners are simple set methods.
Get the main Application class
The corresponding code is:
privateClass<? > deduceMainApplicationClass() {try {
StackTraceElement[] stackTrace = new RuntimeException().getStackTrace();
for (StackTraceElement stackTraceElement : stackTrace) {
if ("main".equals(stackTraceElement.getMethodName())) {
returnClass.forName(stackTraceElement.getClassName()); }}}catch (ClassNotFoundException ex) {
// Swallow and continue
}
return null;
}
Copy the code
Gets the stack of the current Runtime and the class that currently executes main.
The run!
The constructor is done, and it’s really just a bunch of configuration and listener initialization constructor assignments.
Next, parse the run method.
This part of the code is as follows:
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;
}
Copy the code
-
At the beginning, we started a stopWatch, which was mainly to record how much time we spent to start the project.
-
A headless Property is configured. (//todo)
-
Here are the listeners.
- This step is similar to the setListener logic above.
-
Remind the listeners of the previous step.
The code is: listeners.starting();
-
The observational design pattern is used here:
void starting(a) { for (SpringApplicationRunListener listener : this.listeners) { listener.starting(); }}Copy the code
In fact we only register a listener here: EventPublishingRunListener
So our call goes here:
//in EventPublishingRunListener public void starting(a) { this.initialMulticaster.multicastEvent(new ApplicationStartingEvent(this.application, this.args)); } public void multicastEvent(final ApplicationEvent event, @Nullable ResolvableType eventType) { ResolvableType type = (eventType ! =null ? eventType : resolveDefaultEventType(event)); Executor executor = getTaskExecutor(); for(ApplicationListener<? > listener : getApplicationListeners(event, type)) {if(executor ! =null) { executor.execute(() -> invokeListener(listener, event)); } else{ invokeListener(listener, event); }}}Copy the code
This is where the properties of our input events are parsed and distributed to the corresponding listeners.
-
-
Configure the Application startup parameter (we entered null).
-
Configure the environment.
The environment is initialized based on the webApplicationType obtained in our initialization.
At the same time:
- Notify all registered listeners of the preparation of the environment and make it run.
- Bind the environment to Spring.
-
Configure ignored beans.
-
Print the banner.
-
With webApplicationType, create applicationContext.
-
Prepare the context. (//todo, similar to 6)
-
Refresh context
-
Start the listeners.
-
Start the runners.