Deep learning of spring boot source code -- spring factorysloader

Keywords: Spring SpringBoot github Java

preface

  if you want to learn the source code in depth, you must first learn to understand its notes, after all, it is first-hand knowledge. We all know what an excellent framework spring boot is. It has brought great convenience to Java developers, and there is no need to integrate SSM, so we won't continue to praise spring boot here. I'm sure everyone will be curious about how the underlying source code of spring boot works? How is it amazing that we can quickly develop Java EE enterprise projects? How to quickly integrate the third-party framework? The next in-depth study of the spring boot source series, let me and you guys learn the spring boot underlying source code together.

Let's learn about springfactorysloader. This class plays a very important role in the whole underlying source code of SpringBoot. You can see how critical it is until you read the whole article.

text

1. Don't say much, read a wave of source code annotation

General purpose factory loading mechanism for internal use within the framework.

<p>{@code SpringFactoriesLoader} {@linkplain #loadFactories loads} and instantiates
 factories of a given type from {@value #FACTORIES_RESOURCE_LOCATION} files which
 may be present in multiple JAR files in the classpath. The {@code spring.factories}
 file must be in {@link Properties} format, where the key is the fully qualified
 name of the interface or abstract class, and the value is a comma-separated list of
 implementation class names. For example:
 

The translation is:

Spring factoriesloader is a general factory loading mechanism used within the spring framework.

Springfactorysloader loads and instantiates from facts through the loadfactors method_ RESOUCE_ The factory type given by the file in the location path,
These files may be included in the jar package of the classpath. These files are usually named spring.factories , and they are all in the form of properties property. The key in the file indicates
 Is the fully qualified name of the interface or abstract class, and the value is a comma separated list of implementation class names.

2. Source code

  first of all, you can see that SpringFactoriesLoader is a final class, the class decorated with final cannot be inherited, and the methods in the class cannot be overwritten, and the default is all the methods decorated with final. You can guess that at the beginning of the design, the SpringFactoriesLoader class did not want the initiator to inherit the class and extend the class. So, if you don't want others to inherit or extend your class in development, use final to decorate it~~

public final class SpringFactoriesLoader {
}

What are the member variables of the SpringFactoriesLoader class?

	/**
	 * Find the location of the factory
	 * Factories can be stored in multiple jar files
	 */
public static final String FACTORIES_RESOURCE_LOCATION = "META-INF/spring.factories";

Let's take a look at spring-boot:2.2.1.RELEASEjar Under the bag, spring.factories Content of the file.


stay spring.factories In the file, there are many factory classes, including property source loader, error reporter, container initializer, container listener, etc. These factory classes play a very important role in SpringBoot. Specific readers can go to see them by themselves.

// Customized cache for storage factory
private static final Map<ClassLoader, MultiValueMap<String, String>> cache = new ConcurrentReferenceHashMap<>();

  the cache here is implemented through the ConcurrentReferenceHashMap class. It's the first time for many readers to see this class The following is a brief introduction to the data structure of ConcurrentReferenceHashMap.

By reading the class annotation of ConcurrentReferenceHashMap, you can summarize the following points:

  1. It is a concurrent HashMap, and its key and value use ReferenceType.SOFT Or maybe ReferenceType.WEAK , that is, soft reference and weak reference.
  2. It can be used as Collections.synchronizedMap (New weakhashmap < K, reference > ()).
  3. It follows the same design constraints as ConcurrentHashMap, but it also supports null keys and null values.
  4. It uses the SoftReference soft reference by default.
  5. Using soft references means that the GC will recycle the objects pointed to by the soft references if the OOM is about to happen in the next GC. This feature is suitable for caching.

2.1 loadfactors method

  let's first look at the comments for the loadfactors method.

	/**
	 * Load and instantiate the factory implementations of the given type from
	 * {@value #FACTORIES_RESOURCE_LOCATION}, using the given class loader.
	 * <p>The returned factories are sorted through {@link AnnotationAwareOrderComparator}.
	 * <p>If a custom instantiation strategy is required, use {@link #loadFactoryNames}
	 * to obtain all registered factory names.
	 * @param factoryType the interface or abstract class representing the factory
	 * @param classLoader the ClassLoader to use for loading (can be {@code null} to use the default)
	 * @throws IllegalArgumentException if any factory implementation class cannot
	 * be loaded or if an error occurs while instantiating any factory
	 * @see #loadFactoryNames
	 */

The translation is:
  loadfactors method loads and instantiates facts through class loader_ RESOURCE_ The factory implementation defined in the location path file. Before returning to the factory, it will be sorted through the AnnotationAwareOrderComparator class. If you need to customize the instantiation policy, use loadFactoryNames to get all registered factory names.
  in the loadfactors method, enter the parameter factoryType to represent the interface or abstract class of the factory class; enter the parameter classLoader to represent the class loader of the loading factory. If it is empty, the default class loader will be used.

public static <T> List<T> loadFactories(Class<T> factoryType, @Nullable ClassLoader classLoader) {
		Assert.notNull(factoryType, "'factoryType' must not be null");
		// Classloader
		ClassLoader classLoaderToUse = classLoader;
		if (classLoaderToUse == null) {
			// If it is empty, the system default classloader will be used
			classLoaderToUse = SpringFactoriesLoader.class.getClassLoader();
		}
		// By getting the name collection of all factory implementation classes
		List<String> factoryImplementationNames = loadFactoryNames(factoryType, classLoaderToUse);
		if (logger.isTraceEnabled()) {
			logger.trace("Loaded [" + factoryType.getName() + "] names: " + factoryImplementationNames);
		}
		List<T> result = new ArrayList<>(factoryImplementationNames.size());
		for (String factoryImplementationName : factoryImplementationNames) {
			// Instance factory implementation class, and then add it to the result collection
			result.add(instantiateFactory(factoryImplementationName, factoryType, classLoaderToUse));
		}
		// Sort the factory names by annotationawareordercomparator ා sort method
		AnnotationAwareOrderComparator.sort(result);
		return result;
	}
	private static <T> T instantiateFactory(String factoryImplementationName, Class<T> factoryType, ClassLoader classLoader) {
		try {
			// Obtain the Class object of the factory implementation Class through the classUtils tool Class
			Class<?> factoryImplementationClass = ClassUtils.forName(factoryImplementationName, classLoader);
			if (!factoryType.isAssignableFrom(factoryImplementationClass)) {
				throw new IllegalArgumentException(
						"Class [" + factoryImplementationName + "] is not assignable to factory type [" + factoryType.getName() + "]");
			}
			// Create factory class instance object with reflection tool
			return (T) ReflectionUtils.accessibleConstructor(factoryImplementationClass).newInstance();
		}
		catch (Throwable ex) {
			throw new IllegalArgumentException(
				"Unable to instantiate factory class [" + factoryImplementationName + "] for factory type [" + factoryType.getName() + "]",
				ex);
		}
	}

  it can be seen that the loadfactors method logic is relatively simple and its function is relatively clear, namely:
① Use classLoader to load the factory to get its corresponding class name;
② The chemical plant class is exemplified by the instantiateFactory method;
③ The factory is sorted by annotationawareorder comparator ා sort method;

See where the loadfactors method is used:

The ConfigFileApplicationListener class in the above figure is a listener class used to load application configuration files. For the loading of application configuration files, it will be explained in detail later~~

2.2 loadFactoryNames method

  since the comments of the loadFactoryNames method are the same as the loadfactors content, they will not be written here.

	public static List<String> loadFactoryNames(Class<?> factoryType, @Nullable ClassLoader classLoader) {
		// Get factoryType factory type
		String factoryTypeName = factoryType.getName();
		// Load springfactors, if not, return an empty collection
		return loadSpringFactories(classLoader).getOrDefault(factoryTypeName, Collections.emptyList());
	}
	private static Map<String, List<String>> loadSpringFactories(@Nullable ClassLoader classLoader) {
		// Get the loaded spring factors from the cache
		MultiValueMap<String, String> result = cache.get(classLoader);
		if (result != null) {
			return result;
		}

		try {
			Enumeration<URL> urls = (classLoader != null ?
					// Read the spring.factories The file is then encapsulated as a URL and stored in Enumeration
					classLoader.getResources(FACTORIES_RESOURCE_LOCATION) :
					ClassLoader.getSystemResources(FACTORIES_RESOURCE_LOCATION));
			result = new LinkedMultiValueMap<>();
			// Traverse urls, and then encapsulate urls as url resource objects
			while (urls.hasMoreElements()) {
				URL url = urls.nextElement();
				UrlResource resource = new UrlResource(url);
				/**
				 *  Load through PropertiesLoaderUtils property loader spring.factories value in.
				 *  Here, Properties inherit a property of HashTable. key and value correspond to each other spring.factories key and value in the file.
				 *  In properties loader utils, the underlying layer is the file data read through the IO stream, which will not be discussed in detail here.
				 */ 
				Properties properties = PropertiesLoaderUtils.loadProperties(resource);
				for (Map.Entry<?, ?> entry : properties.entrySet()) {
					String factoryTypeName = ((String) entry.getKey()).trim();
					// Traverse to get the factory implementation class name
					for (String factoryImplementationName : StringUtils.commaDelimitedListToStringArray((String) entry.getValue())) {
						result.add(factoryTypeName, factoryImplementationName.trim());
					}
				}
			}
			// Cache the obtained results
			cache.put(classLoader, result);
			return result;
		}
		catch (IOException ex) {
			throw new IllegalArgumentException("Unable to load factories from location [" +
					FACTORIES_RESOURCE_LOCATION + "]", ex);
		}
	}

Through the IDEA, we can see where the loadFactoryName method is used:

You can see where the loadFactoryNames method is used in the spring application in the above picture,

When you enter the constructor of spring application, you will call the setInitializers method, which is used to set the initializer. The initializer is also a very important knowledge point, which will be described in detail later.


As you can see, in the getSpringFactoriesInstance method in spring application, spring factoriesloader ා loadfactynames is called. After the springfactorysloader ා loadfactorynames method is called, it is obtained spring.factories The value value in the Set is stored in the Set collection, and then the createSpringFactoriesInstances method is called to instantiate the factory class stored in the Set collection through the reflection tool, which is sorted and then returned to the previous layer for calling.

Next, a simple book sequence diagram is used to describe the calling process of spring factors loader in spring boot.

3. Summary

After learning the source code of springfactorysloader, you can really step into the door of learning the source code of SpringBoot. Through springfactorysloader, SpringBoot can load the jar package in the classpath spring.factories The file can read all kinds of factory classes in the file, including listener, initializer, property source loader, etc., through which the S Pringboot has become so powerful!!! In the next few articles, the spring boot source code will deeply analyze the listener, initializer, property source, configuration loading and other mechanisms~~

Think the author wrote a good point like, pay attention to the author.
Github https://github.com/coderbruis/JavaSourceLearning Included, more source articles and source code can be learned in GitHub.

Posted by TheVoice on Thu, 04 Jun 2020 23:31:33 -0700