The SpringApplication startup logic
Personally think this is also very important, incidentally here reference “Taro” blog in-depth study of the underlying source code, and then with a picture to help understand memory. Our Spring Boot version is 1.5.9, so your source code implementation may be different, but I think the overall idea of Spring Boot should be similar between different versions.
The constructor
The sources array contains only one element: demoApplication.class
public SpringApplication(Object... sources) {
initialize(sources);
}
private void initialize(Object[] sources) {
if(sources ! =null && sources.length > 0) {
this.sources.addAll(Arrays.asList(sources));
}
// Check whether it is a Web environment, different versions of Spring Boot, implementation may be different
this.webEnvironment = deduceWebEnvironment();
/ / set ApplicationContextInitializer
setInitializers((Collection) getSpringFactoriesInstances(
ApplicationContextInitializer.class));
// ApplicationListener List
setListeners((Collection)
getSpringFactoriesInstances(ApplicationListener.class));
this.mainApplicationClass = deduceMainApplicationClass();
}
public void setInitializers( Collection
> initializers) {
this.initializers = newArrayList<ApplicationContextInitializer<? > > ();this.initializers.addAll(initializers);
}
public void setListeners(Collection
> listeners) {
this.listeners = newArrayList<ApplicationListener<? > > ();this.listeners.addAll(listeners);
}
// Here are some properties of the SpringApplication class
private ConfigurableEnvironment environment;
private Class<? extends ConfigurableApplicationContext> applicationContextClass;
private boolean webEnvironment;
privateList<ApplicationContextInitializer<? >> initializers;privateList<ApplicationListener<? >> listeners;Copy the code
! [] (liutianruo-2019-go-go-go.oss-cn-shanghai.aliyuncs.com/notes/02_Sp… Application constructor. PNG)
run()
This is the core code to Boot Spring Boot.
public ConfigurableApplicationContext run(String... args) {
// It is used to match the start time of log statistics
StopWatch stopWatch = new StopWatch();
stopWatch.start();
ConfigurableApplicationContext context = null;
FailureAnalyzers analyzers = null;
// AwT is related and can be ignored
configureHeadlessProperty();
/ / get SpringApplicationRunListeners (encapsulates the multiple SpringApplicationRunListener)
SpringApplicationRunListeners listeners = getRunListeners(args);
// Start the listener
listeners.starting();
try {
// Create an ApplicationArguments object
ApplicationArguments applicationArguments = new DefaultApplicationArguments(
args);
// Load the property configuration. After execution, all environment properties are loaded, including application.properties and external property configurations
ConfigurableEnvironment environment = prepareEnvironment(listeners,
applicationArguments);
// It is not important to display the Spring Boot pattern on the console
Banner printedBanner = printBanner(environment);
// Spring context
context = createApplicationContext();
// Exception reporter
analyzers = new FailureAnalyzers(context);
// Call initialize for all classes
prepareContext(context, environment, listeners, applicationArguments,
printedBanner);
// Initialize the Spring context
refreshContext(context);
// Perform the initialization postlogic of the Spring context
afterRefresh(context, applicationArguments);
// Notify the listener that the Spring context has been started
listeners.finished(context, null);
// The statistics duration ends
stopWatch.stop();
if (this.logStartupInfo) {
new StartupInfoLogger(this.mainApplicationClass)
.logStarted(getApplicationLog(), stopWatch);
}
return context;
}
catch (Throwable ex) {
handleRunFailure(context, listeners, analyzers, ex);
throw newIllegalStateException(ex); }}Copy the code
SpringApplicationRunListeners
private SpringApplicationRunListeners getRunListeners(String[] args) { Class<? >[] types =newClass<? >[] { SpringApplication.class, String[].class };return new SpringApplicationRunListeners(logger, getSpringFactoriesInstances(
SpringApplicationRunListener.class, types, this, args));
}
Copy the code
getSpringFactoriesInstances
private<T> Collection<? extends T> getSpringFactoriesInstances(Class<T> type, Class<? >[] parameterTypes, Object... args) { ClassLoader classLoader = Thread.currentThread().getContextClassLoader();// Use names and ensure unique to protect against duplicates
Set<String> names = new LinkedHashSet<String>(
SpringFactoriesLoader.loadFactoryNames(type, classLoader));
List<T> instances = createSpringFactoriesInstances(type, parameterTypes,
classLoader, args, names);
AnnotationAwareOrderComparator.sort(instances);
return instances;
}
Copy the code
SpringFactoriesLoader get meta-inf/spring. Factories configured SpringApplicationRunListener implementation class corresponding to the full name of the class, will use the Set come here.
org.springframework.boot.SpringApplicationRunListener=\
org.springframework.boot.context.event.EventPublishingRunListener
Copy the code
SpringApplicationRunListeners(Log log,
Collection<? extends SpringApplicationRunListener> listeners) {
this.log = log;
this.listeners = new ArrayList<SpringApplicationRunListener>(listeners);
}
Copy the code
After got the SpringApplicationRunListener implementation class, is SpringApplicationRunListeners packaged as a List.
EventPublishingRunListener
An event listener is essentially an event announcer, encapsulated in an ApplicationListener. Can only say EventPublishingRunListener is only a code farmer just spend money to find someone else to write code, well, that SpringApplicationRunListeners is the company’s boss. Ha ha
public class EventPublishingRunListener implements SpringApplicationRunListener.Ordered {
private final SpringApplication application;
private final String[] args;
private final SimpleApplicationEventMulticaster initialMulticaster;
public EventPublishingRunListener(SpringApplication application, String[] args) {
this.application = application;
this.args = args;
this.initialMulticaster = new SimpleApplicationEventMulticaster();
// Get the ApplicationListener List wrapped in the SpringApplication constructor,
/ / and inject the List to the impleApplicationEventMulticaster
for(ApplicationListener<? > listener : application.getListeners()) {this.initialMulticaster.addApplicationListener(listener); }}Copy the code
SimpleApplicationEventMulticaster site is at the bottom of the event listeners, really can hide, the tamilian boy.
Look at the EvenPublishingRunListener about event handling.
@Override
@SuppressWarnings("deprecation")
public void starting(a) {
// The direct broadcast event is gone
this.initialMulticaster
.multicastEvent(new ApplicationStartedEvent(this.application, this.args));
}
@Override
public void environmentPrepared(ConfigurableEnvironment environment) {
this.initialMulticaster.multicastEvent(new ApplicationEnvironmentPreparedEvent(
this.application, this.args, environment));
}
Copy the code
So let’s verify that
SpringApplicationRunListeners#starting
public void starting(a) {
/ / there are only a EventPublishingRunListener internal implementation
for (SpringApplicationRunListener listener : this.listeners) { listener.starting(); }}Copy the code
-
SpringApplicationRunListeners.starting
-
SpringApplicationRunListener.starting
EventPublishingRunListener
SimpleApplicationEventMulticaster.multicastEvent
List<ApplicationListener>
@Override public void multicastEvent(final ApplicationEvent event, ResolvableType eventType) { ResolvableType type = (eventType ! =null ? eventType : resolveDefaultEventType(event)); for (finalApplicationListener<? > listener : getApplicationListeners(event, type)) { Executor executor = getTaskExecutor();if(executor ! =null) { executor.execute(new Runnable() { @Override public void run(a) { invokeListener(listener, event); }}); }else{ invokeListener(listener, event); }}}Copy the code
-
Leave a picture:
At this point, the ApplicationListener encapsulates so many layers that I have to deal with it, and we’re pretty much done.
ConfigurableEnvironment
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;
}
Copy the code
getOrCreateEnvironment
private ConfigurableEnvironment getOrCreateEnvironment(a) {
if (this.environment ! =null) {
return this.environment;
}
/ / if it is a Web environment, creates StandardServletEnvironment
if (this.webEnvironment) {
return new StandardServletEnvironment();
}
return new StandardEnvironment();
}
Copy the code
Note: our Spring Boot version 1.5, at the bottom of the Spring Framework version is 4.3, so there is no WebFlux (Since 5.0), so there is no StandardReactiveWebEnvironment
configureEnvironment
protected void configureEnvironment(ConfigurableEnvironment environment, String[] args) {
configurePropertySources(environment, args);
configureProfiles(environment, args);
}
Copy the code
configurePropertySources
MutablePropertySources
protected void configurePropertySources(ConfigurableEnvironment environment, String[] args) {
MutablePropertySources sources = environment.getPropertySources();
/ / configuration defaultProperties
if (this.defaultProperties ! =null&&!this.defaultProperties.isEmpty()) {
sources.addLast(
new MapPropertySource("defaultProperties".this.defaultProperties));
}
// The source of properties for the start parameter
if (this.addCommandLineProperties && args.length > 0) {
String name = CommandLinePropertySource.COMMAND_LINE_PROPERTY_SOURCE_NAME;
// If it already exists, replace it
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 {
// If it does not exist, add
sources.addFirst(newSimpleCommandLinePropertySource(args)); }}}Copy the code
private boolean addCommandLineProperties = true;
Whether to add JVM startup parameters. The default is add
configureProfiles
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()]));
}
Copy the code
Give me another picture
! [] (liutianruo-2019-go-go-go.oss-cn-shanghai.aliyuncs.com/notes/02_Sp… Application Environment.png)
createApplicationContext
/** * The class name of application context that will be used by default for web * environments. */
public static final String DEFAULT_WEB_CONTEXT_CLASS = "org.springframework."
+ "boot.context.embedded.AnnotationConfigEmbeddedWebApplicationContext";
private static final String[] WEB_ENVIRONMENT_CLASSES = { "javax.servlet.Servlet"."org.springframework.web.context.ConfigurableWebApplicationContext" };
protected ConfigurableApplicationContext createApplicationContext(a) { Class<? > contextClass =this.applicationContextClass;
if (contextClass == null) {
try {
// Get the Class object based on reflection
contextClass = Class.forName(this.webEnvironment
? DEFAULT_WEB_CONTEXT_CLASS : DEFAULT_CONTEXT_CLASS);
}
catch (ClassNotFoundException ex) {
throw new IllegalStateException(
"Unable create a default ApplicationContext, "
+ "please specify an ApplicationContextClass", ex); }}/ / according to the Class object instantiated ConfigurableApplicationContext object
return (ConfigurableApplicationContext) BeanUtils.instantiate(contextClass);
}
Copy the code
prepareContext
private void prepareContext(ConfigurableApplicationContext context, ConfigurableEnvironment environment, SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments, Banner printedBanner) {
// ApplicationContext is associated with Environment
context.setEnvironment(environment);
// Set some properties of the context
postProcessApplicationContext(context);
/ / initialize ApplicationContextInitializer
applyInitializers(context);
listeners.contextPrepared(context);
if (this.logStartupInfo) {
logStartupInfo(context.getParent() == null);
logStartupProfileInfo(context);
}
// Add boot Specific Singleton beans Set beanFactory properties
context.getBeanFactory().registerSingleton("springApplicationArguments",
applicationArguments);
if(printedBanner ! =null) {
context.getBeanFactory().registerSingleton("springBootBanner", printedBanner);
}
// Load the sources
/ / load BeanDefinition
Set<Object> sources = getSources();
Assert.notEmpty(sources, "Sources must not be empty");
load(context, sources.toArray(new Object[sources.size()]));
listeners.contextLoaded(context);
}
Copy the code
setEnvironment
Set the Environment property of ApplicationContext.
postProcessApplicationContext
Set some properties of ApplicationContext
protected void postProcessApplicationContext(ConfigurableApplicationContext context) {
if (this.beanNameGenerator ! =null) {
context.getBeanFactory().registerSingleton(
AnnotationConfigUtils.CONFIGURATION_BEAN_NAME_GENERATOR,
this.beanNameGenerator);
}
if (this.resourceLoader ! =null) {
if (context instanceof GenericApplicationContext) {
((GenericApplicationContext) context)
.setResourceLoader(this.resourceLoader);
}
if (context instanceof DefaultResourceLoader) {
((DefaultResourceLoader) context)
.setClassLoader(this.resourceLoader.getClassLoader()); }}}Copy the code
applyInitializers
protected void applyInitializers(ConfigurableApplicationContext context) {
for (ApplicationContextInitializer initializer : getInitializers()) {
/ / check ApplicationContextInitializer generics is not emptyClass<? > requiredType = GenericTypeResolver.resolveTypeArgument( initializer.getClass(), ApplicationContextInitializer.class); Assert.isInstanceOf(requiredType, context,"Unable to call initializer.");
/ / ApplicationContextInitializer performs initialization logicinitializer.initialize(context); }}Copy the code
getInitializers
setInitializers((Collection) getSpringFactoriesInstances(
ApplicationContextInitializer.class));
Copy the code
In SpringApplication constructor implementation, the initialization ApplicationContextInitializer, how to initialize, From the meta-inf/spring. Factories in traverse attribute is org. Springframework. Context. ApplicationContextInitializer corresponding attribute values.
refreshContext
Start the Spring container
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();
}
Copy the code
org.springframework.context.support.AbstractApplicationContext#refresh
See, Spring the Boot just encapsulates the API, for the Environment to do the custom to create, also set up some attribute of the ApplicationContext, callback ApplicationContextInitializer implementation, Finally start the container or with the aid of the Spring Framework AbstractApplicationContext. Refresh to complete.
Include event listener is also, finally to actually listen to the ApplicationListener is also from the Spring Framework, so the Spring Boot source code, to the back, is the Spring Framework itself.
afterRefresh
callRunners
private void callRunners(ApplicationContext context, ApplicationArguments args) {
List<Object> runners = new ArrayList<Object>();
runners.addAll(context.getBeansOfType(ApplicationRunner.class).values());
runners.addAll(context.getBeansOfType(CommandLineRunner.class).values());
AnnotationAwareOrderComparator.sort(runners);
for (Object runner : new LinkedHashSet<Object>(runners)) {
if (runner instanceof ApplicationRunner) {
callRunner((ApplicationRunner) runner, args);
}
if (runner instanceofCommandLineRunner) { callRunner((CommandLineRunner) runner, args); }}}Copy the code
ApplicationRunner
CommandLineRunner
These two interfaces perform callback methods
Summary in one chart
! [] (liutianruo-2019-go-go-go.oss-cn-shanghai.aliyuncs.com/notes/02_Sp… Application integrity. PNG)