SpringFactoriesLoader.loadFactories()与SpringFactoriesLoader.loadFactoryNames() 详细源码解读

1. 前言

  1. SpringFactoriesLoader类的主要作用是通过类路径下的META-INF/spring.factories文件获取工厂类接口的实现类,初始化并保存在缓存中,以供Springboot启动过程中各个阶段的调用
  2. SpringFactoriesLoader.loadFactories():是根据参数factoryClass获取spring.factories下配置的所有实现类实例,返回List<T>的。
  3. SpringFactoriesLoader.loadFactoryNames():是根据参数factoryClass获取spring.factories下配置的所有实现类的全限定类名返回List<String>的。

2. 源码解读

1. SpringFactoriesLoader.loadFactoryNames()

	// 参数:
 	// Class<?> factoryType:需要被加载的工厂类的class
 	// ClassLoader classLoader:类加载器
	public static List<String> loadFactoryNames(Class<?> factoryType, @Nullable ClassLoader classLoader) {
		ClassLoader classLoaderToUse = classLoader;
		if (classLoaderToUse == null) {
			// 若没传入类加载器,使用该本类的类加载器
			classLoaderToUse = SpringFactoriesLoader.class.getClassLoader();
		}
		// class.getName():获取该类的全类限定名字
		String factoryTypeName = factoryType.getName();
		// loadSpringFactories(classLoaderToUse) 返回是Map
		// Map.getOrDefault(A,B): A为Key,从Map中获取Value,若Value为Null,则返回B 当作返回值
		return loadSpringFactories(classLoaderToUse).getOrDefault(factoryTypeName, Collections.emptyList());
	}
// 该方法是最核心部分:
// 1. 从项目中找到所有META-INF/spring.factories
// 2. 并解析所有spring.factories文件,生成 Map<一个接口类--->对应所有具体实现类集合>,即Map<key,List<String>>
private static Map<String, List<String>> loadSpringFactories(ClassLoader classLoader) {
		// Map<ClassLoader, Map<String, List<String>>> cache:
		// 该缓存存储的是:一个类加载器对应 加载spring.factories的结果集
		// 这里类加载器的知识往后我再补吧,望有好心人提醒我
		Map<String, List<String>> result = cache.get(classLoader);
		// 有则说明已经加载过了,直接返回
		// Map<String, List<String>> result:
		// key: 接口类的名字--->value: 具体实现类的名字
		if (result != null) {
			return result;
		}

		result = new HashMap<>();
		try {
			// public static final String FACTORIES_RESOURCE_LOCATION = "META-INF/spring.factories";
			// springboot项目部署之后如何读取到 Resource(ClassPath)下的资源,请看下面博客:
			// https://blog.csdn.net/xueyijin/article/details/121441738
			// Enumeration:可以认为是旧版的迭代器,目前被Iterator取代
			// 加载spring.factories资源并封装为URL对象
			Enumeration<URL> urls = classLoader.getResources(FACTORIES_RESOURCE_LOCATION);
			while (urls.hasMoreElements()) {
				URL url = urls.nextElement();
				// UrlResource 类是spring 对URL资源一种封装而已
				UrlResource resource = new UrlResource(url);
				// 解析properties文件,因为spring.factories 本身就是以key-->value来书写的
				Properties properties = PropertiesLoaderUtils.loadProperties(resource);
				for (Map.Entry<?, ?> entry : properties.entrySet()) {
					// key:接口全限定名字
					String factoryTypeName = ((String) entry.getKey()).trim();
					// 因为value:是可以写多个,并且要求之间 用,\ 来隔开
					// commaDelimitedListToStringArray方法就是把value的值,解析并封装为数组
					String[] factoryImplementationNames =
							StringUtils.commaDelimitedListToStringArray((String) entry.getValue());
					for (String factoryImplementationName : factoryImplementationNames) {
						// result 格式:Map<String, List<String>>
						// computeIfAbsent(key,function):
							// 若key 找不到则执行function方法得到value,并put(key,value)
							// 若key 找的到则直接返回get(key)的值,这里则是List<String>因此后面可以直接跟list.add(String)方法
						// Map中compute()、computeIfPresent()、computeIfAbsent()、merge()的使用以及原理,请看这篇博客:
						// https://blog.csdn.net/xueyijin/article/details/122851868
						result.computeIfAbsent(factoryTypeName, key -> new ArrayList<>())
								.add(factoryImplementationName.trim());
					}
				}
			}

			// Replace all lists with unmodifiable lists containing unique elements
			// replaceAll(BiFunction):将对map中的value值做一系列变化操作,比如原来key:5,value:5 可以改成key:5 value:5-5
			// 由BiFunction 实现最终value值的逻辑
			// Collections::unmodifiableList:是不可修改的List类,如果执行set、add等修改操作,直接报错,具体下文会说
			// 把spring.factories的key对应的value结果集去重,并且转化为不可变List类
			result.replaceAll((factoryType, implementations) -> implementations.stream().distinct()
					.collect(Collectors.collectingAndThen(Collectors.toList(), Collections::unmodifiableList)));
			// 放入缓存,防止多次执行上述操作,提高性能
			cache.put(classLoader, result);
		}
		catch (IOException ex) {
			throw new IllegalArgumentException("Unable to load factories from location [" +
					FACTORIES_RESOURCE_LOCATION + "]", ex);
		}
		return result;
	}

2. SpringFactoriesLoader.loadFactories()

	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);
		}
		// 因为loadFactories 是返回具体的实现类,又因为spring.factories的配置value可以多个并用,\区分
		// 因此 result的个数等于上面 获取到实现类全限定类名的个数
		List<T> result = new ArrayList<>(factoryImplementationNames.size());
		for (String factoryImplementationName : factoryImplementationNames) {
			// 重点:instantiateFactory: 将获取到的全限定类名 加载进jvm并生成对应的类对象
			// 第一时间反应,全限定类名变对象,肯定是反射了
			result.add(instantiateFactory(factoryImplementationName, factoryType, classLoaderToUse));
		}
		AnnotationAwareOrderComparator.sort(result);
		return result;
	}
	// 参数:
	// factoryImplementationName:实现类的全限定类名
	// factoryType:接口的类型
	// classLoader:类加载器
	private static <T> T instantiateFactory(String factoryImplementationName, Class<T> factoryType, ClassLoader classLoader) {
		try {
			// 这就是熟悉的加载字节码文件,只是我们学习的时候,是使用Class.forName()
			// spring将Class.forName()方法封装一下,细节先忽略
			Class<?> factoryImplementationClass = ClassUtils.forName(factoryImplementationName, classLoader);
			// A.isAssignableFrom(B): B是否是由A派生出来的,通俗一点,就是B是不是A的子类(儿子)
			// 这里比较严谨,判断factoryImplementationName(实现类)是不是factoryType(接口)的子类
			if (!factoryType.isAssignableFrom(factoryImplementationClass)) {
				throw new IllegalArgumentException(
						"Class [" + factoryImplementationName + "] is not assignable to factory type [" + factoryType.getName() + "]");
			}
			// newInstance():通过反射创建对象 并返回。
			return (T) ReflectionUtils.accessibleConstructor(factoryImplementationClass).newInstance();
		}
		catch (Throwable ex) {
			throw new IllegalArgumentException(
				"Unable to instantiate factory class [" + factoryImplementationName + "] for factory type [" + factoryType.getName() + "]",
				ex);
		}
	}
  • 6
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值