Spring Factories机制

Spring Factories机制简述

Spring Factories机制和Java SPI的扩展机制类似,Spring Boot采用了spring.factories的扩展机制,在很多spring的starter 包中都可以看到,通过读取 META-INF/spring.factories文件中的配置指定自动配置类入口,然后在程序中读取这些配置文件并实例化,从而让框架加载该类实现jar的动态加载。比如我们自定义的一些Spring Boot Starter公共组件就可以使用Spring Factories机制,通过极简的配置就可以在需要使用组件的地方引入依赖直接使用。

什么是SPI机制

SPI ,全称为 Service Provider Interface,是一种服务发现机制。它通过在ClassPath路径下的META-INF/services文件夹查找文件,自动加载文件里所定义的类。

这一机制为很多框架扩展提供了可能,比如在Dubbo、JDBC中都使用到了SPI机制。

Spring Factories实现原理

spring-core包(博主的是版本是5.2.8.RELEASE)里定义了SpringFactoriesLoader类,这个类实现了检索META-INF/spring.factories文件,并获取指定接口的配置的功能。

以下为SpringFactoriesLoader类的注释说明:

General purpose factory loading mechanism for internal use within the framework
即该类是为框架内部使用工厂加载机制服务的。

SpringFactoriesLoader loads and instantiates factories of a given type from FACTORIES_RESOURCE_LOCATION files which may be present in multiple JAR files in the classpath.
SpringFactoriesLoader类通过读取spring.factories文档(该文档会存在多个classpath的多个jar包中),对给定的type及factoryClass加载和实例化其工厂类。

The spring.factories file must be in java.util.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.
spring.factories格式:key为接口或抽象类全称,value为具体实现类全称的列表

举例如下:

# PropertySource Loaders
org.springframework.boot.env.PropertySourceLoader=\
org.springframework.boot.env.PropertiesPropertySourceLoader,\
org.springframework.boot.env.YamlPropertySourceLoader

# Run Listeners
org.springframework.boot.SpringApplicationRunListener=\
org.springframework.boot.context.event.EventPublishingRunListener

通过FACTORIES_RESOURCE_LOCATION指定扫描的配置文件路径:

/**
 * The location to look for factories.
 * <p>Can be present in multiple JAR files.
 */
public static final String FACTORIES_RESOURCE_LOCATION = "META-INF/spring.factories";

类中定义了两个公共的方法:

  • loadFactories 根据接口类获取其实现类的实例,这个方法返回的是对象列表。
  • loadFactoryNames 根据接口获取其接口类的名称,这个方法返回的是类名的列表。

其中还包含了私有方法:

  • instantiateFactory 根据类名创建实例对象。
  • loadSpringFactories 通过扫描配置文件获取接口名称和接口实现类名称列表。

loadFactories

loadFactories方法首先调用loadFactoryNames方法获取待实例化的具体实现类的全称,然后调用instantiateFactory方法实例化每一个具体实现类,最终返回一个具体实现类的实例列表。

public static <T> List<T> loadFactories(Class<T> factoryType, @Nullable ClassLoader classLoader) {
	Assert.notNull(factoryType, "'factoryType' must not be null");
	ClassLoader classLoaderToUse = classLoader;
	if (classLoaderToUse == null) {
		classLoaderToUse = SpringFactoriesLoader.class.getClassLoader();
	}
	//调用loadFactoryNames获取接口的实现类
	List<String> factoryImplementationNames = loadFactoryNames(factoryType, classLoaderToUse);
	if (logger.isTraceEnabled()) {
		logger.trace("Loaded [" + factoryType.getName() + "] names: " + factoryImplementationNames);
	}
	//遍历 factoryNames 数组,创建实现类的对象
	List<T> result = new ArrayList<>(factoryImplementationNames.size());
	for (String factoryImplementationName : factoryImplementationNames) {
		//调用instantiateFactory根据类创建实例对象
		result.add(instantiateFactory(factoryImplementationName, factoryType, classLoaderToUse));
	}
	//排序
	AnnotationAwareOrderComparator.sort(result);
	return result;
}

instantiateFactory

根据实现类名实例化每一个具体实现类返回

@SuppressWarnings("unchecked")
private static <T> T instantiateFactory(String factoryImplementationName, Class<T> factoryType, ClassLoader classLoader) {
	try {
		Class<?> factoryImplementationClass = ClassUtils.forName(factoryImplementationName, classLoader);
		if (!factoryType.isAssignableFrom(factoryImplementationClass)) {
			throw new IllegalArgumentException(
					"Class [" + factoryImplementationName + "] is not assignable to factory type [" + factoryType.getName() + "]");
		}
		return (T) ReflectionUtils.accessibleConstructor(factoryImplementationClass).newInstance();
	}
	catch (Throwable ex) {
		throw new IllegalArgumentException(
			"Unable to instantiate factory class [" + factoryImplementationName + "] for factory type [" + factoryType.getName() + "]",
			ex);
	}
}

loadFactoryNames

在这个方法中会遍历整个ClassLoader中所有jar包下的spring.factories文件。通过Properties解析所有接口的实现类名称。

public static List<String> loadFactoryNames(Class<?> factoryType, @Nullable ClassLoader classLoader) {
	String factoryTypeName = factoryType.getName();
	return loadSpringFactories(classLoader).getOrDefault(factoryTypeName, Collections.emptyList());
}

loadSpringFactories

private static Map<String, List<String>> loadSpringFactories(@Nullable ClassLoader classLoader) {
	MultiValueMap<String, String> result = cache.get(classLoader);
	if (result != null) {
		return result;
	}

	try {
		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();
				//StringUtils.commaDelimitedListToStringArray:将配置文件中的value(接口实现类名称)通过","分隔成String数组
				for (String factoryImplementationName : StringUtils.commaDelimitedListToStringArray((String) entry.getValue())) {
					result.add(factoryTypeName, factoryImplementationName.trim());
				}
			}
		}
		cache.put(classLoader, result);
		return result;
	}
	catch (IOException ex) {
		throw new IllegalArgumentException("Unable to load factories from location [" +
				FACTORIES_RESOURCE_LOCATION + "]", ex);
	}
}
posted @ 2020-12-23 14:17  curtin  阅读(745)  评论(0编辑  收藏  举报