SpringFactoriesLoader源码分析


前言

本文主要记录 SpringFactoriesLoader 里的一些方法的源码,供自己学习记录。


一、SpringFactoriesLoader是什么?

SpringFactoriesLoader 工厂加载机制是 Spring 内部提供的一个加载方式,只需要在模块的 META-INF/spring.factories 文件中,以 Properties 类型(即 key-value 形式)配置,就可以将相应的实现类注入 Spirng 容器中。示例如下:

# 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

# Error Reporters
org.springframework.boot.SpringBootExceptionReporter=\
org.springframework.boot.diagnostics.FailureAnalyzers

# Application Context Initializers
org.springframework.context.ApplicationContextInitializer=\
org.springframework.boot.context.ConfigurationWarningsApplicationContextInitializer,\
org.springframework.boot.context.ContextIdApplicationContextInitializer,\
org.springframework.boot.context.config.DelegatingApplicationContextInitializer,\
org.springframework.boot.web.context.ServerPortInfoApplicationContextInitializer

这里需要注意的是 SpringFactoriesLoaderfinal 类型,里面对外提供的方法为静态方法,可以直接使用。

二、方法解析

1.loadFactories

加载我们指定的类下需要导入的类,实例化返回。

public static <T> List<T> loadFactories(Class<T> factoryClass, @Nullable ClassLoader classLoader) {
	// factoryClass 也就是我们所说的 key
	Assert.notNull(factoryClass, "'factoryClass' must not be null");
	// 获取类加载器
	ClassLoader classLoaderToUse = classLoader;
	if (classLoaderToUse == null) {
		classLoaderToUse = SpringFactoriesLoader.class.getClassLoader();
	}
	// 获取需要加载的类的名称
	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));
	}
	// 按照 order 进行排序
	AnnotationAwareOrderComparator.sort(result);
	return result;
}

2.loadFactoryNames

获取需要加载类的全限定名,并返回。

public static List<String> loadFactoryNames(Class<?> factoryClass, @Nullable ClassLoader classLoader) {
	// 获取 key 的值
	String factoryClassName = factoryClass.getName();
	// loadSpringFactories 获取 classLoader 加载器能够加载的所有类,这里是返回的 map
	// 如果说有我们指定的 key 则返回对应结果,如果不包含则返回 空集合
	return loadSpringFactories(classLoader).getOrDefault(factoryClassName, Collections.emptyList());
}

private static Map<String, List<String>> loadSpringFactories(@Nullable ClassLoader classLoader) {
	// Map<ClassLoader, MultiValueMap<String, String>> cache = new ConcurrentReferenceHashMap<>()
	// cache 用于缓存一次扫描的结果
	MultiValueMap<String, String> result = cache.get(classLoader);
	if (result != null) {
		return result;
	}

	try {
		// 这个过程就是 寻找我们项目里所有 jar 包下的 META-INF/spring.factories 文件
		// 将其进行解析,并放置到缓存中
		// 这样做的好处在于,一次load获取所有结果,而加载的调用过程是分布进行的
		// 加载一次之后,就可以通过缓存获取,非常方便
		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 factoryClassName = ((String) entry.getKey()).trim();
				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);
	}
}

3.instantiateFactory

instantiateFactory 就是实例化的过程。

private static <T> T instantiateFactory(String instanceClassName, Class<T> factoryClass, ClassLoader classLoader) {
	try {
		// 通过反射机制实现动态加载
		Class<?> instanceClass = ClassUtils.forName(instanceClassName, classLoader);
		// 判断 要实例化的对象不是继承于 factoryClass 也就是 key 
		// eg: SpringApplicationRunListener 要加载 EventPublishingRunListener
		//     这里 EventPublishingRunListener 是继承 SpringApplicationRunListener 
		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);
	}
}

总结

SpringFactoriesLoader 在SpringBoot项目启动时使用很多,可以结合分析 SpringApplicationrun 方法一起分析。有兴趣的话也可以同步分析一下读取 META-INF/spring.factories 文件的过程~

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值