Github source code Analysis project launched!! Here is the Github address of the note:

Github.com/yuanmabiji/…

What is the startup process of SpringBoot? SpringBoot source code (seven)

1 Review the old and learn the new

Review the past, let’s briefly review the content of the last article, the last article we analyzed the startup process of SpringBoot, now the key steps are condensed and summarized:

  1. buildSpringApplicationObject to launch SpringBoot;
  2. fromspring.factoriesLoad in configuration fileEventPublishingRunListenerObject is used to emit different life cycle events at different startup phases;
  3. Prepare environment variables, including system variables, environment variables, command line parameters, and configuration files (e.gapplication.properties), etc.
  4. Create a containerApplicationContext;
  5. Do some initialization for the container object created in step 4, prepare some container property values, etc., and call eachApplicationContextInitializerTo perform some initialization logic, etc.
  6. Refresh the container, this is the most important step, the most important step, too much complex logic is implemented here;
  7. callApplicationRunnerandCommandLineRunnerThe run method can realize the two interfaces in the container started after loading some business data;

During SpringBoot startup, each startup phase emits a different built-in lifecycle event, And then the corresponding listener will listen to these events to perform some initialization logic work such as ConfigFileApplicationListener will monitor onApplicationEnvironmentPreparedEvent incident to load the environment variables, etc.

2 the introduction

In the previous article, we saw that a New SpringApplication object was created to start the SpringBoot project. So, today we will look at the process of building SpringApplication objects and explain the SPI mechanism implemented by SpringBoot itself.

3 SpringApplication object building process

This section starts with the construction of SpringApplication objects, because the construction of an object is nothing more than assigning values to its member properties in its constructor, and contains little additional business logic (although sometimes we may open threads in the constructor). So, let’s look at some of the member properties used to construct SpringApplication objects:

// SpringApplication.java

/** * The boot class of SpringBoot is the main class that contains the main function */
privateSet<Class<? >> primarySources;/** * the main class containing the main function */
privateClass<? > mainApplicationClass;/** * resource loader */
private ResourceLoader resourceLoader;
/** * Application type */
private WebApplicationType webApplicationType;
/** * initializer */
privateList<ApplicationContextInitializer<? >> initializers;/** * listener */
privateList<ApplicationListener<? >> listeners;Copy the code

As you can see, the SpringApplication object is built primarily by assigning values to the six member properties in the code above. Now I’ll move on to the construction of the SpringApplication object.

Let’s go back to the code that builds the SpringApplication object in the previous article:

// SpringApplication.java

// The run method is a static method used to start SpringBoot
public static ConfigurableApplicationContext run(Class
       [] primarySources, String[] args) {
	// Build a SpringApplication object and call its run method to launch it
	return new SpringApplication(primarySources).run(args);
}
Copy the code

Follow up in the SpringApplication constructor:

// SpringApplication.java

public SpringApplication(Class
       ... primarySources) {
    // Continue to call another SpringApplication constructor
	this(null, primarySources);
}
Copy the code

Follow up with another SpringApplication constructor:

// SpringApplication.java

public SpringApplication(ResourceLoader resourceLoader, Class
       ... primarySources) {
	// [1] Assign the resourceLoader attribute. Note that the resourceLoader parameter passed is null
	this.resourceLoader = resourceLoader;
	Assert.notNull(primarySources, "PrimarySources must not be null");
	PrimarySources = springApplication. run(mainApplication. class, args); The MainApplication. Class
	this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
	// [3] Assign the webApplicationType attribute to determine the application type based on what type of class exists in the classpath
	this.webApplicationType = WebApplicationType.deduceFromClasspath();
	/ / [4] to initializers attribute assignment, use the SPI SpringBoot custom, since the spring. The factories in loading ApplicationContextInitializer interface implementation class and assign a value to the initializers properties
	setInitializers((Collection) getSpringFactoriesInstances(
			ApplicationContextInitializer.class));
	[5] Load the ApplicationListener interface implementation class from Spring. Factories using the SpringBoot custom SPI and assign values to the Listeners
	setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
	// [6] Assign the mainApplicationClass attribute, i.e. infer which class called main, and then assign the mainApplicationClass attribute, which will be used to print some logs later in the startup process.
	this.mainApplicationClass = deduceMainApplicationClass();
}
Copy the code

As you can see, building the SpringApplication object is actually doing some initialization work by assigning the member properties of the previous six SpringApplication classes:

  1. toresourceLoaderAttribute assignment.resourceLoaderProperty, the resource loader, passed in at this timeresourceLoaderParameters fornull;
  2. toprimarySourcesAttribute assignment.primarySourcesattributeNamely SpringApplication. Run (MainApplication. Class, args);In the incomingMainApplication.classThis class is the startup class of the SpringBoot project and is used for scanningConfigurationClass loadingbean;
  3. towebApplicationTypeAttribute assignment.webApplicationTypeProperty representing the application type, according toclasspathExistential correspondenceApplicationClass to judge. Because it’s based onwebApplicationTypeTo determine which one to createEnvironmentObject and create whichApplicationContextFor detailed analysis, please see the followingIn section 3.1;
  4. toinitializersAttribute assignment.initializersProperties forList<ApplicationContextInitializer<? >>Collection, using SpringBoot’s SPI mechanism fromspring.factoriesThese initializers are later applied to perform some initialization work when the container is initialized. The SPI mechanism implemented by SpringBoot itself is important, so it is analyzed in a separate section. For detailed analysis, see the following sectionThe fourth section;
  5. tolistenersAttribute assignment.listenersProperties forList<ApplicationListener<? >>Collection, also utilizing SpringBoot’s SPI mechanism fromspring.factoriesLoad in configuration file. Since SpringBoot launches events at different stages, these loaded listeners are used to listen for lifecycle events during SpringBoot launches.
  6. tomainApplicationClassAttribute assignment.mainApplicationClassProperty means containmainThe class of the function, that is, which class is being calledmainFunction, and then assigns the fully qualified name of the class tomainApplicationClassProperty to print some logs later in the startup process, as detailed belowIn section 3.2.

3.1 Infer the project application type

We then analyze tectonic SpringApplication object step [3] WebApplicationType. DeduceFromClasspath (); This code:

// WebApplicationType.java

public enum WebApplicationType {
        // Common applications
	NONE,
	// Servlet type Web application
	SERVLET,
	// Reactive Web applications
	REACTIVE;

	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";

	static WebApplicationType deduceFromClasspath(a) {
		"Org.springframework." + does not exist in classpath "Web. Servlet. DispatcherServlet" and "org. Anyone. Jersey. Servlet. ServletContainer"
		/ / return WebApplicationType. REACTIVE, it shows that is REACTIVE application
		if (ClassUtils.isPresent(WEBFLUX_INDICATOR_CLASS, null)
				&& !ClassUtils.isPresent(WEBMVC_INDICATOR_CLASS, null)
				&& !ClassUtils.isPresent(JERSEY_INDICATOR_CLASS, null)) {
			return WebApplicationType.REACTIVE;
		}
		If / / {" javax.mail. Servlet. Servlet ",
		// "org.springframework.web.context.ConfigurableWebApplicationContext" }
		// None of them exist in the classpath
		for (String className : SERVLET_INDICATOR_CLASSES) {
			if(! ClassUtils.isPresent(className,null)) {
				returnWebApplicationType.NONE; }}// Finally return to the normal Web application
		returnWebApplicationType.SERVLET; }}Copy the code

As shown in the above code, the application type is determined according to the classpath. That is, the specified flag class is determined by reflection loading of the classpath to determine whether the application is Reactive, Servlet type Web application or common application.

3.2 Infer which class calls main

Let’s skip steps [4] and [5] for constructing the SpringApplication object, First brought to the analysis of tectonic SpringApplication object step [6] this. MainApplicationClass = deduceMainApplicationClass (); This code:

// SpringApplication.java

privateClass<? > deduceMainApplicationClass() {try {
		The StackTraceElement object stores information about the call stack (class name, method name, etc.).
		StackTraceElement[] stackTrace = new RuntimeException().getStackTrace();
		// Iterate over the stackTrace array
		for (StackTraceElement stackTraceElement : stackTrace) {
			// If the stackTraceElement record calls a method name equal to main
			if ("main".equals(stackTraceElement.getMethodName())) {
				// Return the class name of the stackTraceElement record containing the main function
				returnClass.forName(stackTraceElement.getClassName()); }}}catch (ClassNotFoundException ex) {
		// Swallow and continue
	}
	return null;
}
Copy the code

Can see from StackTraceElement deduceMainApplicationClass method is the main purpose of the call stack in the array for which class calls the main method, and then return to the assigned to mainApplicationClass properties, It is then used to print some logs in the later startup process.

4 Interpretation of SPI mechanism of SpringBoot

Since SpringBoot’s SPI mechanism is an important point of knowledge, this section will analyze it separately. As we all know, SpringBoot does not use Java’s SPI mechanism. , is really full of dry goods), but a custom implementation of their own SPI mechanism. SpringBoot utilizes the SPI mechanism of a custom implementation to load initializer implementation classes, listener implementation classes, auto-configuration classes, and so on. If we’re adding auto-configuration classes or custom listeners, it’s important to configure them in Spring.Factories before SpringBoot loads them.

Ok, so let’s focus on how SpringBoot implements its own SPI mechanism.

Here is the code for step [4] and Step [5] of section 3 to construct the SpringApplication object, because step [4] and step [5] both use SpringBoot’s SPI mechanism to load the extension implementation class. So here only analysis step [4] setInitializers ((Collection) getSpringFactoriesInstances (ApplicationContextInitializer. Class)); This code, see SpringBoot getSpringFactoriesInstances method is how to realize its own set of SPI to load ApplicationContextInitializer initializer extension of the interface implementation class?

// SpringApplication.java

private <T> Collection<T> getSpringFactoriesInstances(Class<T> type) {
    / / continue calling overloaded getSpringFactoriesInstances methods for loading
    return getSpringFactoriesInstances(type, newClass<? > [] {}); }Copy the code

Continue to follow up overloading getSpringFactoriesInstances method:

// SpringApplication.java

private <T> Collection<T> getSpringFactoriesInstances(Class
       
         type, Class
        [] parameterTypes, Object... args)
        {
	[1] get the classloader
	ClassLoader classLoader = getClassLoader();
	// Use names and ensure unique to protect against duplicates
	// [2] Load the interface implementation classes from the Spring. factories configuration file by passing the interface type and classloader as parameters to the loadFactoryNames method
	Set<String> names = new LinkedHashSet<>(
			SpringFactoriesLoader.loadFactoryNames(type, classLoader));
	// [3] instantiate the interface implementation class loaded from Spring.Factories
	List<T> instances = createSpringFactoriesInstances(type, parameterTypes,
			classLoader, args, names);
	// [4]
	AnnotationAwareOrderComparator.sort(instances);
	// [5] Returns the loaded and instantiated interface implementation class
	return instances;
}
Copy the code

It can be seen that the most important steps [1], [2] and [3] in the SPI mechanism code of SpringBoot custom implementation are the above steps, which will be analyzed in the following sections.

4.1 Getting a class loader

Remember how Java implemented its own SPI mechanism? In this article, Java’s SPI mechanism uses a thread-context classloader to load extension classes by default. SpringBoot’s own SPI mechanism uses what class loader to load extension classes in the Spring.Factories configuration file?

ClassLoader = getClassLoader(); Here’s a sneak peek at the code:

// SpringApplication.java

public ClassLoader getClassLoader(a) {
	// We passed the null resourceLoader parameter to the SpringApplicaiton object, so the logic in the if statement will not be executed
	if (this.resourceLoader ! =null) {
		return this.resourceLoader.getClassLoader();
	}
	// Get the default class loader
	return ClassUtils.getDefaultClassLoader();
}
Copy the code

Follow up with the getDefaultClassLoader method:

// ClassUtils.java

public static ClassLoader getDefaultClassLoader(a) {
	ClassLoader cl = null;
	try {
	        // Get the thread context class loader
		cl = Thread.currentThread().getContextClassLoader();
	}
	catch (Throwable ex) {
		// Cannot access thread context ClassLoader - falling back...
	}
	// The logic here will not be executed
	if (cl == null) {
		// No thread context class loader -> use class loader of this class.
		cl = ClassUtils.class.getClassLoader();
		if (cl == null) {
			// getClassLoader() returning null indicates the bootstrap ClassLoader
			try {
				cl = ClassLoader.getSystemClassLoader();
			}
			catch (Throwable ex) {
				// Cannot access system ClassLoader - oh well, maybe the caller can live with null...}}}// Returns the thread context classloader you just got
	return cl;
}
Copy the code

SpringBoot uses the thread context classloader to load the extended implementation classes in the Spring. factories file.

4.2 Loading the SPI extension classes in the Spring. factories configuration file

Let’s look at the first step (2) the SpringFactoriesLoader. LoadFactoryNames (type, this). This code is how to load the spring factories SPI in the configuration file extension classes?

// SpringFactoriesLoader.java

public static List<String> loadFactoryNames(Class<? > factoryClass,@Nullable ClassLoader classLoader) {
        / / factoryClass SPI interface, such as ApplicationContextInitializer EnableAutoConfiguration interface, etc
	String factoryClassName = factoryClass.getName();
	// Continue calling the loadSpringFactories method to load the SPI extension class
	return loadSpringFactories(classLoader).getOrDefault(factoryClassName, Collections.emptyList());
}
Copy the code

Continue with the loadSpringFactories method:

// SpringFactoriesLoader.java

/** * The location to look for factories. * 

Can be present in multiple JAR files. */

public static final String FACTORIES_RESOURCE_LOCATION = "META-INF/spring.factories"; private static Map<String, List<String>> loadSpringFactories(@Nullable ClassLoader classLoader) { // Use classLoader as the key to retrieve the key from the cache MultiValueMap<String, String> result = cache.get(classLoader); if(result ! =null) { return result; } // If there are no records in the cache, go to the spring.factories configuration file to obtain them try { // Load the URL paths of all jar packages that contain the "matf-INF /spring.factories" filesEnumeration<URL> urls = (classLoader ! =null ? classLoader.getResources(FACTORIES_RESOURCE_LOCATION) : ClassLoader.getSystemResources(FACTORIES_RESOURCE_LOCATION)); result = new LinkedMultiValueMap<>(); // Traverse the urls path and add the key/value pairs of all spring.factories (key:SPI value:SPI extension) // load into the result collection while (urls.hasMoreElements()) { // Retrieve a URL URL url = urls.nextElement(); // Encapsulate the URL into an UrlResource object UrlResource resource = new UrlResource(url); // Load the spring.factories file key-value pair contents into the Properties object using the loadProperties method of PropertiesLoaderUtils Properties properties = PropertiesLoaderUtils.loadProperties(resource); // Iterate over the newly loaded key-value pair Properties object for(Map.Entry<? ,? > entry : properties.entrySet()) {// Fetch the SPI interface name String factoryClassName = ((String) entry.getKey()).trim(); // Iterate over the implementation class corresponding to the SPI interface name, namely the SPI extension class for (String factoryName : StringUtils.commaDelimitedListToStringArray((String) entry.getValue())) { // SPI interface name as key, SPI extension class as value in resultresult.add(factoryClassName, factoryName.trim()); }}}// Use the classLoader as the key and result as the value to cache cache.put(classLoader, result); // Finally returns the result object return result; } catch (IOException ex) { throw new IllegalArgumentException("Unable to load factories from location [" + FACTORIES_RESOURCE_LOCATION + "]", ex); }}Copy the code

The main thing the loadSpringFactories method does is load all the extended implementation classes for all SPI interfaces from all the Spring. factories configuration files in the classpath using the thread context classloaders that we previously obtained and put them into the cache. The spring-factories configuration file does not need to be loaded with the SPI extension implementation classes. The spring-factories configuration file does not need to be loaded with the SPI extension implementation classes. Such as after obtaining ApplicationListener FailureAnalyzer and EnableAutoConfiguration the extension of the interface implementation class can be directly obtained from the cache.

Thought 1: Why grab all the extended classes from the Spring. factories configuration file and put them in the cache at once? Instead of going to the Spring. factories configuration file every time according to the SPI interface?

Before thinking about 2: remember when I speak SpringBoot automatic configuration of the source code for mentioned AutoConfigurationImportFilter this interface? Now we should have a better idea of what this interface does.

After loading all SPI extension implementation classes, call getOrDefault(factoryClassName, collections.emptyList ()) to filter the current corresponding extension implementation class based on the SPI interface name. Such as incoming factoryClassName parameter called ApplicationContextInitializer interface here, So the interface will be the key from the cache the data just now ApplicationContextInitializer corresponding SPI interface extension implementation class. ApplicationContextInitializer retrieved from spring. Factories all the SPI interface corresponding extension implementation such as shown below:

4.3 Instantiate the SPI extension classes loaded from Spring.Factories

From the spring. The factories in get to ApplicationContextInitializer all SPI interface corresponding extension after implementation class, at this point to expand the SPI class to instantiate.

Now let’s look at the instantiation code of step [3] above: List

instances = createSpringFactoriesInstances(type, parameterTypes, classLoader, args, names); .

// SpringApplication.java

private <T> List<T> createSpringFactoriesInstances(Class
       
         type, Class
        [] parameterTypes, ClassLoader classLoader, Object[] args, Set
        
          names)
        
        {
	// Create a new Instances collection to store SPI extended class objects that will be instantiated later
	List<T> instances = new ArrayList<>(names.size());
	// Iterate through the name collection, which stores the fully qualified names of all SPI extension classes
	for (String name : names) {
		try {
			// Use reflection to load classes based on fully qualified namesClass<? > instanceClass = ClassUtils.forName(name, classLoader);// Assert whether the SPI extension class you just loaded is an SPI interface type
			Assert.isAssignable(type, instanceClass);
			// Get the constructor for the SPI extension classConstructor<? > constructor = instanceClass .getDeclaredConstructor(parameterTypes);// Instantiate the SPI extension class
			T instance = (T) BeanUtils.instantiateClass(constructor, args);
			// add instances to instances collection
			instances.add(instance);
		}
		catch (Throwable ex) {
			throw new IllegalArgumentException(
					"Cannot instantiate " + type + ":"+ name, ex); }}/ / return
	return instances;
}
Copy the code

The above code is very simple, and the main thing to do is to instantiate the SPI extension class. Well, SpringBoot’s custom SPI mechanism has been analyzed.

Thought 3: Why did SpringBoot abandon Java’s SPI and create a custom ONE?

5 subtotal

Well, that’s the end of this piece, and then summarize the previous knowledge points:

  1. Analysis of theSpringApplicationObject construction process;
  2. An SPI mechanism implemented by SpringBoot itself is analyzed.

6. Express your feelings

Since I began to write source code analysis articles in February, I have also known some technical masters, from them to see that the more powerful people work harder. In retrospect, I now have a very narrow range of knowledge, more importantly, I have no depth of technology involved, in a word, but also very vegetables, and see more powerful than their own big cattle are still so hard, I have no reason not to work hard? Like ding Wei teacher’s words: “only perseverance”. Then take one step at a time, believe that you can make greater progress, continue to work.