【springboot源码解读系列】(五、springboot启动之解析META-INF/spring.factories的SpringFactoriesLoader详细解读)

时间:2020-7-29 作者:admin


上一讲【springboot源码解读系列】(四、springboot启动之SpringApplicationRunListeners:通知所有监听器,系统启动中…用于非常早的初始化)说到通过SpringFactoriesLoader加载META-INF/spring.factories获取我们的自定义监听器和启动器。

那么这讲就来揭开SpringFactoriesLoader神秘的面纱:讲解都在注释里面:

值得学习的是他的这种设计理念,通过指定规范,然后对其使用者提供扩展,springboot的启动器原理也是这样来进行实现的。

精华提取:ConcurrentMap(线程安全的map集合)作为缓存cache、通过构造器去创建实例(accessibleConstructor)、isAssignableFrom判断是否是其子类(isAssignableFrom和instanceof关键字的区别)

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;

/**
 * 通用工厂加载机制实现的内部使用框架。spring内部使用的
 *
 * springfactoresloader会加载META-INF/spring.factories文件中给定类型的工厂,
 * 为什么是工厂,因为一个key可以对应多个value,
 * spring.factories的文件类型是properties格式的,其中键是完全限定的接口或抽象类的名称,值是以逗号分隔的
 *
 * 比如:
 * <pre class="code">example.MyService=example.MyServiceImpl1,example.MyServiceImpl2</pre>
 *
 */
public final class SpringFactoriesLoader {

	/**
	 * 本地的工厂文件,
	 * 可以存在于多个jar中,也就是他会扫码所有的jar,然后解析所有的META-INF/spring.factories文件,
	 * 并将其配置的创建其工厂,以及对所有value进行实例化
	 */
	public static final String FACTORIES_RESOURCE_LOCATION = "META-INF/spring.factories";


	// 日志框架
	private static final Log logger = LogFactory.getLog(SpringFactoriesLoader.class);

	// 定义一个map,其中key为ClassLoader,value为MultiValueMap
	// MultiValueMap集成自Map
	// ConcurrentReferenceHashMap集成自ConcurrentMap,也就是一个线程安全的map
	private static final Map<ClassLoader, MultiValueMap<String, String>> cache = new ConcurrentReferenceHashMap<>();


	// 空参构造函数
	private SpringFactoriesLoader() {
	}


	/**
	 * 通过classLoader从各个jar包的classpath下面的META-INF/spring.factories加载并解析其key-value值,然后创建其给定类型的工厂实现
	 *
	 * 返回的工厂通过AnnotationAwareOrderComparator进行排序过的。
	 * AnnotationAwareOrderComparator就是通过@Order注解上面的值进行排序的,值越高,则排的越靠后
	 *
	 * 如果需要自定义实例化策略,请使用loadFactoryNames方法获取所有注册工厂名称。
	 *
	 * @param  factoryType 接口或者抽象类的Class对象
	 * @param  classLoader 用于加载的类加载器(可以是null,如果是null,则使用默认值)
	 * @throws IllegalArgumentException 如果无法加载任何工厂实现类,或者在实例化任何工厂时发生错误,则会抛出IllegalArgumentException异常
	 */
	public static <T> List<T> loadFactories(Class<T> factoryType, @Nullable ClassLoader classLoader) {
		// 首先断言,传入的接口或者抽象类的Class对象不能为空
		Assert.notNull(factoryType, "'factoryType' must not be null");
		// 将传入的classLoader赋值给classLoaderToUse
		// 判断classLoaderToUse是否为空,如果为空,则使用默认的SpringFactoriesLoader的classLoader
		ClassLoader classLoaderToUse = classLoader;
		if (classLoaderToUse == null) {
			classLoaderToUse = SpringFactoriesLoader.class.getClassLoader();
		}
		// 加载所有的META-INF/spring.factories并解析,获取其配置的factoryNames
		List<String> factoryImplementationNames = loadFactoryNames(factoryType, classLoaderToUse);
		if (logger.isTraceEnabled()) {
			logger.trace("Loaded [" + factoryType.getName() + "] names: " + factoryImplementationNames);
		}

		List<T> result = new ArrayList<>(factoryImplementationNames.size());
		// 通过反射对所有的配置进行实例化。
		for (String factoryImplementationName : factoryImplementationNames) {
			result.add(instantiateFactory(factoryImplementationName, factoryType, classLoaderToUse));
		}
		AnnotationAwareOrderComparator.sort(result);
		return result;
	}

	/**
	 * 使用给定的类加载器从META-INF/spring.factories加载给定类型的工厂实现的完全限定类名。
	 * @param factoryType 接口或者抽象类的Class对象
	 *
	 * @param classLoader classLoader 用于加载的类加载器(可以是null,如果是null,则使用默认值)
	 *
	 * @throws IllegalArgumentException 如果在加载工厂名称时发生错误
	 * @see #loadFactories
	 */
	public static List<String> loadFactoryNames(Class<?> factoryType, @Nullable ClassLoader classLoader) {
		// 通过Class对象获取全限定类名
		String factoryTypeName = factoryType.getName();
		// loadSpringFactories方法是获取所有的springFactories
		// getOrDefault是通过key,获取到对应的类的集合。因为value是通过逗号相隔的,可以有多个,所以是list
		// getOrDefault如果存在就返回,如果不存在那么就返回给的默认值
		return loadSpringFactories(classLoader).getOrDefault(factoryTypeName, Collections.emptyList());
	}


	// 加载所有的springFactories
	private static Map<String, List<String>> loadSpringFactories(@Nullable ClassLoader classLoader) {
		// 首先从cache中获取,根据classLoader,
		// cache是以ClassLoader作为key的。是静态的final修饰的。整个应用只有一份
		MultiValueMap<String, String> result = cache.get(classLoader);
		// 如果为null,证明还没有加载过,如果不为空,那么就添加。
		if (result != null) {
			return result;
		}

		try {
			// 三目表达式,判断参数classLoader是否为空,如果不为空,那么直接使用传入的classLoader获取META-INF/spring.factories
			// 如果为空,那么就使用系统的classLoader来获取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
				URL url = urls.nextElement();
				UrlResource resource = new UrlResource(url);
				// 解析properties
				Properties properties = PropertiesLoaderUtils.loadProperties(resource);
				// 将所有的key放入result
				for (Map.Entry<?, ?> entry : properties.entrySet()) {
					String factoryTypeName = ((String) entry.getKey()).trim();
					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);
		}
	}

	// 实例化工厂,根据
	@SuppressWarnings("unchecked")
	private static <T> T instantiateFactory(String factoryImplementationName, Class<T> factoryType, ClassLoader classLoader) {
		try {
			// 根据全限定类名通过反射获取Class对象
			Class<?> factoryImplementationClass = ClassUtils.forName(factoryImplementationName, classLoader);
			// 判断获取的Class对象是否从factoryType里面来,
			// 说具体点就是判断我们配置的spring.factories中的权限定类名所对应的类是否是对应的子类或者实现类
			// 比如
			// org.springframework.context.ApplicationContextInitializer=\
			// org.springframework.boot.context.ConfigurationWarningsApplicationContextInitializer,\
			// org.springframework.boot.context.ContextIdApplicationContextInitializer,
			// 那么他就会验证ConfigurationWarningsApplicationContextInitializer和ContextIdApplicationContextInitializer是否是ApplicationContextInitializer的子类
//			isAssignableFrom()方法与instanceof关键字的区别总结为以下两个点:
//			isAssignableFrom()方法是从类继承的角度去判断,instanceof关键字是从实例继承的角度去判断。
//			isAssignableFrom()方法是判断是否为某个类的父类,instanceof关键字是判断是否某个类的子类。
			// 如果不是,则会保存
			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);
		}
	}

}
声明:本文内容由互联网用户自发贡献自行上传,本网站不拥有所有权,未作人工编辑处理,也不承担相关法律责任。如果您发现有涉嫌版权的内容,欢迎进行举报,并提供相关证据,工作人员会在5个工作日内联系你,一经查实,本站将立刻删除涉嫌侵权内容。