SpringApplication construction method of SpringBoot source code analysis core source code analysis

Posted by asmith on Fri, 07 Jan 2022 03:59:36 +0100

    earlier, I introduced the core process of SpringBoot startup. This article begins to introduce the details of the specific implementation of SpringBoot startup.

Spring application constructor

  first, let's look at how the spring application construction method helps us complete these four core operations.

	@SuppressWarnings({ "unchecked", "rawtypes" })
	public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
		// The passed resourceLoader is null
		this.resourceLoader = resourceLoader;
		Assert.notNull(primarySources, "PrimarySources must not be null");
		// Record the configuration class name of the main method
		this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
		// Record the type of the current project
		this.webApplicationType = WebApplicationType.deduceFromClasspath();
		// Load configuration in spring The type corresponding to the ApplicationContextInitializer in the factories file and instantiate it
		// The loaded data is stored in the initializers member variable.
		setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
		// Initialize the listener and store the loaded listener instance object in the listeners member variable
		setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
		// The Class object where the main method is located is pushed back and recorded in the mainApplicationClass object
		this.mainApplicationClass = deduceMainApplicationClass();
	}

1.webApplicationType

  first, let's look at how webApplicationType deduces the type of the currently started project. It can be seen from the code that it is derived from the ClassPath through the deduceFromClassPath() method.

this.webApplicationType = WebApplicationType.deduceFromClasspath();

  track in to see the code

	static WebApplicationType deduceFromClasspath() {
		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)) {
				return WebApplicationType.NONE;
			}
		}
		return WebApplicationType.SERVLET;
	}

  before looking at the overall implementation logic, let's look at two contents respectively. The first is that the relevant static variables are used in the above code.

  these static variables are actually the full Class paths of some bound Java classes. The second is classutils Ispresent () method. The logic of this method is also very simple, that is, it obtains the Class object of the corresponding type through reflection. If it exists, it returns true, otherwise it returns false

  so the logic of this derivation is very clear

2.setInitializers

  then let's look at how to implement the loading initializer.

// Load configuration in spring The type corresponding to the ApplicationContextInitializer in the factories file and instantiate it
		// The loaded data is stored in the initializers member variable.
		setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));

  first, all initializers implement the ApplicationContextInitializer interface, that is, load the relevant implementation classes according to this type.

public interface ApplicationContextInitializer<C extends ConfigurableApplicationContext> {
    void initialize(C var1);
}

  the key method for loading is the getspringfactoryesinstances () method. This method loads spring The key in the factories file is org springframework. context. The value of applicationcontextinitializer.

Spring boot project

# Application Context Initializers
org.springframework.context.ApplicationContextInitializer=\
org.springframework.boot.context.ConfigurationWarningsApplicationContextInitializer,\
org.springframework.boot.context.ContextIdApplicationContextInitializer,\
org.springframework.boot.context.config.DelegatingApplicationContextInitializer,\
org.springframework.boot.rsocket.context.RSocketPortInfoApplicationContextInitializer,\
org.springframework.boot.web.context.ServerPortInfoApplicationContextInitializer

Spring boot autoconfigure project

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

  the specific loading method is getspringfactiesinstance(). Let's go to view it

	private <T> Collection<T> getSpringFactoriesInstances(Class<T> type, Class<?>[] parameterTypes, Object... args) {
		// Gets the current context class loader
		ClassLoader classLoader = getClassLoader();
		// The obtained extension class name is stored in the set collection to prevent duplication
		Set<String> names = new LinkedHashSet<>(SpringFactoriesLoader.loadFactoryNames(type, classLoader));
		// Create extension point instance
		List<T> instances = createSpringFactoriesInstances(type, parameterTypes, classLoader, args, names);
		AnnotationAwareOrderComparator.sort(instances);
		return instances;
	}

    first enter springfactoriesloader See the process of loading files in loadfactorynames (type, classloader)

  then let's look at the loadSpringFactories method

  it will be clearer to check through Debug

  through the loadSpringFactories method, we see that spring All the information in the factories file has been loaded into memory, but now we only need to load the data of ApplicationContextInitializer type. At this time, we can check it through the getOrDefault() method.

  enter the method to view

   then the corresponding instance object will be obtained according to the reflection.

  well, in fact, we know that the function of the getspringfactoryesinstances method is to help us get the definitions in meta-inf / spring The in the factories file can be the value of ApplicationContextInitializer. And obtain the instance object by reflection. Then the object information of the instance is stored in the initializers attribute of spring application.

3.setListeners

    after the function of setInitializers() method is clear, it is very simple to look at setListeners() method. getSpringFactoriesInstances method is called, but the types passed in are different. That is, meta-inf / spring Different information defined in the factories file.

  that is, loading is defined in meta-inf / spring All listeners declared in the factories file, and the obtained listeners are stored in the listeners attribute of spring application.

  the default loaded listeners are:

4.mainApplicationClass

  finally, let's take a look at how the durucemainapplicaitonclass () method deduces the Class object where the main method is located. Through the source code, we can see that it is implemented through StackTrace.

StackTrace: when learning function calls, we all know that each function has its own stack space. When a function is called, a new stack space is created. Then a function call stack is finally formed through the nested calls of functions

StackTrace is actually a link that records the execution of program methods. It can be presented more intuitively through Debug.

    then we can get the relevant call links. For the rest, we only need to get each link to judge whether the executed method name is main.

                     !