One, foreword

In the last article, we mainly introduced the @SpringBootApplication annotation for automatic configuration, so first let’s review what this annotation does for our SprngBoot project: We can summarize the key steps of automatic configuration and corresponding annotations as follows:

2.@Conditional——–>>>>>> Set automatic configuration condition dependency

3. @ EnableConfigurationProperties with @ ConfigurationProperties – > reads the configuration file is converted into a bean.

4.@EnableAutoConfiguration, @AutoConfigurationPackage, and @import -> implement bean discovery and loading.

Today, we will analyze its startup process through the source code.

This article is based on the 2.0.4.RELEASE version. To read this article, you need to have some basic knowledge of Java and the Spring Framework. If you do not know what Spring Boot is, it is recommended to read the Spring Boot tutorial on the official website first.

Spring Boot entry class

Above is the simplest generic entry class for Spring Boot. The entry class is required to be the first class under the topmost package that contains the main method, using the @SpringBootApplication annotation to enable the SpringBoot feature. Use the SpringApplication.run method to start the Spring Boot project.

First look at this class inside the run method call source:

The second parameter, args, is the application parameter passed to the application.

We instantiate a SpringApplication object with the main resource class and then call the run method of the object, so we analyze the startup source in two steps.

The instantiation process of SpringApplication

Follow the above run to the following method:

Go to SpringApplication and see the following source code:

As you can see from the above source code, the entire instantiation process has seven steps:

1. Resource initialization The resource loader is null

this.resourceLoader = resourceLoader;
Copy the code

2. Assert that the main loading resource class cannot be null; otherwise, an error is reported

Assert.notNull(primarySources, "PrimarySources must not be null");
Copy the code

3. Initialize the main load resource class set and deduplicate it

this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
Copy the code

4. Infer the current WEB application type

this.webApplicationType = WebApplicationType.deduceFromClasspath();
Copy the code

Here’s the WebApplicationType method to look at the source code and the associated constructor:

public enum WebApplicationType {

	/** * The application should not run as a web application and should not start an * embedded web server. */
	NONE,

	/** * The application should run as a servlet-based web application and should start an * embedded servlet web server. * /
	 
	SERVLET,

	/** * The application should run as a reactive web application and should start an * embedded reactive web server. */
	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) {
		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;
	}

	static WebApplicationType deduceFromApplicationContext(Class
        applicationContextClass) {
		if (isAssignable(SERVLET_APPLICATION_CONTEXT_CLASS, applicationContextClass)) {
			return WebApplicationType.SERVLET;
		}
		if (isAssignable(REACTIVE_APPLICATION_CONTEXT_CLASS, applicationContextClass)) {
			return WebApplicationType.REACTIVE;
		}
		return WebApplicationType.NONE;
	}

	private static boolean isAssignable(String target, Class
        type) {
		try {
			return ClassUtils.resolveClassName(target, null).isAssignableFrom(type);
		}
		catch (Throwable ex) {
			return false; }}}Copy the code

Spring Boot 2 supports responsive programming. Spring Boot 2 supports responsive programming.

5. Set the application context initializer

setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
Copy the code

Entering ApplicationContextInitializer, we can know the role of:

public interface ApplicationContextInitializer<C extends ConfigurableApplicationContext> {

	/**
	 * Initialize the given application context.
	 * @param applicationContext the application to configure
	 */
	void initialize(C applicationContext);

}
Copy the code

Used to initialize the specified Spring application context, such as registering property resources, enabling Profiles, and so on.

Look at setInitializers method source code, is actually initialize a ApplicationContextInitializer application context instance initializer collection.

	public void setInitializers(Collection
       > initializers) {
		this.initializers = new ArrayList<>();
		this.initializers.addAll(initializers);
	}
Copy the code

Finally, we look at the core method getSpringFactoriesInstances its source code is as follows:

	private <T> Collection<T> getSpringFactoriesInstances(Class
       
         type, Class
        [] parameterTypes, Object... args)
        {
		ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
		// 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

Setting up an application context initializer can be done in five steps. Here is the core of instantiation:

5.1) Gets the current thread context classloader

ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
Copy the code

5.2) to obtain ApplicationContextInitializer instance name collection and to heavy

Set<String> names = new LinkedHashSet<>(SpringFactoriesLoader.loadFactoryNames(type, classLoader));
Copy the code

The loadFactoryNames source code is as follows:

	public static List<String> loadFactoryNames(Class
        factoryClass, @Nullable ClassLoader classLoader) {
		String factoryClassName = factoryClass.getName();
		return loadSpringFactories(classLoader).getOrDefault(factoryClassName, Collections.emptyList());
	}
	
	public static final String FACTORIES_RESOURCE_LOCATION = "META-INF/spring.factories";
	
	private static Map<String, List<String>> loadSpringFactories(@Nullable ClassLoader classLoader) {
		MultiValueMap<String, String> result = cache.get(classLoader);
		if(result ! =null) {
			return result;
		}

		try{ Enumeration<URL> urls = (classLoader ! =null ?
					classLoader.getResources(FACTORIES_RESOURCE_LOCATION) :
					ClassLoader.getSystemResources(FACTORIES_RESOURCE_LOCATION));
			result = new LinkedMultiValueMap<>();
			while (urls.hasMoreElements()) {
				URL url = urls.nextElement();
				UrlResource resource = new UrlResource(url);
				Properties properties = PropertiesLoaderUtils.loadProperties(resource);
				for(Map.Entry<? ,? > entry : properties.entrySet()) { String factoryClassName = ((String) entry.getKey()).trim();for (String factoryName : StringUtils.commaDelimitedListToStringArray((String) entry.getValue())) {
						result.add(factoryClassName, factoryName.trim());
					}
				}
			}
			cache.put(classLoader, result);
			return result;
		}
		catch (IOException ex) {
			throw new IllegalArgumentException("Unable to load factories from location [" +
					FACTORIES_RESOURCE_LOCATION + "]", ex); }}Copy the code

According to the class path under the meta-inf/spring. The factories of all configuration file parsing and obtain ApplicationContextInitializer interface class path name.

Spring – the boot – autoconfigure – 2.0.4. RELEASE. The jar! / meta-INF /spring.factories

# Initializers org.springframework.context.ApplicationContextInitializer=\ org.springframework.boot.autoconfigure.SharedMetadataReaderFactoryContextInitializer,\ org.springframework.boot.autoconfigure.logging.ConditionEvaluationReportLoggingListener # Application Listeners org.springframework.context.ApplicationListener=\ org.springframework.boot.autoconfigure.BackgroundPreinitializer # Auto  Configuration Import Listeners org.springframework.boot.autoconfigure.AutoConfigurationImportListener=\ org.springframework.boot.autoconfigure.condition.ConditionEvaluationReportAutoConfigurationImportListener # Auto Configuration Import Filters org.springframework.boot.autoconfigure.AutoConfigurationImportFilter=\ org.springframework.boot.autoconfigure.condition.OnClassCondition # Auto Configure ......Copy the code

5.3) Create a list of initializer instances based on the classpath above

List<T> instances = createSpringFactoriesInstances(type, parameterTypes, classLoader, args, names);
Copy the code

5.4) Initializer instance list sorting

AnnotationAwareOrderComparator.sort(instances);
Copy the code

5.5) Return the instance object

return instances;
Copy the code

6. Set the listener

setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
Copy the code

What does ApplicationListener do? The source code is as follows. (write another application of springboot2 listener when you are free)

@FunctionalInterface
public interface ApplicationListener<E extends ApplicationEvent> extends EventListener {

	/**
	 * Handle an application event.
	 * @param event the event to respond to
	 */
	void onApplicationEvent(E event);

}
Copy the code

This interface extends the Java JDK java.util.EventListener interface and implements observer mode. It is generally used to define the event types of interest, restricted to subclasses of ApplicationEvent. This also inherits the JDK’s java.util.eventobject interface.

Setting the listener calls the same method as setting the initializer, except that the type passed in is different. Setting the listener’s interface type is: GetSpringFactoriesInstances, corresponding spring – the boot – autoconfigure – 2.0.4. The jar! The/meta-INF /spring.factories files are the Application Listeners

You can see from the file that there is currently only one BackgroundPreinitializer listener.

7. Infer the main entry application class

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

By constructing a runtime exception, iterating through the name of the method in the exception stack, retrieving the stack frame with the method name main, retrieving the name of the entry class and returning that class.

Four,

Today, the main analysis of SpringBoot initialization instances of source code analysis, this chapter temporarily analyzed to: