springboot SPI 实现自动配置

1. Java类加载器资源查找的过程

ClassLoader 类中资源查找有2种

  • 一种是 classLoader 对象的实例方法,在 classsLoader 对象的目录范围内查找
    // ClassLoader.java
    
    public Enumeration<URL> getResources(String name) throws IOException {
        @SuppressWarnings("unchecked")
        Enumeration<URL>[] tmp = (Enumeration<URL>[]) new Enumeration<?>[2];
        if (parent != null) {
            tmp[0] = parent.getResources(name);
        } else {
            // parent == null, 表示已经找到最顶级 BootStrapClassLoader 加载器
            tmp[0] = getBootstrapResources(name);
        }
        tmp[1] = findResources(name);
    
        return new CompoundEnumeration<>(tmp);
    }
    
  • 另一种是 ClassLoader 类的静态方法,在 AppClassLoader 的范围,即 classPath 下进行查找
    // ClassLoader.java
    
    public static URL getSystemResource(String name) {
        ClassLoader system = getSystemClassLoader(); // 返回 AppClassLoader
        // parent == null, 表示已经找到最顶级 BootStrapClassLoader 加载器
        if (system == null) {
            return getBootstrapResource(name);
        }
        return system.getResource(name);
    }
    

两种查找资源的方法,都是先递归委托给 parent 的类加载器查找,最后在自己的加载范围内查找。和类加载时的双亲委派一样。

2. SpringFactoriesLoader 扫描资源

SpringFactoriesLoader 会使用 classLoader 对象扫描其可见域下的 spring.properties 实现类,并缓存所有 classLoader 对象和 Map<接口名, List[实现类名]> 的映射关系,其他类加载时,直接通过缓存获取接口的实现类名集合,并用 loadFactories() 反射实例化实现类集合

public final class SpringFactoriesLoader {
	public static final String FACTORIES_RESOURCE_LOCATION = "META-INF/spring.factories";
	// interface MultiValueMap<K, V> extends Map<K, List<V>>
	// 全局缓存,key: classLoader 对象   
	//         value: Map <String,List<String>>  (key: 接口名, value: 实现类名的list )
	private static final Map<ClassLoader, MultiValueMap<String, String>> cache = new ConcurrentReferenceHashMap<>();
 	// 
	public static <T> List<T> loadFactories(Class<T> factoryType, @Nullable ClassLoader classLoader) {
		ClassLoader classLoaderToUse = classLoader;
		if (classLoaderToUse == null) {
			classLoaderToUse = SpringFactoriesLoader.class.getClassLoader();
		}
		// 找到接口名下配置的所有实现类类名
		List<String> factoryImplementationNames = loadFactoryNames(factoryType, classLoaderToUse);
		List<T> result = new ArrayList<>(factoryImplementationNames.size());
		for (String factoryImplementationName : factoryImplementationNames) {
			// 对 class 反射实例化出对象
			result.add(instantiateFactory(factoryImplementationName, factoryType, classLoaderToUse));
		}
		AnnotationAwareOrderComparator.sort(result);
		return result;
	}
	
	// 找到接口名下配置的所有实现类类名, 返回 List<String>
	public static List<String> loadFactoryNames(Class<?> factoryType, @Nullable ClassLoader classLoader) {
		String factoryTypeName = factoryType.getName();
		// 调用 private 方法
		return loadSpringFactories(classLoader).getOrDefault(factoryTypeName, Collections.emptyList());
	}

	/**
	  * 加载 classLoader 对象扫描域下,所有 spring.factpries 文件中配置的 <接口名,List[实现类名]>,放入 cache 中,
	  * 下次从 cache 中直接通过 classLoader 对象取 <接口名,List[实现类名]>
	  */
	private static Map<String, List<String>> loadSpringFactories(@Nullable ClassLoader classLoader) {
		MultiValueMap<String, String> result = cache.get(classLoader);
		if (result != null) {
			return result;
		}

		try {
			// ClassLoader 两种扫描资源的方法
			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 文件的每一行,加入 Map<String,List<String>> 中
				Properties properties = PropertiesLoaderUtils.loadProperties(resource);
				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;
		}
		// ...  
	}
	/**
	 * (1)用 classLoader 加载实现类
	 * (2)并校验实现类和配置的接口是否是实现关系
	 * (3)根据实现类名反射实例化对象,
	 */
	private static <T> T instantiateFactory(String factoryImplementationName, Class<T> factoryType, ClassLoader classLoader) {
		try {
			// (1)用 classLoader 加载实现类
			Class<?> factoryImplementationClass = ClassUtils.forName(factoryImplementationName, classLoader);
			// (2)并校验实现类和配置的接口是否是实现关系
			if (!factoryType.isAssignableFrom(factoryImplementationClass)) {
				throw new IllegalArgumentException(
						"Class [" + factoryImplementationName + "] is not assignable to factory type [" + factoryType.getName() + "]");
			}
			// (3)根据实现类名反射实例化对象,
			return (T) ReflectionUtils.accessibleConstructor(factoryImplementationClass).newInstance();
		}
		// ...
	}
}
3. 实现自动配置的 @EnableAutoConfiguration 注解

@EnableAutoConfiguration 注解如下:

@AutoConfigurationPackage
@Import(AutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration {}

AutoConfigurationImportSelector.class 该类的 selectImports() 方法使用 SpringFactoriesLoader 扫描并实例化 EnableAutoConfiguration.class 的实现类对象,所以 spring.factories 文件中配置的都是 EnableAutoConfiguration.class 的实现类。如 mybatis starter 的文件内容:

# Auto Configure
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.mybatis.spring.boot.autoconfigure.MybatisLanguageDriverAutoConfiguration,\
org.mybatis.spring.boot.autoconfigure.MybatisAutoConfiguration
protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
	List<String> configurations = SpringFactoriesLoader.loadFactoryNames(EnableAutoConfiguration.class,
			getBeanClassLoader());
	return configurations;
}
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值