Spring源码之SpringFactoriesLoader(一)

作用

SpringFactoriesLoader可以加载jar包下META-INF下的spring.factories,把相关接口的实现按照key,value的形式加载到内存,一个接口的多个实现可以按照","进行分割。对程序员来说,利用SpringFactoriesLoader可以加载自定义的ApplicationListener、ApplicationContextInitializer、Configuration类等,spring-boot的starter就是通过SpringFactoriesLoader加载了相关Configuration配置类。
以下是spring的官方解释:
在这里插入图片描述

源码

我们可以通过SpringFactoriesLoader加载ApplicationContextInitializer的流程分析SpringFactoriesLoader的源码。
1、ApplicationContextInitializer接口相关类的加载是在SpringApplication构造方案里面完成的。
Spring源码之SpringFactoriesLoader(一)_第1张图片
2、进入getSpringFactoriesInstances方法内,可以看到调用了SpringFactoriesLoader.loadFactoryNames(type, classLoader),其中传入了ClassLoader,说明每个同一个类被不同的ClassLoader加载在JVM中属于不同的类。
Spring源码之SpringFactoriesLoader(一)_第2张图片
3、进入loadFactoryNames,调用了loadSpringFactories方法。
Spring源码之SpringFactoriesLoader(一)_第3张图片
4、进入loadSpringFactories方法中。

private static Map<String, List<String>> loadSpringFactories(@Nullable ClassLoader classLoader) {
// 根据classLoader从缓存取,MultiValueMap是一个key,values的数据结构,一个key对应多个value
		MultiValueMap<String, String> result = cache.get(classLoader);
		if (result != null) {
			return result;
		}

		try {
		// 扫描所有jar下面的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()) {
				URL url = urls.nextElement();
				UrlResource resource = new UrlResource(url);
				// 把spring.factories数据加载到Properties
				Properties properties = PropertiesLoaderUtils.loadProperties(resource);
				// 遍历所有的行数据
				for (Map.Entry<?, ?> entry : properties.entrySet()) {
					String factoryTypeName = ((String) entry.getKey()).trim();
					// 把value按照,分割成数组
					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);
		}
	}

示例

1、在resource下面创建META-INF/spring.factories文件,加入

org.springframework.context.ApplicationContextInitializer=com.zjl.spring.initializer.FirstInitializer
org.springframework.context.ApplicationListener=com.zjl.spring.listener.FirsetListener,com.zjl.spring.listener.FourthListener

Spring源码之SpringFactoriesLoader(一)_第4张图片

2、debug查看数据已经被加载
Spring源码之SpringFactoriesLoader(一)_第5张图片

starter中的示例

mybatis-spring-boot-starter中引入了org.mybatis.spring.boot.autoconfigure.MybatisAutoConfiguration
配置类,从而帮助自动注入一些DataSource、SqlSessionFactory等类。
Spring源码之SpringFactoriesLoader(一)_第6张图片

总结

SpringFactoriesLoader是Spring的一个内部加载工厂类,我们自定义的一些ApplicationListener、ApplicationContextInitializer可以通过它加载到内存中,同时starter也必须通过SpringFactoriesLoader把Configuration类加载到内存中,从而被spring解析。

你可能感兴趣的:(spring,Java)