Introduce!

We all know that SpringBoot has always had a loud slogan “convention over configuration” since its launch. What’s the purpose? That certainly is to reduce the software developers in the middle of all kinds of tedious configuration, think that year life in SSH, SSM era of students, good guy, a day to spend half of the configuration file, always give you a bizarre error, which have time to fall in love with girls, you don’t work overtime who overtime?

Bing interview is not hanging by the interviewer situation, today to share with you, Springboot is so powerful, we do not need to do so much configuration? So how does it do that? Where is its configuration? Is how to start the role and so on a series of question marks in with his girlfriend flirtatious time, will still emerge from time to time. This seriously affected our programmers “xing” happy life, in order to let the programmers enjoy getting my girlfriend’s life, and the interview is not the interviewer diao, today let’s follow the source code to explore together under springboot exactly how to do “convention over configuration,” fasten your seat belt, and the driver to take you racing hi ~

First, let’s create a springboot project, version, use Springboot 2.3.4RELEASE.

Add a dependency here to indicate that this is a Web project!

<dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-web</artifactId>
</dependency>

Copy the code

Once you’ve created it, you can take a look at the SpringBoot source code to explore its startup process. In a few words, you can find the main entry method of the application and make a breakpoint on it.

Once started, F5 enters the run() method

/** * Static helper that can be used to run a {@link SpringApplication} from the * specified sources using default settings and user supplied arguments. * @param primarySources the primary sources to load * @param args the application arguments (usually passed from a Java main method) * @return the running {@link ApplicationContext} */ public static ConfigurableApplicationContext run(Class<? >[] primarySources, String[] args) { return new SpringApplication(primarySources).run(args); }Copy the code

At this point new SpringApplication(primary resources) is executed to create the Spring application object, and F5 is followed by the SpringApplication constructor.

Public SpringApplication(ResourceLoader ResourceLoader, Class<? >... PrimarySources) {this.resourceLoader = resourceLoader; Assert.notNull(primarySources, "PrimarySources must not be null"); this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources)); // 1. Types of possible Web application types. this.webApplicationType = WebApplicationType.deduceFromClasspath(); / / 2. Set the initial application context setInitializers ((Collection) getSpringFactoriesInstances (ApplicationContextInitializer. Class)); / / 3. Set the initialization to monitor setListeners ((Collection) getSpringFactoriesInstances (ApplicationListener. Class)); / / 4. The deduction of the main program class enclosing mainApplicationClass = deduceMainApplicationClass (); }Copy the code

A lot of unknown things happen when this object is initialized, heh heh 😋, here we are to decrypt one by one, layer by layer undress it “clothes”

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; }} / / here we test the web container to return WebApplicationType. SERVLET. }Copy the code

Infer the Type of Web application

This code is intended to infer what kind of Web application our application is

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; // Reactive Web applications (webflux)Copy the code

Of course, we add web dependencies from the beginning, so of course we are servlet containers.

Initialize the application context

When set to initialize the application context is first performed ‘getSpringFactoriesInstances ApplicationContextInitializer. Class’ method, Parameter is ApplicationContextInitializer. The class bytecode object.

private <T> Collection<T> getSpringFactoriesInstances(Class<T> type, Class<? >[] parameterTypes, Object... args) { ClassLoader classLoader = getClassLoader(); // Use names and ensure unique to protect against duplicates Set<String> names = new LinkedHashSet<>( // Loading ApplicationContextInitializer. Class type of the class. / / here is the incoming parameters ApplicationContextInitializer clas SpringFactoriesLoader.loadFactoryNames(type, classLoader)); / / instantiate the class that is loaded into the List < T > instances = createSpringFactoriesInstances (parameterTypes type, this, args, names); AnnotationAwareOrderComparator.sort(instances); // return instances; }Copy the code
public static List<String> loadFactoryNames(Class<? > factoryType, @Nullable ClassLoader classLoader) { String factoryTypeName = factoryType.getName(); return (List)loadSpringFactories(classLoader).getOrDefault(factoryTypeName, Collections.emptyList()); }Copy the code

Let’s see how it loads these classes. Okay

private static Map<String, List<String>> loadSpringFactories(@nullable ClassLoader ClassLoader) { String> result = cache.get(classLoader); if (result ! = null) { return result; } try {public static final String ACTORIES_RESOURCE_LOCATION = "meta-INF /spring.factories"; public static final String FACTORIES_RESOURCE_LOCATION = "META-INF/spring.factories"; 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 all loaded classes return result; } catch (IOException ex) { throw new IllegalArgumentException("Unable to load factories from location [" + FACTORIES_RESOURCE_LOCATION + "]", ex); }}Copy the code

The breakpoints show that the application loaded the spring.factores file from the following jars.

Double-click Shift to search spring.Factories to see that it exists in the following projects

Spring. factores under spring-boot-2.3.4.release.jar

Spring – the boot – autoconfigure – 2.3.4. RELEASE. The spring under the jar. The factores

Spring beans – 5.2.9. RELEASE. The spring under the jar. The factores

From the Map according to the org. Springframework. Context. ApplicationContextInitializer type to get the needed initialization, The breakpoint enters getOrDefault(factoryClassName, collections.emptyList ()); methods

Classes that need to be initialized are then instantiated and added to a collection for later use

Initialize the listener class

The most important thing is this

When we follow through, we will see that the same code is used to initialize the listener class as above to initialize the application context. The only difference is getSpringFactoriesInstances ApplicationListener. Class) pass in is · ApplicationListener. Class so there is no longer here.

Four, deduce the main program class

This is the most critical piece of code

this.mainApplicationClass = deduceMainApplicationClass();
Copy the code

This completes the process of initializing the springapplication during the springboot startup process.

! summary

It mainly describes the process of initializing SpringApplication during springBoot startup, which is roughly divided into four steps:

  1. Deduce the type of web application (if NONE is added)
  2. Initialize ApplicationContextInitializer
  3. Initialize the ApplicationListener
  4. Push the main program class

This completes the initialization of the first SpringApplication in four steps.