General documentation: Article directory Github: github.com/black-ant

A. The preface

This article will go through the process of automatically configuring SpringBoot, which involves loading the Factories.

The Factories function as metadata for the factory class, and Spring reflects the classes that need to be initialized using the.factories file

Ii. Core categories

The core class in the Factories process is SpringFactoriesLoader, which loads the properties associated with the Factories when SpringApplication Run

  • Spring.factories configuration file: Spring’s own set of SPI configuration files
  • SpringFactoriesLoader class for loading the spring.factories configuration file

First take a look at the main SpringFactoriesLoader process

SpringFactoriesLoader SF- FACTORIES_RESOURCE_LOCATION : SF- Map<ClassLoader,MultivalueMap> cache M- loadFactoryNames(Class<? > factoryClass,@NullableThis this) | - get the name of the class of the interface - > factoryClass. The getName () | - loaded FACTORIES_RESOURCE_LOCATION configuration file, For the interface is the realization of the corresponding class name | - > loadSpringFactories M - loadSpringFactories (@NullableThis this) | - cache exists, then returned directly to win the FACTORIES_RESOURCE_LOCATION corresponding URL '| | - - create LinkedMultiValueMap < String, String = > object result | | - traverse URL array - get URL = URL | - create UrlResource object (URL) | - load"META-INF/spring.factories"Configuration file, Become a Properties object | - traverse the Properties object | | - use commas - added to the result | - added to cache the M - loadFactories (Class < T > factoryClass,@NullableClassLoader classLoader) ? - get the name of the implementation class corresponding to the interface, Then create the corresponding object | - get this - > SpringFactoriesLoader. Class. GetClassLoader () | - get interface corresponding to the implementation class name - > loadFactoryNames | - traversal FactoryNames array, create object that implements class ->for+ result. The add | - sort - > AnnotationAwareOrderComparator. Sort (result); M - instantiateFactory | - get Class Class | - determine whether | - creating objects at the specified interface is realizedCopy the code

3. The process

3.1 starting point

The starting point for loading the factories is in the SpringApplication.

public SpringApplication(ResourceLoader resourceLoader, Class
       ... primarySources) {
    this.resourceLoader = resourceLoader;
    this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
    this.webApplicationType = WebApplicationType.deduceFromClasspath();
    //获取  ApplicationContextInitializer 的 Factories
    setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
    // Get the Factories of the ApplicationListener
    setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
    this.mainApplicationClass = deduceMainApplicationClass();
}

Copy the code

3.2 Loading the Factories of Application

C1- SpringApplication M1_01- getSpringFactoriesInstances(Class<T> type, Class<? >[] parameterTypes, Object... args) ? - Get an instance of SpringFactories - Get a ClassLoader, Here is normal AppClassLoader - create instance through SpringFactoriesLoader loading FactoryNames - createSpringFactoriesInstances - > M1_02 - return after sorting M1_02 - createSpringFactoriesInstances - ClassUtils. Class.forname reflection classes, BeanUtils. InstantiateClass create instances of C2 - SpringFactoriesLoader F01 - Map < this, MultiValueMap < String, String>> cache M2_01- loadFactoryNames - factoryClass.getName() : FactoryClassName -> PS_M2_01_1 - loadSpringFactories(classLoader).getorDefault (factoryClassName, collections.emptyList ())? - Load the FACTORIES_RESOURCE_LOCATION configuration file to get the names of the implementation classes corresponding to the interface M2_02- loadSpringFactories(@NullableClassLoader classLoader) ? - Load the FACTORIES_RESOURCE_LOCATION configuration file, Get the name of the MultiValueMap from cache (F01) and return it directly. If a ClassLoader exists, Classloader.getresources (FACTORIES_RESOURCE_LOCATION) - If the classLoader does not exist, ClassLoader.getSystemResources(FACTORIES_RESOURCE_LOCATION) ? -ps_m2_02_1, get file resource iterator - iterated load, first get UrlResource, then get Properties FOR- iterated Properties, add to Map, put in cache? - PS_M2_02_2 Adds details/ / PS_M2_01_1: this parameter is org. Springframework. Context. ApplicationContextInitializer
// PS_M2_02_1 : location 
    - FACTORIES_RESOURCE_LOCATION = "META-INF/spring.factories"- location is in more than one package, each package can have spring.factories, and they iterate over// M2_02 pseudo-code
Map<String, List<String>> loadSpringFactories(@Nullable ClassLoader classLoader){
    MultiValueMap<String, String> result = cache.get(classLoader);
    if(result ! =null) {
        returnresult; } 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 factoryTypeName = ((String) entry.getKey()).trim();for (String factoryImplementationName : StringUtils.commaDelimitedListToStringArray((String) entry.getValue())) {
                result.add(factoryTypeName, factoryImplementationName.trim());
            }
        }
    }
    cache.put(classLoader, result);
    return result;
}          

Copy the code

PS_M2_02_2 details:

result.add(factoryTypeName, factoryImplementationName.trim())

Spring.factories corresponds to the structure

org.springframework.context.ApplicationListener=\
org.springframework.boot.ClearCachesApplicationListener,\
org.springframework.boot.builder.ParentContextCloserApplicationListener,\
org.springframework.boot.cloud.CloudFoundryVcapEnvironmentPostProcessor,\
org.springframework.boot.context.FileEncodingApplicationListener,\
org.springframework.boot.context.config.AnsiOutputApplicationListener,\
org.springframework.boot.context.config.ConfigFileApplicationListener,\
org.springframework.boot.context.config.DelegatingApplicationListener,\
org.springframework.boot.context.logging.ClasspathLoggingApplicationListener,\
org.springframework.boot.context.logging.LoggingApplicationListener,\
org.springframework.boot.liquibase.LiquibaseServiceLocatorApplicationListener
Copy the code

As you can see, factoryTypeName is the first node, with the corresponding implementation underneath

The difference between getResources and getSystemResources

3.3 Environment Process Loads loadFactories

Starting point of loading

The starting point for loading the Environment is the SpringApplication, which is loaded mainly from the SpringListener

C- SpringApplication
    M- run(String... args)
        - prepareEnvironment(listeners, applicationArguments)

/ / called again: the listener. EnvironmentPrepared (environment)C3 - ConfigFileApplicationListener M3_01 - loadPostProcessors () : load// M3_01 pseudo-code
return SpringFactoriesLoader.loadFactories(EnvironmentPostProcessor.class, getClass().getClassLoader());



        
Copy the code

Loading process

The main reason for the interception is that the method called here is different from the last one

In addition to fetching the above collection, loadFactories will load the instance via instantiateFactory, which loads and instantiates the factory implementation of the given type from “meta-INF /spring”.


C2- SpringFactoriesLoader
    M2_03- loadFactories


Copy the code
P-factories_resource_location: -> Static property, which defines which to read"META-INF/spring.factories"Configuration file p-map <ClassLoader, MultiValueMap<String, String>> Cache -> read"META-INF/spring.factories"Buffer M - loadFactories: the configuration file of the interface, the realization of the corresponding class name and then create the corresponding subjects - > classLoaderToUse = SpringFactoriesLoader. Class. GetClassLoader (); -> loadFactoryNames(factoryClass, classLoaderToUse); ? Get the name of the implementation class corresponding to the interface ->newArrayList<>(factoryNames.size()); InstantiateFactory (factoryName, factoryClass, classLoaderToUse)); -> AnnotationAwareOrderComparator.sort(result); -- order m-instantiateFactory -> classutils. forName(instanceClassName, classLoader); ? - get Class ->if(! factoryClass.isAssignableFrom(instanceClass)) { ? - determine whether the specified interface is implemented ->throw newIllegalArgumentException -> ReflectionUtils.accessibleConstructor(instanceClass).newInstance(); -> Create objectCopy the code

Other locations where the main use of factories is


// Load the Factories of EnvironmentPostProcessor
C- ConfigFileApplicationListener
    M- loadPostProcessors
        - SpringFactoriesLoader.loadFactories(EnvironmentPostProcessor.class, getClass().getClassLoader())
        
C- ConfigFileApplicationListener
    - SpringFactoriesLoader.loadFactories(PropertySourceLoader.class,getClass().getClassLoader());
   
   
C- AutoConfigurationImportSelector
    - SpringFactoriesLoader.loadFactories(AutoConfigurationImportListener.class, this.beanClassLoader);                                
        

Copy the code

Appendix: comparison of 2

PS: looked like there is no difference between, way is to create the most ApplicationContextInitializer and ApplicationListener object, which is set to the SpringApplication

3.4 Scanning the Factories file

The core logic is in the loadSpringFactories method


C2- SpringFactoriesLoader
    M2_02- loadSpringFactories(@Nullable ClassLoader classLoader)
        - PropertiesLoaderUtils.loadProperties
        
C- PropertiesLoaderUtils
    M- loadProperties : 调用 fillProperties    
    M- fillProperties : 后面就是读成一个 inputStream 了 , 没有看的价值
         - String key = loadConvert(lr.lineBuf, 0, keyLen, convtBuf);
         - String value = loadConvert(lr.lineBuf, valueStart, limit - valueStart, convtBuf);
         - put(key, value);
         
// PS : Properties extends Hashtable<Object,Object> 
         
Copy the code

3.5 Usage Skills

TODO: I don’t want to write today

4. Summarize

Basis: SPI mechanism + Properties scanning process: start scanning ApplicationContextInitializer + ApplicationListener, runtime scan other related Factories, and instantiation

Conclusion: the “Factories” are simpler, and I don’t understand why there are two different loading paths, with the underlying constructor + instance implementing an object. Guess what is possible and optimize the loading order

PS: Have a clear welcome to let me know in the comments thanks ❤❤❤