IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> Java知识库 -> SpringFactoriesLoader.loadFactories()与SpringFactoriesLoader.loadFactoryNames() 详细源码解读 -> 正文阅读

[Java知识库]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);
		}
	}
  Java知识库 最新文章
计算距离春节还有多长时间
系统开发系列 之WebService(spring框架+ma
springBoot+Cache(自定义有效时间配置)
SpringBoot整合mybatis实现增删改查、分页查
spring教程
SpringBoot+Vue实现美食交流网站的设计与实
虚拟机内存结构以及虚拟机中销毁和新建对象
SpringMVC---原理
小李同学: Java如何按多个字段分组
打印票据--java
上一篇文章      下一篇文章      查看所有文章
加:2022-03-03 15:57:54  更:2022-03-03 16:02:42 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/24 11:51:43-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码