Initialization of the SpringApplication

Having examined the @SpringBootApplication annotation on the bootstrap class,

Then continue to analyze the main method, just call a SpringApplication. Run (SpringbootApplication. Class, args), started the web container, we see the run method do

public static ConfigurableApplicationContext run(Class
       [] primarySources, String[] args) {
		return new SpringApplication(primarySources).run(args);
}
Copy the code

As you can see, the SpringApplication is initialized and its run method is executed.

Starting from the constructor of SpringApplication, the constructor of SpringApplication mainly does four things:

  1. Configuration source
  2. Infer the Web application type
  3. Apply context initiators and apply event listeners
  4. Derive references to the main class

The source code is as follows:

@SuppressWarnings({ "unchecked"."rawtypes" })
	public SpringApplication(ResourceLoader resourceLoader, Class
       ... primarySources) {
		this.resourceLoader = resourceLoader;
		Assert.notNull(primarySources, "PrimarySources must not be null");
        / / 1. Configuration source
		this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
        //2. Infer the Web application type
		this.webApplicationType = WebApplicationType.deduceFromClasspath();
        //3. Apply context initializer and apply event listener
		setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
		setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
        //4. Derive references to the main class
		this.mainApplicationClass = deduceMainApplicationClass();
	}
Copy the code

Configuration source

Configuration springApplication start class, in the form of Java configuration we are very familiar with, direct use: springApplication. Run (MySpringBootApplication. Class, args); , including MySpringBootApplication. Class is the second parameter primarySources constructors

XML configuration:

SpringApplication springApplication = new SpringApplication();
springApplication.setSources();
springApplication.run(args);
Copy the code

This is done by adding parameters to the setSources method, such as the class name, package name, and XML path

PrimarySources is a set of data sources used by Spring Boot.

public Set<Object> getAllSources(a) {
		Set<Object> allSources = new LinkedHashSet<>();
		if(! CollectionUtils.isEmpty(this.primarySources)) {
			allSources.addAll(this.primarySources); // The source configured in Java mode
		}
		if(! CollectionUtils.isEmpty(this.sources)) {
			allSources.addAll(this.sources);  // The source configured in XML mode
		}
		return Collections.unmodifiableSet(allSources);
}

private void prepareContext(ConfigurableApplicationContext context, ConfigurableEnvironment environment, //..... // Load the sources Set sources = getAllSources();
		Assert.notEmpty(sources, "Sources must not be empty");
		load(context, sources.toArray(new Object[0]));
		listeners.contextLoaded(context);
}
Copy the code

The prepareContext method is called from the SpringApplication’s Run method, and as you can see, sources is passed to the load method.

protected void load(ApplicationContext context, Object[] sources) {
		if (logger.isDebugEnabled()) {
			logger.debug("Loading source " + StringUtils.arrayToCommaDelimitedString(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();
}
Copy the code

B: well… Follow up:

protected BeanDefinitionLoader createBeanDefinitionLoader(BeanDefinitionRegistry registry, Object[] sources) {
		return new BeanDefinitionLoader(registry, sources);
}
Copy the code

Look for BeanDefinitionLoader constructor:

BeanDefinitionLoader(BeanDefinitionRegistry registry, Object... sources) {
		Assert.notNull(registry, "Registry must not be null");
		Assert.notEmpty(sources, "Sources must not be empty");
		this.sources = sources;
		this.annotatedReader = new AnnotatedBeanDefinitionReader(registry);
		this.xmlReader = new XmlBeanDefinitionReader(registry);
		if (isGroovyPresent()) {
			this.groovyReader = new GroovyBeanDefinitionReader(registry);
		}
		this.scanner = new ClassPathBeanDefinitionScanner(registry);
		this.scanner.addExcludeFilter(new ClassExcludeFilter(sources));
}
Copy the code

As you can see, respectively by AnnotatedBeanDefinitionReader and XmlBeanDefinitionReader these two classes to read and will load for the Spring Bean configuration source. This also shows that there can be two data sources during the SpringBoot configuration phase.

Infer the Web application type and main bootstrap class

Spring Boot can be used not only as a WEB project, but also as a non-Web project, so how does SpringBoot infer the application type? The answer is to infer the WEB application type based on whether the relevant implementation class exists in the current application ClassPath. Types are defined in the enumeration class WebApplicationType, and there are three types:

Web Reactive: enumerated types for WebApplicationType Reactive, corresponding to the class path: org. Springframework. Web. Reactive. DispatcherHandler

Web Servlet: enumerated types for WebApplicationType Servlet, the corresponding class path: org. Springframework. Web. Servlet. DispatcherServlet

Non-web: The enumeration type is WebApplicationType.None

DeduceFromClasspath determines the type in WebApplicationType:

static WebApplicationType deduceFromClasspath(a) {
         / / in the presence of classpath Web Reactive class path and there is no WebApplicationType. SERVLET class path is enabled
         / / WEB Reactive container
		if (ClassUtils.isPresent(WEBFLUX_INDICATOR_CLASS, null) && !ClassUtils.isPresent(WEBMVC_INDICATOR_CLASS, null)
				&& !ClassUtils.isPresent(JERSEY_INDICATOR_CLASS, null)) {
			return WebApplicationType.REACTIVE;
		}
         // Non-Web types are enabled when none exist on the classpath
		for (String className : SERVLET_INDICATOR_CLASSES) {
			if(! ClassUtils.isPresent(className,null)) {
				returnWebApplicationType.NONE; }}// The Tomcat container is enabled by default
		return WebApplicationType.SERVLET;
}
Copy the code

Apply context initiators and apply event listeners

The following two methods are called to implement the initialization context and event listener:

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

Both initialization of the same, so you just need to pick just one to see, here see the initial application context, first by getSpringFactoriesInstances (ApplicationContextInitializer. Class) returns a collection of As you can guess from the name of this method, the factory mode is used to initialize the application context.

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

First look at the through SpringFactoriesLoader. LoadFactoryNames (type, this) this method, to obtain a set collection, introduced to the two parameters, First is ApplicationContextInitializer. The class, the second is the class loader, look into the method:

/**
	 * Load the fully qualified class names of factory implementations of the
	 * given type from {@value #FACTORIES_RESOURCE_LOCATION}, using the given
	 * class loader.
	 * @param factoryType the interface or abstract class representing the factory
	 * @param classLoader the ClassLoader to use for loading resources; can be
	 * {@code null} to use the default
	 * @throws IllegalArgumentException if an error occurs while loading factory names
	 * @see #loadFactories
	 */
	public static List<String> loadFactoryNames(Class
        factoryType, @Nullable ClassLoader classLoader) {
		String factoryTypeName = factoryType.getName();
		return loadSpringFactories(classLoader).getOrDefault(factoryTypeName, Collections.emptyList());
	}
Copy the code

What can we learn through annotation, this method for inheritance from # FACTORIES_RESOURCE_LOCATION ApplicationContextInitializer of this class of all classes of the fully qualified class name, Where #FACTORIES_RESOURCE_LOCATION is a string constant: meta-INF /spring.factories

The content of this spring.factories looks like this:

# Initializers
org.springframework.context.ApplicationContextInitializer=\
org.springframework.boot.autoconfigure.SharedMetadataReaderFactoryContextInitializer,\
org.springframework.boot.autoconfigure.logging.ConditionEvaluationReportLoggingListener
Copy the code

Parsing the contents of this file to get the fully qualified class name returns a Set of strings, which is the Set< string > names we saw at the beginning.

And then passed to the createSpringFactoriesInstances method, an instance and then sort

private <T> List<T> createSpringFactoriesInstances(Class
       
         type, Class
        [] parameterTypes, ClassLoader classLoader, Object[] args, Set
        
          names)
        
        {
		List<T> instances = new ArrayList<>(names.size());
		for (String name : names) {
			try{ Class<? > instanceClass = ClassUtils.forName(name, classLoader); Assert.isAssignable(type, instanceClass); Constructor<? > constructor = instanceClass.getDeclaredConstructor(parameterTypes); T instance = (T) BeanUtils.instantiateClass(constructor, args); instances.add(instance); }catch (Throwable ex) {
				throw new IllegalArgumentException("Cannot instantiate " + type + ":"+ name, ex); }}return instances;
}
Copy the code

Derive references to the main class

As mentioned earlier, we usually write the boot class code for SpringBoot as follows:

@SpringBootApplication
public class MySpringBootApplication {
   public static void main(String[] args) { SpringApplication.run(MySpringBootApplication.class, args); }}Copy the code

We can clearly see that the main class is MySpringBootApplication, but SpringBoot can be launched in multiple ways, and the first argument to SpringApplication.run is variable, that is, I can start MySpringBootApplication by calling springApplication. run in the main method of another class, then that class is the main class, not MySpringBootApplication:

public class App {
    public static void main(String[] args) { SpringApplication .run(MySpringBootApplication.class,args); }}Copy the code

The method derived from the reference to the main class is called in the constructor of SpringBoot:

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

Debug check:

As you can see, SpringBoot iterates through the exception stack to determine the main method, and finds that the Chinese method name of the class named App is main, thus deriving App as the main class.

So SpringBoot’s boot main class is derived by SpringBoot itself, not passed on by us.