spring boot 获取 spring.factories 中某一键对应的所有值的流程

  1. 最开始的入口,我们最熟悉的SpringApplication.run方法
	public static ConfigurableApplicationContext run(Class<?> primarySource, String... args) {
		return run(new Class<?>[] { primarySource }, args);
	}
	
	//重载
	public static ConfigurableApplicationContext run(Class<?> primarySource, String... args) {
		return run(new Class<?>[] { primarySource }, args);
	}
	
	//重载x2
	public static ConfigurableApplicationContext run(Class<?>[] primarySources, String[] args) {
		//这里创建了一个SpringApplication的实例对象,然后又调用实例的run方法
		return new SpringApplication(primarySources).run(args);
	}
  1. 在spring boot加载的时候经常会访问spring.factories,第一次访问在创建SpringApplication实例的时候便发生了
	public SpringApplication(Class<?>... primarySources) {
		this(null, primarySources);
	}
	
	//重载..
	@SuppressWarnings({ "unchecked", "rawtypes" })
	public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
		this.resourceLoader = resourceLoader;
		Assert.notNull(primarySources, "PrimarySources must not be null");
		this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
		this.webApplicationType = WebApplicationType.deduceFromClasspath();
		//getSpringFactoriesInstances便是spring封装的获取spring.factories中某一项的方法
		//这里获取其的是键为 org.springframework.context.ApplicationContextInitializer 的值列表
		setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
		setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
		this.mainApplicationClass = deduceMainApplicationClass();
	}

	private Class<?> deduceMainApplicationClass() {
		try {
			StackTraceElement[] stackTrace = new RuntimeException().getStackTrace();
			for (StackTraceElement stackTraceElement : stackTrace) {
				if ("main".equals(stackTraceElement.getMethodName())) {
					return Class.forName(stackTraceElement.getClassName());
				}
			}
		}
		catch (ClassNotFoundException ex) {
			// Swallow and continue
		}
		return null;
	}

3.开始进入正题

	private <T> Collection<T> getSpringFactoriesInstances(Class<T> type) {
		return getSpringFactoriesInstances(type, new Class<?>[] {});
	}
	
	// 一个重载方法
	private <T> Collection<T> getSpringFactoriesInstances(Class<T> type, Class<?>[] parameterTypes, Object... args) {
		ClassLoader classLoader = getClassLoader();
		// Use names and ensure unique to protect against duplicates 使用set防止值重复
		// SpringFactoriesLoader.loadFactoryNames 方法获取 type 对应的值列表
		Set<String> names = new LinkedHashSet<>(SpringFactoriesLoader.loadFactoryNames(type, classLoader));
		//这个方法获取所有值的实例对象
		List<T> instances = createSpringFactoriesInstances(type, parameterTypes, classLoader, args, names);
		//根据 @Ordered 和 @Priority 注解排序
		AnnotationAwareOrderComparator.sort(instances);
		return instances;
	}
  1. 进入 SpringFactoriesLoader.loadFactoryNames 方法
	public static List<String> loadFactoryNames(Class<?> factoryType, @Nullable ClassLoader classLoader) {
		String factoryTypeName = factoryType.getName();
		//loadSpringFactories 方法获取所有 spring.factories 中所有的信息
		//loadSpringFactories 方法返回的是一个 键为string,值为list的map,map中存储的就是所有spring.factories聚合后的内容
		return loadSpringFactories(classLoader).getOrDefault(factoryTypeName, Collections.emptyList());
	}
  1. 进入 loadSpringFactories 方法
	private static Map<String, List<String>> loadSpringFactories(@Nullable ClassLoader classLoader) {
		//cache是个concurrentHashMap,这种经常需要的数据当然是要上缓存的(我暂时不知道用map做缓存干嘛,可能会有多个类加载器?)
		MultiValueMap<String, String> result = cache.get(classLoader);
		if (result != null) {
			return result;
		}

		//没有缓存的话就获取一遍
		try {
			//spring boot能获取classpath下每一项的同一个路径的文件的核心就在这里,调用类加载器的 getResources 方法
			//FACTORIES_RESOURCE_LOCATION 是一个值为 META-INF/spring.factories 的常量,定义spring.factories的位置
			//不同的类加载器对于 getResources 方法都有不同的实现(没细看,其实)
			Enumeration<URL> urls = (classLoader != null ?
					classLoader.getResources(FACTORIES_RESOURCE_LOCATION) :
					ClassLoader.getSystemResources(FACTORIES_RESOURCE_LOCATION));
			result = new LinkedMultiValueMap<>();
			//遍历每一个 spring.factories
			while (urls.hasMoreElements()) {
				URL url = urls.nextElement();
				UrlResource resource = new UrlResource(url);
				//生成 Properties 对象
				Properties properties = PropertiesLoaderUtils.loadProperties(resource);
				//保存所有键值对
				for (Map.Entry<?, ?> entry : properties.entrySet()) {
					String factoryTypeName = ((String) entry.getKey()).trim();
					//StringUtils.commaDelimitedListToStringArray 按逗号分割
					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);
		}
	}

总结:spring boot 加载spring.factories其实就是利用了类加载器的 getResources 方法,获取classpath下每一项的spring.factories文件,然后将所有factores文件的所有键值对保存在一个map中,要用是按key获取便是

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值