Springboot 内部工具类 SpringFactoriesLoader 源码解析

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/andy_zhang2007/article/details/84025989

类名 : SpringFactoriesLoader
所在包 : org.springframework.core.io.support
官方文档

本文源代码基于 springboot 2.1.0,跟之前 springboot 1.5.x 版本相比,SpringFactoriesLoader实现已经有了变化,
比如在新的实现中已经加入了缓存机制。如果相应的文件需要被读取多次的话,第一次读取后会缓存起来,之后
的读取会使用缓存数据。

概述

该类并不对外暴露给应用开发者使用,而是spring框架自己使用的内部工具类,本身被声明为 abstract,不可以被实例化。

Springboot 应用启动的过程中,这个类的工作很重要, 启动逻辑使用该类从classpath上所有jar包中找出各自的 META-INF/spring.factories 属性文件,并分析出其中定义的工厂类。这些工厂类进而被启动逻辑使用,应用于进一步初始化工作。

公开成员介绍

  1. 类静态成员常量 final String FACTORIES_RESOURCE_LOCATION = "META-INF/spring.factories"

此常量定义了该工具类要从每个jar包中提取的工厂类定义属性文件的相对路径。

  1. 类静态方法 <T> List<T> loadFactories(Class<T> factoryClass, ClassLoader classLoader)

此方法会读取classpath上所有的jar包中的所有 META-INF/spring.factories属性文件,找出其中定义的匹配类型 factoryClass的工厂类,然后创建每个工厂类的对象/实例,并返回这些工厂类对象/实例的列表。
3. 类静态方法 List<String> loadFactoryNames(Class<?> factoryClass, ClassLoader classLoader)

此方法会读取classpath上所有的jar包中的所有META-INF/spring.factories属性文件,找出其中定义的匹配类型 factoryClass的工厂类,然后并返回这些工厂类的名字列表,注意是包含包名的全限定名。

源代码解析

package org.springframework.core.io.support;

import java.io.IOException;
import java.net.URL;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Enumeration;
import java.util.List;
import java.util.Map;
import java.util.Properties;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import org.springframework.core.annotation.AnnotationAwareOrderComparator;
import org.springframework.core.io.UrlResource;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;
import org.springframework.util.ConcurrentReferenceHashMap;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.util.ReflectionUtils;
import org.springframework.util.StringUtils;

/**
 * General purpose factory loading mechanism for internal use within the framework.
 * 仅限框架内部使用的工具类,通用目的的工厂加载机制。
 *
 * SpringFactoriesLoader#loadFactories loads and instantiates
 * factories of a given type from #FACTORIES_RESOURCE_LOCATION files which
 * may be present in multiple JAR files in the classpath. The spring.factories
 * file must be in 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. 
 * 
 * SpringFactoriesLoader#loadFactories设计用于加载和实例化指定类型的工厂,这些工厂类型的定义
 * 来自classpath中多个JAR包内常量FACTORIES_RESOURCE_LOCATION所指定的那些spring.factories文件。
 * spring.factories文件的格式必须是属性文件格式,每条属性的key必须是接口或者抽象类的全限定名,
 * 而属性值value是一个逗号分割的实现类的名称。
 * 
 * 举例来讲,一条属性定义如下:
 *
 * example.MyService=example.MyServiceImpl1,example.MyServiceImpl2
 *
 * 这里 example.MyService 是接口或者抽象类的全限定名, MyServiceImpl1和MyServiceImpl2是相应的
 * 两个实现类。
 *
 */
public final class SpringFactoriesLoader {

	/**
	 * The location to look for factories.
	 * Can be present in multiple JAR files.
	 * 在classpath中的多个JAR中要扫描的工厂配置文件的在本JAR包中的路径。
	 * 实际上,Springboot的每个 autoconfigure包都包含一个这样的文件。
	 */
	public static final String FACTORIES_RESOURCE_LOCATION = "META-INF/spring.factories";


	private static final Log logger = LogFactory.getLog(SpringFactoriesLoader.class);

	private static final Map<ClassLoader, MultiValueMap<String, String>> cache = 
				new ConcurrentReferenceHashMap<>();


	private SpringFactoriesLoader() {
	}


	/**
	 * Load and instantiate the factory implementations of the given type from
	 *  #FACTORIES_RESOURCE_LOCATION, using the given class loader.
	 * The returned factories are sorted through  AnnotationAwareOrderComparator.
	 * If a custom instantiation strategy is required, use  #loadFactoryNames
	 * to obtain all registered factory names.
	 * @param factoryClass the interface or abstract class representing the factory
	 * @param classLoader the ClassLoader to use for loading (can be  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
	 */
	public static <T> List<T> loadFactories(Class<T> factoryClass, @Nullable ClassLoader classLoader) {
		Assert.notNull(factoryClass, "'factoryClass' must not be null");
		ClassLoader classLoaderToUse = classLoader;
		if (classLoaderToUse == null) {
			classLoaderToUse = SpringFactoriesLoader.class.getClassLoader();
		}
		// 加载类型为factoryClass的工厂的名称,其实是一个个的全限定类名,使用指定的classloader:
		// classLoaderToUse
		List<String> factoryNames = loadFactoryNames(factoryClass, classLoaderToUse);
		if (logger.isTraceEnabled()) {
			logger.trace("Loaded [" + factoryClass.getName() + "] names: " + factoryNames);
		}
		List<T> result = new ArrayList<>(factoryNames.size());
		// 实例化所加载的每个工厂类
		for (String factoryName : factoryNames) {
			result.add(instantiateFactory(factoryName, factoryClass, classLoaderToUse));
		}
		// 排序
		AnnotationAwareOrderComparator.sort(result);
		return result;
	}

	/**
	 * Load the fully qualified class names of factory implementations of the
	 * given type from  #FACTORIES_RESOURCE_LOCATION, using the given
	 * class loader.
	 * @param factoryClass the interface or abstract class representing the factory
	 * @param classLoader the ClassLoader to use for loading resources; can be
	 *  null to use the default
	 * @throws IllegalArgumentException if an error occurs while loading factory names
	 * @see #loadFactories
	 */
	public static List<String> loadFactoryNames(Class<?> factoryClass, @Nullable ClassLoader classLoader) {
		String factoryClassName = factoryClass.getName();
		// 1. 使用指定的classloader扫描classpath上所有的JAR包中的文件META-INF/spring.factories,加载其中的多值
		// 工厂属性定义,使用多值Map的形式返回,
		// 2. 返回多值Map中key为factoryClassName的工厂名称列表,如果没有相应的entry,返回空列表而不是返回null
		return loadSpringFactories(classLoader).getOrDefault(factoryClassName, Collections.emptyList());
	}

	/**
	* 使用指定的classloader扫描classpath上所有的JAR包中的文件META-INF/spring.factories,加载其中的多值
	* 工厂属性定义,使用多值Map的形式返回
	**/
	private static Map<String, List<String>> loadSpringFactories(@Nullable ClassLoader classLoader) {
		MultiValueMap<String, String> result = cache.get(classLoader);
		if (result != null) {
			return result;
		}

		try {
			// 扫描classpath上所有JAR中的文件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()) {
				// 找到的每个META-INF/spring.factories文件都是一个Properties文件,将其内容
				// 加载到一个 Properties 对象然后处理其中的每个属性
				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();
					// 将逗号分割的属性值逐个取出,然后放到多值Map结果result中去。
					for (String factoryName : StringUtils.commaDelimitedListToStringArray(
						(String) entry.getValue())) {
						result.add(factoryClassName, factoryName.trim());
					}
				}
			}
			cache.put(classLoader, result);
			return result;
		}
		catch (IOException ex) {
			throw new IllegalArgumentException("Unable to load factories from location [" +
					FACTORIES_RESOURCE_LOCATION + "]", ex);
		}
	}

	/**
	* @param instanceClassName 工厂实现类全限定名称
	* @param factoryClass 工厂所属接口/抽象类全限定名称
	* @param classLoader 所要使用的类加载器
	**/
	@SuppressWarnings("unchecked")
	private static <T> T instantiateFactory(String instanceClassName, Class<T> factoryClass, 
						ClassLoader classLoader) {
		try {
			Class<?> instanceClass = ClassUtils.forName(instanceClassName, classLoader);
			if (!factoryClass.isAssignableFrom(instanceClass)) {
				throw new IllegalArgumentException(
						"Class [" + instanceClassName + "] is not assignable to [" 
							+ factoryClass.getName() + "]");
			}
			return (T) ReflectionUtils.accessibleConstructor(instanceClass).newInstance();
		}
		catch (Throwable ex) {
			throw new IllegalArgumentException("Unable to instantiate factory class: " 
				+ factoryClass.getName(), ex);
		}
	}

}

主要用途

Spring提供的一些JAR包,主要是springboot提供的一些JAR包里面会带有文件META-INF/spring.factories,然后在Springboot启动的时候,根据启动阶段不同的需求,框架就会多次调用SpringFactoriesLoader加载相应的工厂配置信息。
比如Springboot应用使用了注解@EnableAutoConfiguration时,就会触发对SpringFactoriesLoader.loadFactoryNames()的调用。具体请参考 :
Spring EnableAutoConfigurationImportSelector 是如何工作的 ?

还有其他一些应用点,比如 :

// SpringApplication.initialize 
// => SpringApplication.getSpringFactoriesInstances()
SpringFactoriesLoader.loadFactoryNames(org.springframework.context.ApplicationContextInitializer)
// SpringApplication.initialize 
// => SpringApplication.getSpringFactoriesInstances()
SpringFactoriesLoader.loadFactoryNames(org.springframework.context.ApplicationListenr)
// SpringApplication.run 
// => getRunListeners 
// => SpringApplication.getSpringFactoriesInstances()
SpringFactoriesLoader.loadFactoryNames(org.springframework.boot.SpringApplicationRunListener)
// SpringApplication.run 
// => prepareEnvironment 
// => SpringApplication.getSpringFactoriesInstances()
// => ConfigFileApplicationListener.onApplicationEnvironmentPreparedEvent() //事件处理
// => loadPostProcessors()
SpringFactoriesLoader.loadFactoryNames(org.springframework.boot.env.EnvironmentPostProcessor)

当然还有其他一些入口点,这里就不一一列举。

猜你喜欢

转载自blog.csdn.net/andy_zhang2007/article/details/84025989