SpringBoot startup process flowchart
Entrance class
@SpringBootApplication public class HelloWorldMainApplication { public static void main(String[] args) { SpringApplication.run(HelloWorldMainApplication.class, args); }}Copy the code
@springBootApplication we covered this in general in our last article. If you are interested, check out my first article on SpringBoot. This article focuses on SpringApplication. Run (HelloWorldMainApplication. Class, args); Let’s go inside
/ / call the static class, parameter matching is HelloWorldMainApplication. The class as well as the main method of the args public static ConfigurableApplicationContext run (class <? > primarySource,String... args) { return run(new Class<? >[] { primarySource }, args); } public static ConfigurableApplicationContext run(Object[] sources, String[] args) { return (new SpringApplication(sources)).run(args); }Copy the code
It will construct a SpringApplication instance, and we start the class HelloWorldMainApplication. * * * * the class as a parameter to pass in, then run it run method
SpringApplication constructor
public SpringApplication(ResourceLoader resourceLoader, Class<? >... primarySources) { this.resourceLoader = resourceLoader; Assert.notNull(primarySources, "PrimarySources must not be null"); / / the HelloWorldMainApplication. Class is set to the attribute stored enclosing primarySources = new LinkedHashSet < > (arrays.aslist (primarySources)); // Set the application type to Standard or Web this.webApplicationType = deduceWebApplicationType(); / / set the Initializer (, Initializer), finally will invoke the Initializer setInitializers ((Collection) getSpringFactoriesInstances ( ApplicationContextInitializer.class)); / / set the Listener (the Listener) setListeners ((Collection) getSpringFactoriesInstances (ApplicationListener. Class)); this.mainApplicationClass = deduceMainApplicationClass(); }Copy the code
HelloWorldMainApplication. The first class is stored in the enclosing primarySources attribute
Setting the Application Type
private WebApplicationType deduceWebApplicationType() { if (ClassUtils.isPresent(REACTIVE_WEB_ENVIRONMENT_CLASS, null) && ! ClassUtils.isPresent(MVC_WEB_ENVIRONMENT_CLASS, null)) { return WebApplicationType.REACTIVE; } for (String className : WEB_ENVIRONMENT_CLASSES) { if (! ClassUtils.isPresent(className, null)) { return WebApplicationType.NONE; } } return WebApplicationType.SERVLET; } private static final String REACTIVE_WEB_ENVIRONMENT_CLASS = "org.springFramework." + "web.reactive.DispatcherHandler"; private static final String MVC_WEB_ENVIRONMENT_CLASS = "org.springframework." + "web.servlet.DispatcherServlet"; private static final String[] WEB_ENVIRONMENT_CLASSES = { "javax.servlet.Servlet", "org.springframework.web.context.ConfigurableWebApplicationContext" };Copy the code
The Class loader is used to determine whether REACTIVE classes exist. If not, the Web environment is a SERVLET. The Web environment type is set here, and the corresponding environment is initialized later based on the type. Remember the dependencies we introduced in the first article?
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
Copy the code
Spring-boot-starter-web poM will introduce Tomcat and Spring-web MVC as follows
<dependency> <groupId>org.springframework</groupId> <artifactId>spring-webmvc</artifactId> < version > 5.0.5. RELEASE < / version > < scope > compile < / scope > < / dependency >Copy the code
Let’s look at the Spring-WebMVC JAR
Obviously, there is a DispatcherServlet class in Spring-WebMVC, which is the core Servlet of our previous SpringMvc. The DispatcherServlet class can be loaded by class loading. So our application type nature is WebApplicationType. SERVLET
public enum WebApplicationType {
NONE,
SERVLET,
REACTIVE;
private WebApplicationType() {
}
}
Copy the code
Set the Initializer
/ / set the Initializer (, Initializer), finally will invoke the Initializer setInitializers ((Collection) getSpringFactoriesInstances ( ApplicationContextInitializer.class));Copy the code
Now let’s look at getSpringFactoriesInstances * * * * (ApplicationContextInitializer. Class)
private <T> Collection<T> getSpringFactoriesInstances(Class<T> type) { return getSpringFactoriesInstances(type, new Class<? > [] {}); } / / into the participation type here is ApplicationContextInitializer class private < T > Collection < T > getSpringFactoriesInstances (class < T > type, Class<? >[] parameterTypes, Object... args) { ClassLoader classLoader = Thread.currentThread().getContextClassLoader(); / / using the Set store names to avoid repeating element Set < String > names = new LinkedHashSet < > (SpringFactoriesLoader. LoadFactoryNames (type, classLoader)); / / according to the names to instantiate the List < T > instances = createSpringFactoriesInstances (parameterTypes type, this, args, names); / / prioritize AnnotationAwareOrderComparator instance. Sort (instances). return instances; }Copy the code
This first reads all names from the input parameter type (which is a set of strings) and then instantiates them from that set:
/ / into the participation is ApplicationContextInitializer. Class public static a List < String > loadFactoryNames (class <? > factoryClass, ClassLoader classLoader) { String factoryClassName = factoryClass.getName(); Try {// Load all default auto-configured classes Enumeration<URL> urls = classLoader! = null? classLoader.getResources("META-INF/spring.factories"):ClassLoader.getSystemResources("META-INF/spring.factories"); ArrayList result = new ArrayList(); while(urls.hasMoreElements()) { URL url = (URL)urls.nextElement(); Properties properties = PropertiesLoaderUtils.loadProperties(new UrlResource(url)); / / get ApplicationContextInitializer. All class value String factoryClassNames = properties. The getProperty (factoryClassName); result.addAll(Arrays.asList(StringUtils.commaDelimitedListToStringArray(factoryClassNames))); } return result; } catch (IOException var8) { throw new IllegalArgumentException("Unable to load [" + factoryClass.getName() + "] factories from location [" + "META-INF/spring.factories" + "]", var8); }}Copy the code
The method attempts to read the configuration file from meta-INF /spring.factories on the classpath, and then iterates through it. Org. Springframework. Context. ApplicationContextInitializer value. Take the spring-boot-autoconfigure package as an example. The meta-INF/spring.Factories part of the package is defined as follows:
org.springframework.context.ApplicationContextInitializer=\
org.springframework.boot.autoconfigure.SharedMetadataReaderFactoryContextInitializer,\
org.springframework.boot.autoconfigure.logging.AutoConfigurationReportLoggingInitializer
Copy the code
The two class names will be read and placed into the Set Set, ready for the following instantiation:
/ / parameterTypes: step on to get the names of private collection < T > List < T > createSpringFactoriesInstances (Class < T > type, Class <? >[] parameterTypes, ClassLoader classLoader, Object[] args, Set<String> names) { List<T> instances = new ArrayList<T>(names.size()); for (String name : names) { try { Class<? > instanceClass = ClassUtils.forName(name, classLoader); / / confirm the loaded class is a subclass of ApplicationContextInitializer Assert. IsAssignable (type, instanceClass); Constructor<? > constructor = instanceClass.getDeclaredConstructor(parameterTypes); / / reflection instantiation object T instance. = (T) BeanUtils instantiateClass (constructor, args); // add instances. Add (instance); } catch (Throwable ex) { throw new IllegalArgumentException( "Cannot instantiate " + type + " : " + name, ex); } } return instances; }Copy the code
Confirm loaded class is indeed org. Springframework. Context. ApplicationContextInitializer subclasses, and then there is initialized by the constructor, add to the list of instances.
Therefore, the so-called initializer is org. Springframework. Context. ApplicationContextInitializer implementation class, the interface is defined like this:
public interface ApplicationContextInitializer<C extends ConfigurableApplicationContext> {
void initialize(C applicationContext);
}
Copy the code
Initialization before the Spring context is refreshed. Typically, such as in a Web application, register Property Sources or enable Profiles. Property Sources is a configuration file. Profiles are an entity that Spring abstracts to load different configuration items in different environments such as DEV, TEST, PRODUCTION, etc.
Setting a Listener
Let’s start setting up listeners:
setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
Copy the code
We still see getSpringFactoriesInstances follow up code
/ / the into the participation type: org. Springframework. Context. ApplicationListener. Class private < T > Collection <? extends T> getSpringFactoriesInstances(Class<T> type) { return getSpringFactoriesInstances(type, new Class<? > [] {}); } 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
[spring-boot-autoconfigure] [key-value] [spring-boots-autoconfigure] [spring-boots-autoconfigure] [spring-boots-autoconfigure] [spring-boots-autoconfigure] [spring-boots-autoconfigure] [spring-boots-autoconfigure] [spring-boots-autoconfigure] [spring-boots-autoconfigure] [spring-boots-autoconfigure] [spring-boots-autoconfigure] [spring-boots-autoconfigure] [spring-boots-autoconfigure]
org.springframework.context.ApplicationListener=\
org.springframework.boot.autoconfigure.BackgroundPreinitializer
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
Copy the code
These 10 listeners will continue throughout the springBoot life cycle. At this point, the initialization process for the SpringApplication instance is complete.
SpringApplication. Run method
With SpringApplication instantiated, call the run method:
public ConfigurableApplicationContext run(String... Args) {// StopWatch StopWatch = new StopWatch(); stopWatch.start(); ConfigurableApplicationContext context = null; Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>(); configureHeadlessProperty(); / / the first step: get and start the listener SpringApplicationRunListeners listeners = getRunListeners (args); listeners.starting(); try { ApplicationArguments applicationArguments = new DefaultApplicationArguments(args); // Step 2: According to SpringApplicationRunListeners and parameters to prepare environment ConfigurableEnvironment environment = prepareEnvironment(listeners,applicationArguments); configureIgnoreBeanInfo(environment); PrintedBanner = printBanner(environment); // Create Spring container context = createApplicationContext(); exceptionReporters = getSpringFactoriesInstances( SpringBootExceptionReporter.class, new Class[] { ConfigurableApplicationContext.class }, context); / / step 4: the Spring container pre-processing prepareContext (the context, the environment, listeners, applicationArguments, printedBanner); // Step 5: refresh the container refreshContext(context); AfterRefresh (context, applicationArguments); // Listeners. Started (context); Runners this.callRunners(context, applicationArguments); stopWatch.stop(); // Return context; } catch (Throwable ex) { handleRunFailure(context, listeners, exceptionReporters, ex); throw new IllegalStateException(ex); }}Copy the code
- Step 1: Get and start the listener
- The second step: according to SpringApplicationRunListeners and parameters to prepare the environment
- Step 3: Create the Spring container
- Step 4: Spring container preprocessing
- Step 5: Refresh the container
- Step 6: Spring container post-processing
- Step 7: Emit an event to end execution
- Step 8: Execute Runners
The following is specific analysis.
Step 1: Get and start the listener
Get listener
Methods of Tracking getRunListeners:
private SpringApplicationRunListeners getRunListeners(String[] args) { Class<? >[] types = new Class<? >[] { SpringApplication.class, String[].class }; return new SpringApplicationRunListeners(logger, getSpringFactoriesInstances(SpringApplicationRunListener.class, types, this, args)); }Copy the code
Here still getSpringFactoriesInstances method was used for instance, you can see in front of the methods to analyze, From the meta-inf/spring. Factories reads the Key for org. Springframework. Boot. SpringApplicationRunListener Values:
org.springframework.boot.SpringApplicationRunListener=\
org.springframework.boot.context.event.EventPublishingRunListener
Copy the code
Reflection for instance in getSpringFactoriesInstances triggered when EventPublishingRunListener constructors, let’s take a look at the EventPublishingRunListener constructor:
public class EventPublishingRunListener implements SpringApplicationRunListener, Ordered { private final SpringApplication application; private final String[] args; / / radio, private final SimpleApplicationEventMulticaster initialMulticaster; public EventPublishingRunListener(SpringApplication application, String[] args) { this.application = application; this.args = args; this.initialMulticaster = new SimpleApplicationEventMulticaster(); Iterator var3 = application.getListeners().iterator(); while(var3.hasNext()) { ApplicationListener<? > listener = (ApplicationListener)var3.next(); / / the above Settings to SpringApplication eleven listeners all added to the SimpleApplicationEventMulticaster the radio device this.initialMulticaster.addApplicationListener(listener); }} //... }Copy the code
* * EventPublishingRunListener we see there is a radio, EventPublishingRunListener constructor will SpringApplication eleven listeners all added to the SimpleApplicationEventMulticaster the radio apparatus, * * let’s take a look at how to add to the radio:
public abstract class AbstractApplicationEventMulticaster implements ApplicationEventMulticaster, BeanClassLoaderAware, BeanFactoryAware {/ / radio, save in the parent class within the listener's internal private final AbstractApplicationEventMulticaster. ListenerRetriever defaultRetriever = new AbstractApplicationEventMulticaster.ListenerRetriever(false); @Override public void addApplicationListener(ApplicationListener<? > listener) { synchronized (this.retrievalMutex) { Object singletonTarget = AopProxyUtils.getSingletonTarget(listener); if (singletonTarget instanceof ApplicationListener) { this.defaultRetriever.applicationListeners.remove(singletonTarget); } / / inner class object this. DefaultRetriever. ApplicationListeners. Add (the listener); this.retrieverCache.clear(); }} private class ListenerRetriever {public final Set<ApplicationListener<? >> applicationListeners = new LinkedHashSet(); public final Set<String> applicationListenerBeans = new LinkedHashSet(); private final boolean preFiltered; public ListenerRetriever(boolean preFiltered) { this.preFiltered = preFiltered; } public Collection<ApplicationListener<? >> getApplicationListeners() { LinkedList<ApplicationListener<? >> allListeners = new LinkedList(); Iterator var2 = this.applicationListeners.iterator(); while(var2.hasNext()) { ApplicationListener<? > listener = (ApplicationListener)var2.next(); allListeners.add(listener); } if (! this.applicationListenerBeans.isEmpty()) { BeanFactory beanFactory = AbstractApplicationEventMulticaster.this.getBeanFactory(); Iterator var8 = this.applicationListenerBeans.iterator(); while(var8.hasNext()) { String listenerBeanName = (String)var8.next(); try { ApplicationListener<? > listenerx = (ApplicationListener)beanFactory.getBean(listenerBeanName, ApplicationListener.class); if (this.preFiltered || ! allListeners.contains(listenerx)) { allListeners.add(listenerx); } } catch (NoSuchBeanDefinitionException var6) { ; } } } AnnotationAwareOrderComparator.sort(allListeners); return allListeners; }} //... }Copy the code
The methods defined in the SimpleApplicationEventMulticaster AbstractApplicationEventMulticaster superclass. The key code for this. DefaultRetriever. ApplicationListeners. Add (the listener); This is an inner class that holds all listeners. In this step, will spring. The listener to a SimpleApplicationEventMulticaster in factories. We now know that there is a radio equipment in EventPublishingRunListener SimpleApplicationEventMulticaster, SimpleApplicationEventMulticaster radio device and storage of all listeners.
Start listener
We obtained above step by getRunListeners listeners for EventPublishingRunListener, can be seen from the name is to start the event listener, is mainly used to release start events.
public class EventPublishingRunListener implements SpringApplicationRunListener, Ordered {
private final SpringApplication application;
private final String[] args;
private final SimpleApplicationEventMulticaster initialMulticaster;
Copy the code
Now let’s look at SpringApplicationRunListener this interface
package org.springframework.boot; Public interface SpringApplicationRunListener {/ / in the run () method starts, this method is called immediately, can be used in most early to do some work in the initialization void starting (); // This method is called void environmentPrepared(ConfigurableEnvironment Environment) when the environment is built and before ApplicationContext is created; / / when the ApplicationContext build is complete, this method is called void contextPrepared (ConfigurableApplicationContext context). / / in the ApplicationContext finished loading, but not be refreshed before, this method is called void contextLoaded (ConfigurableApplicationContext context). // After ApplicationContext is refreshed and started, CommandLineRunners and ApplicationRunner are not called, This method is called void started (ConfigurableApplicationContext context). / / the run () method completes this method is called before void running (ConfigurableApplicationContext context). / / this method is invoked when application running error void failed (ConfigurableApplicationContext context, Throwable exception). }Copy the code
SpringApplicationRunListener interface in the Spring the Boot startup initialization of various states in the process of execution, we can also add your own listener, upon initial SpringBoot listen event execute custom logic, Starting () :
@override public void starting() { To create the application start event ` ApplicationStartingEvent ` enclosing initialMulticaster. MulticastEvent (new ApplicationStartingEvent(this.application, this.args)); }Copy the code
Here to create a first start events ApplicationStartingEvent, we continue to follow up SimpleApplicationEventMulticaster, a core method:
@Override public void multicastEvent(final ApplicationEvent event, @Nullable ResolvableType eventType) { ResolvableType type = (eventType ! = null ? eventType : resolveDefaultEventType(event)); For (Final ApplicationListener<? > Listener: getApplicationListeners(event, type)) Here the thread pool is empty and has not been initialized. Executor executor = getTaskExecutor(); if (executor ! Execute (() -> invokeListener(Listener, event)); } else {invokeListener(listener, event); }}}Copy the code
ApplicationStartingEvent gets the corresponding listener based on the event type. After the container is started, the response action is executed. There are four types of listeners:
We choose springBoot log listener to explain, the core code is as follows:
@override public void onApplicationEvent(ApplicationEvent event) {// If (event instanceof) when SpringBoot is started ApplicationStartedEvent) { onApplicationStartedEvent((ApplicationStartedEvent) event); } / / springboot Environment Environment ready to complete the else if (event instanceof ApplicationEnvironmentPreparedEvent) { onApplicationEnvironmentPreparedEvent( (ApplicationEnvironmentPreparedEvent) event); } else if (Event Instanceof ApplicationPreparedEvent) {if (Event Instanceof ApplicationPreparedEvent) { onApplicationPreparedEvent((ApplicationPreparedEvent) event); Else if (Event instanceof ContextClosedEvent && (ContextClosedEvent) event) .getApplicationContext().getParent() == null) { onContextClosedEvent(); } else if (Event Instanceof ApplicationFailedEvent) {onApplicationFailedEvent(); }}Copy the code
Because our event type for ApplicationEvent executes onApplicationStartedEvent ((ApplicationStartedEvent) event); . SpringBoot sends events at various stages of the run process to execute the corresponding methods for the listener.
Step 2: Build the environment
ConfigurableEnvironment environment = prepareEnvironment(listeners,applicationArguments);
Copy the code
Follow the method:
private ConfigurableEnvironment prepareEnvironment( SpringApplicationRunListeners listeners, ApplicationArguments ApplicationArguments) {// Get the corresponding ConfigurableEnvironment ConfigurableEnvironment environment = getOrCreateEnvironment(); / / configuration configureEnvironment (environment, applicationArguments getSourceArgs ()); / / publishing environment is ready to event, this is the second time publish event listeners. EnvironmentPrepared (environment); bindToSpringApplication(environment); ConfigurationPropertySources.attach(environment); return environment; }Copy the code
Look at getOrCreateEnvironment () method, as mentioned earlier, the environment has been set for the servlet type, so here is the environment created by object is StandardServletEnvironment.
private ConfigurableEnvironment getOrCreateEnvironment() { if (this.environment ! = null) { return this.environment; } if (this.webApplicationType == WebApplicationType.SERVLET) { return new StandardServletEnvironment(); } return new StandardEnvironment(); }Copy the code
Then look at the listeners. EnvironmentPrepared (environment); As mentioned above, here is the second release event. What event? Take a look at the listeners obtained by event type:
Main look at ConfigFileApplicationListener * * * *, the listener is very core, mainly used for processing project configuration. The properties and YML files in the project are loaded by its inner classes. To be specific:
>
postProcessors = loadPostProcessors(); The following four types of processing classes are obtained:
# Environment Post Processors org.springframework.boot.env.EnvironmentPostProcessor= Org. Springframework. Boot. Cloud. CloudFoundryVcapEnvironmentPostProcessor, Org. Springframework. Boot. The env. SpringApplicationJsonEnvironmentPostProcessor, org.springframework.boot.env.SystemEnvironmentPropertySourceEnvironmentPostProcessorCopy the code
The execution of the above three listener process, ConfigFileApplicationListener performs the logic of the class itself. The configuration file under the path specified by its internal class Loader loading project:
private static final String DEFAULT_SEARCH_LOCATIONS = "classpath:/,classpath:/config/,file:./,file:./config/";
Copy the code
At this point, the variable configuration of the project has been loaded. Let’s take a look:
There are six configuration files in descending order. That is, the preceding configuration variable overrides the following one. Keep this in mind when configuring variables for projects.
Step 3: Create the container
context = createApplicationContext();
Copy the code
Follow up on the method:
public static final String DEFAULT_WEB_CONTEXT_CLASS = "org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext"; protected ConfigurableApplicationContext createApplicationContext() { Class<? > contextClass = this.applicationContextClass; if (contextClass == null) { try { switch (this.webApplicationType) { case SERVLET: contextClass = Class.forName(DEFAULT_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); }Copy the code
The type of container created here is determined by webApplicationType, which is SERVLET type, so the corresponding bytecode is loaded by reflection, Namely AnnotationConfigServletWebServerApplicationContext
Step 4: Spring container preprocessing
This step is primarily a preparatory action before the container is refreshed. This includes a very critical operation: injecting the startup class into the container to set the stage for enabling automated configuration later.
prepareContext(context, environment, listeners, applicationArguments,printedBanner);
Copy the code
Follow up on the method:
private void prepareContext(ConfigurableApplicationContext context, ConfigurableEnvironment environment, SpringApplicationRunListeners listeners, ApplicationArguments ApplicationArguments, Banner printedBanner) {/ / set the container environment, Context. SetEnvironment (Environment); / / execution container rear handle postProcessApplicationContext (context); / / perform in a container ApplicationContextInitializer (including spring. Factories and custom instances) applyInitializers (context); / / send containers ready event, notify the listener listeners. ContextPrepared (context); // Register the start parameter bean, which encapsulates the container-specified parameters as beans, Injection container context. GetBeanFactory (.) registerSingleton (" springApplicationArguments, "applicationArguments); // Set banner if (printedBanner! = null) { context.getBeanFactory().registerSingleton("springBootBanner", printedBanner); } // Get the arguments specified by our startup class, which can be multiple sets <Object> sources = getAllSources(); Assert.notEmpty(sources, "Sources must not be empty"); Load (context, sources.toarray (new Object[0])); // Publish container loaded events. listeners.contextLoaded(context); }Copy the code
Call the initializer
protected void applyInitializers(ConfigurableApplicationContext context) { // 1. Collection from SpringApplication class initializers for obtaining all ApplicationContextInitializer (ApplicationContextInitializer, initializer: getInitializers()) { // 2. The initialize method of cycle call ApplicationContextInitializer Class <? > requiredType = GenericTypeResolver.resolveTypeArgument( initializer.getClass(), ApplicationContextInitializer.class); Assert.isInstanceOf(requiredType, context, "Unable to call initializer."); initializer.initialize(context); }}Copy the code
Here we finally use the initializers we set up when we created the SpringApplication instance, walking through them in turn and calling the Initialize method. You can also customize the initializer, implement the initialize method, and place it in the meta-INF/Spring. factories configuration file with the Key: Org. Springframework. Context. ApplicationContextInitializer value, here we custom initializer is called, is a way of our project initialization
Load start specified class (emphasis)
Everyone back to the article at first take a look at first, in creating SpringApplication instance, HelloWorldMainApplication. The first class is stored in the enclosing primarySources attributes, now is the time to use this attribute, Let’s look at getAllSources ()
public Set<Object> getAllSources() { Set<Object> allSources = new LinkedHashSet(); if (! Collectionutils.isempty (this.primarysources)) {// Get the primarySources property, Is stored before HelloWorldMainApplication. Class allSources. AddAll (enclosing primarySources); } if (! CollectionUtils.isEmpty(this.sources)) { allSources.addAll(this.sources); } return Collections.unmodifiableSet(allSources); }Copy the code
Obviously, obtained enclosing primarySources attributes, namely we start class * * HelloWorldMainApplication in class, Load (context, sources.toarray (new Object[0]));
protected void load(ApplicationContext context, Object[] sources) { BeanDefinitionLoader loader = createBeanDefinitionLoader(getBeanDefinitionRegistry(context), sources); if (this.beanNameGenerator ! = null) { loader.setBeanNameGenerator(this.beanNameGenerator); } if (this.resourceLoader ! = null) { loader.setResourceLoader(this.resourceLoader); } if (this.environment ! = null) { loader.setEnvironment(this.environment); } loader.load(); } private int load(Class<? > source) { if (isGroovyPresent() && GroovyBeanDefinitionSource.class.isAssignableFrom(source)) { // Any GroovyLoaders added in beans{} DSL can contribute beans here GroovyBeanDefinitionSource loader = BeanUtils.instantiateClass(source, GroovyBeanDefinitionSource.class); load(loader); } if (isComponent(source)) {// Add bootstrap bean information to beanDefinitionMap with annotations, Also is to HelloWorldMainApplication. Class in the beanDefinitionMap enclosing annotatedReader. Register (source); return 1; } return 0; }Copy the code
Start class HelloWorldMainApplication. The class is loaded into the beanDefinitionMap, follow-up to start the class will be open automation configuration of the entrance, behind an article I’ll detail analysis of how to start the class loading, and the detailed process automation configuration to open.
Notifies the listener that the container is ready
listeners.contextLoaded(context);
Copy the code
Main or for some log listener response processing.
Step 5: Refresh the container
At this point, the SpringBoot-related processing is done, and spring takes over. So let’s look at refreshContext(context);
protected void refresh(ApplicationContext applicationContext) { Assert.isInstanceOf(AbstractApplicationContext.class, applicationContext); / / calls to create the container applicationContext the refresh () method (AbstractApplicationContext applicationContext). The refresh (); } public void refresh() throws BeansException, An IllegalStateException {synchronized (enclosing startupShutdownMonitor) {/ * * * refresh context * / prepareRefresh (); /** * Initializes the BeanFactory, parses the XML, and does the same for the XmlBeanFactory. */ ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory(); /** * Prepare the BeanFactory for context, that is, populate the various functions of the BeanFactory, Such as common annotations @autowired @ the Qualifier * * add ApplicationContextAwareProcessor processor in di ignore the interface implementation * Aware, Such as EnvironmentAware, ApplicationEventPublisherAware * registered depend on, such as a bean attribute contains ApplicationEventPublisher (the beanFactory), */ prepareBeanFactory(beanFactory); Try {/** * provides additional handling for subclass overrides, i.e. subclasses handle custom BeanFactoryPostProcess */ postProcessBeanFactory(beanFactory); / * * * to activate a variety of the BeanFactory processor, including BeanDefinitionRegistryBeanFactoryPostProcessor and ordinary spring BeanFactoryPostProcessor * To perform the corresponding postProcessBeanDefinitionRegistry method and postProcessBeanFactory * / invokeBeanFactoryPostProcessors (the beanFactory); /** * Register the BeanPostProcessor, not BeanFactoryPostProcessor, of the intercepting Bean. Will be in bean instantiation of corresponding method * / registerBeanPostProcessors (the beanFactory); /** * initializes resource files in the context, such as processing of internationalization files, etc. */ initMessageSource(); / * * * initializes the context event broadcast, and add applicatioEventMulticaster, such as ApplicationEventPublisher * / initApplicationEventMulticaster (); /** * To extend the subclass to initialize other beans */ onRefresh(); /** * Find listeners on all beans and register them with listeners */ registerListeners(); /** * Set converter * registers a default property value parser * freezes all bean definitions, indicating that registered bean definitions cannot be modified or further processed * Initializes the remaining non-lazy beans, The initialization of lazy loading beans * / finishBeanFactoryInitialization (the beanFactory); /** * Publish ContextRefreshedEvent events through Spring's event publishing mechanism to ensure that the corresponding listener does further processing. These classes implement ApplicationListener<ContextRefreshedEvent>. This is where the execution of these classes is triggered (the onApplicationEvent method is executed). Spring's built-in events are ContextClosedEvent, ContextRefreshedEvent, ContextStartedEvent, ContextStoppedEvent, RequestHandleEvent * When initialization is complete, notify the lifeCycleProcessor of the refresh process and issue ContextRefreshEvent to notify others */ finishRefresh(); } finally { resetCommonCaches(); }}}Copy the code
The Refresh method plays an important role in spring’s entire source architecture and is the key to implementing IOC and AOP. I’ve written an article about this process before, so check it out
Step 6: Spring container post-processing
protected void afterRefresh(ConfigurableApplicationContext context,
ApplicationArguments args) {
}
Copy the code
Extension interface, template method in design mode, default to empty implementation. You can override this method if you have custom requirements. Such as printing some start-end logs, or some other post-processing.
Step 7: Emit an event to end execution
Public void started (ConfigurableApplicationContext context) {/ / here is getting EventPublishingRunListener Iterator var2 = this.listeners.iterator(); while(var2.hasNext()) { SpringApplicationRunListener listener = (SpringApplicationRunListener)var2.next(); / / perform EventPublishingRunListener started method listener. Started (context); }} public void started (ConfigurableApplicationContext context) {/ / create ApplicationStartedEvent event, And publish event / / we see is the execution ConfigurableApplicationContext publishEvent method in this container, Context.publishevent (new ApplicationStartedEvent(this.application, this.args, context)); }Copy the code
Get EventPublishingRunListener listeners, and perform its started method, and will create the Spring container has handed in, create a ApplicationStartedEvent events, And perform ConfigurableApplicationContext publishEvent method, that is to say, this is in the Spring container launch event, not in SpringApplication publishing events, and in front of the starting is different, The previous starting is publishing startup events directly to the 11 listeners in the SpringApplication.
Step 8: Execute Runners
Let’s look at the last step, callRunners(Context, applicationArguments);
private void callRunners(ApplicationContext context, ApplicationArguments args) { List<Object> runners = new ArrayList<Object>(); AddAll (context.getBeansofType (ApplicationRunner.class).values()); // Get all ApplicationRunner Bean instances in the container. Runners. AddAll (context.getBeansofType (commandLinerunner.class).values()); // Get all CommandLineRunner Bean instances in the container. AnnotationAwareOrderComparator.sort(runners); for (Object runner : New LinkedHashSet<Object>(runners)) {if (runner instanceof ApplicationRunner) {// Execute the ApplicationRunner run method callRunner((ApplicationRunner) runner, args); } if (runner instanceof CommandLineRunner) {// Execute CommandLineRunner's run method callRunner((CommandLineRunner) runner, args); }}}Copy the code
If it is ApplicationRunner, the following code is executed:
private void callRunner(ApplicationRunner runner, ApplicationArguments args) { try { runner.run(args); } catch (Exception var4) { throw new IllegalStateException("Failed to execute ApplicationRunner", var4); }}Copy the code
If it is CommandLineRunner, the following code is executed:
private void callRunner(CommandLineRunner runner, ApplicationArguments args) { try { runner.run(args.getSourceArgs()); } catch (Exception var4) { throw new IllegalStateException("Failed to execute CommandLineRunner", var4); }}Copy the code
We can also customize ApplicationRunner or CommandLineRunner, implement its run methods, inject them into the Spring container, and execute all runner’s run methods after SpringBoot is complete