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;