[001] SpringBoot2 startup process analysis 1 (source version 2.0.5.RELEASE)

Posted by Chips on Tue, 14 Jan 2020 04:07:22 +0100

1. entry:

@SpringBootApplication
public class BusApplication {

   public static void main(String[] args) {
      SpringApplication.run(BusApplication.class, args);
   }
}

By calling

public static ConfigurableApplicationContext run(Class<?> primarySource, String... args) Static method

There are two main directions of execution clues:

(new SpringApplication(primarySources)).run(args);

The above code is divided into two steps:

1. Creation of springapplication object

new SpringApplication(primarySources)

2. Call the run method of the SpringApplication object

The following analysis will be based on these two aspects.

2. Creation of springapplication object

The main code flow is as follows:

public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
    this.sources = new LinkedHashSet();
    this.bannerMode = Mode.CONSOLE;
    this.logStartupInfo = true;
    this.addCommandLineProperties = true;
    this.headless = true;
    this.registerShutdownHook = true;
    this.additionalProfiles = new HashSet();
    this.isCustomEnvironment = false;
    this.resourceLoader = resourceLoader;
    Assert.notNull(primarySources, "PrimarySources must not be null");
    this.primarySources = new LinkedHashSet(Arrays.asList(primarySources));
   //Infer application type
    this.webApplicationType = this.deduceWebApplicationType();
    //setInitializers initialization module, load ApplicationContextInitializer
    this.setInitializers(this.getSpringFactoriesInstances(ApplicationContextInitializer.class));
    //Load listener
    this.setListeners(this.getSpringFactoriesInstances(ApplicationListener.class));
   // Infer that the class where the mian method is located [there are colored eggs here ---- pass] will analyze and obtain the stack information through new RuntimeException() in the future
    this.mainApplicationClass = this.deduceMainApplicationClass();
}

2.1 analysis of inferred application types

this.webApplicationType = this.deduceWebApplicationType();

There are three types of enumeration class definitions:

public enum WebApplicationType {
    NONE,
    SERVLET,
    REACTIVE;

    private WebApplicationType() {
    }
}

This method focuses on calling

ClassUtils.isPresent(String className, @Nullable ClassLoader classLoader)

In the future, it will be analyzed specially, not shown here, and a lot of optimizations have been made for this type

2.2 setInitializers initialization module, load ApplicationContextInitializer

Key analysis:

private <T> Collection<T> getSpringFactoriesInstances(Class<T> type) {
    return this.getSpringFactoriesInstances(type, new Class[0]);
}

Reading the Spring Boot code, you will find a lot of calls to this method. This is the SPI mechanism that Spring uses JAVA for reference

Special analysis will be made in the follow-up meeting of SPI

getSpringFactoriesInstances(ApplicationContextInitializer.class)

Analysis of specific steps:

private <T> Collection<T> getSpringFactoriesInstances(Class<T> type, Class<?>[] parameterTypes, Object... args) {
//1 get class loader
    ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
//
    Set<String> names = new LinkedHashSet(SpringFactoriesLoader.loadFactoryNames(type, classLoader));
//
    List<T> instances = this.createSpringFactoriesInstances(type, parameterTypes, classLoader, args, names);
//sort
    AnnotationAwareOrderComparator.sort(instances);
    return instances;
}

Key analysis:

SpringFactoriesLoader.loadFactoryNames(type, classLoader)

The source code is as follows:

public static List<String> loadFactoryNames(Class<?> factoryClass, ClassLoader classLoader) {
    String factoryClassName = factoryClass.getName();

    try {
        Enumeration<URL> urls = classLoader != null ? classLoader.getResources("META-INF/spring.factories") : ClassLoader.getSystemResources("META-INF/spring.factories");
        ArrayList result = new ArrayList();

        while(urls.hasMoreElements()) {
            URL url = (URL)urls.nextElement();
            Properties properties = PropertiesLoaderUtils.loadProperties(new UrlResource(url));
            String factoryClassNames = properties.getProperty(factoryClassName);
            result.addAll(Arrays.asList(StringUtils.commaDelimitedListToStringArray(factoryClassNames)));
        }

        return result;
    } catch (IOException var8) {
        throw new IllegalArgumentException("Unable to load [" + factoryClass.getName() + "] factories from location [" + "META-INF/spring.factories" + "]", var8);
    }
}

This is the point;

META-INF/spring.factories

Scan the spring.factories file under META-INF under the jar package

Loop through the spring.factories file

1. Read the contents of spring.factories file

2. Convert the file to the Properties object (this object is almost the key value pair as we know MAP)

3. Get the name string of the object implementing the interface according to the class name of the interface

4. Separate strings by commas and convert to ArrayList

Start creating object instance:

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

The source code is as follows:

private <T> List<T> createSpringFactoriesInstances(Class<T> type,
      Class<?>[] parameterTypes, ClassLoader classLoader, Object[] args,
      Set<String> names) {
//1. Create a List collection according to the size of the object List implemented by the interface
   List<T> instances = new ArrayList<T>(names.size());
//2. Start recycling
   for (String name : names) {
      try {
/ / 3. Get the Class object of an implementation Class
         Class<?> instanceClass = ClassUtils.forName(name, classLoader);
         Assert.isAssignable(type, instanceClass);
         //4. Get response constructor according to Class object and parameter type
         Constructor<?> constructor = instanceClass
               .getDeclaredConstructor(parameterTypes);
/ / 5. Get object instance by reflection through constructor
         T instance = (T) BeanUtils.instantiateClass(constructor, args);
         instances.add(instance);
      }
      catch (Throwable ex) {
         throw new IllegalArgumentException(
               "Cannot instantiate " + type + " : " + name, ex);
      }
   }
   return instances;
}

The above process has many applications in Spring framework.

For example:

ApplicationContextInitializer.class
ApplicationListener.class 

All are instantiated by the above methods;

 

 

 

Published 62 original articles, won praise 9, visited 100000+
Private letter follow

Topics: Spring Java