This article is participating in “Java Theme Month – Java Development Practice”, for details: juejin.cn/post/696719…

This is the 7th day of my participation in Gwen Challenge

start

In Spring Boot, we always start our application with one line of code:

SpringApplication.run(???.class, args);
Copy the code

The actual method we call is:

/** In SpringApplication */
	public static ConfigurableApplicationContext run(Class
       [] primarySources, String[] args) {
		return new SpringApplication(primarySources).run(args);
	}

/ / -- -- -- -- -- -- -- -- call

	public SpringApplication(Class
       ... primarySources) {
		this(null, primarySources);
	}

/ / -- -- -- -- -- call

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

These include:

  • Initialize SpringApplication
  • SpringApplication.run

Before reading the source code, as someone whose knowledge of Spring is limited to use, I felt that for Spring, I wanted it to do the following when it started:

  • Inject configuration items
  • Generate and put the objects I need into the corresponding references
  • Connect to the database, redis, and configuration in other configuration files
  • .

So let’s look at how to do this with our purpose.

  • Frameworks are proposed to solve repetitive tasks, so the lowest level implementation is not necessarily complex.

Initialize the

Next we parse the constructor of the SpringApplication.

The lowest level code for the constructor called is as follows:

/** In SpringApplication */

public SpringApplication(ResourceLoader resourceLoader, Class
       ... primarySources) {
		this.resourceLoader = resourceLoader;
		Assert.notNull(primarySources, "PrimarySources must not be null");
		this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
		this.webApplicationType = WebApplicationType.deduceFromClasspath();
		setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
		setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
		this.mainApplicationClass = deduceMainApplicationClass();
	}
Copy the code

Since the argument we brought in is NULL, the resourceLoader here is NULL.

In this step we initialize:

  • primarySources
  • webApplicationType
  • mainApplicationClass

And wakes up registered initializers and listeners. Next, we analyze the methods involved.

Determine the application type

Let’s take a look:

WebApplicationType.deduceFromClasspath()

What does this part of the code do?

The source code of this part is as follows:

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;
	}
Copy the code

This section determines the type of startup, which in this case is SERVLET.

  • With the Spring-wrapped classUtils, use reflection to see if the classes started are certain.

Set the initializer

This part from

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

To enter. We need to parse:

  • setInitializer
  • getSpringFactoriesInstances

getSpringFactoriesInstances

This part of the code is as follows:


private <T> Collection<T> getSpringFactoriesInstances(Class<T> type) {
   return getSpringFactoriesInstances(type, newClass<? > [] {}); }//

	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

The process is as follows:

  • Access to this

    • This step gets the classLoader from the current thread.
  • Fetch the factory name from the obtained classLoader.

    • Corresponding method of this step:

      SpringFactoriesLoader.loadFactoryNames(type, classLoader)

  • Initialize the factory based on the factory name obtained in the previous step.

  • According to the AnnotationAwareOrderComparator, ordered to get factory instance

  • Return to the factory instance.

setInitializer

It’s really just a simple assignment.

ApplicationContextInitializer

//todo

Setting up listeners

This section is similar to the above, corresponding to the code:

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

SetListeners are simple set methods.

Get the main Application class

The corresponding code is:

	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

Gets the stack of the current Runtime and the class that currently executes main.

The run!

The constructor is done, and it’s really just a bunch of configuration and listener initialization constructor assignments.

Next, parse the run method.

This part of the code is as follows:

public ConfigurableApplicationContext run(String... args) {
   StopWatch stopWatch = new StopWatch();
   stopWatch.start();
   ConfigurableApplicationContext context = null;
   Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
   configureHeadlessProperty();
   SpringApplicationRunListeners listeners = getRunListeners(args);
   listeners.starting();
   try {
      ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
      ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments);
      configureIgnoreBeanInfo(environment);
      Banner printedBanner = printBanner(environment);
      context = createApplicationContext();
      exceptionReporters = getSpringFactoriesInstances(SpringBootExceptionReporter.class,
            new Class[] { ConfigurableApplicationContext.class }, context);
      prepareContext(context, environment, listeners, applicationArguments, printedBanner);
      refreshContext(context);
      afterRefresh(context, applicationArguments);
      stopWatch.stop();
      if (this.logStartupInfo) {
         new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), stopWatch);
      }
      listeners.started(context);
      callRunners(context, applicationArguments);
   }
   catch (Throwable ex) {
      handleRunFailure(context, ex, exceptionReporters, listeners);
      throw new IllegalStateException(ex);
   }

   try {
      listeners.running(context);
   }
   catch (Throwable ex) {
      handleRunFailure(context, ex, exceptionReporters, null);
      throw new IllegalStateException(ex);
   }
   return context;
}
Copy the code
  1. At the beginning, we started a stopWatch, which was mainly to record how much time we spent to start the project.

  2. A headless Property is configured. (//todo)

  3. Here are the listeners.

    • This step is similar to the setListener logic above.
  4. Remind the listeners of the previous step.

    The code is: listeners.starting();

    • The observational design pattern is used here:

      	void starting(a) {
      		for (SpringApplicationRunListener listener : this.listeners) { listener.starting(); }}Copy the code

      In fact we only register a listener here: EventPublishingRunListener

      So our call goes here:

      //in EventPublishingRunListener
      
      	public void starting(a) {
      		this.initialMulticaster.multicastEvent(new ApplicationStartingEvent(this.application, this.args));
      	}
      
      	public void multicastEvent(final ApplicationEvent event, @Nullable ResolvableType eventType) { ResolvableType type = (eventType ! =null ? eventType : resolveDefaultEventType(event));
      		Executor executor = getTaskExecutor();
      		for(ApplicationListener<? > listener : getApplicationListeners(event, type)) {if(executor ! =null) {
      				executor.execute(() -> invokeListener(listener, event));
      			}
      			else{ invokeListener(listener, event); }}}Copy the code

      This is where the properties of our input events are parsed and distributed to the corresponding listeners.

  5. Configure the Application startup parameter (we entered null).

  6. Configure the environment.

    The environment is initialized based on the webApplicationType obtained in our initialization.

    At the same time:

    • Notify all registered listeners of the preparation of the environment and make it run.
    • Bind the environment to Spring.
  7. Configure ignored beans.

  8. Print the banner.

  9. With webApplicationType, create applicationContext.

  10. Prepare the context. (//todo, similar to 6)

  11. Refresh context

  12. Start the listeners.

  13. Start the runners.