springboot加载spring.factories文件

本文详细解析了SpringBoot如何通过`spring.factories`文件进行初始化,涉及`ClassLoader`,`loadFactoryNames`和`loadSpringFactories`等关键方法,展示了SpringBoot如何高效地从类路径加载配置信息。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

springboot加载spring.factories文件

从springboot的main方法开始逐行分析

开始分析

public static void main(String[] args)
{
    SpringApplication.run(DemoApplication.class, args);
}
public static ConfigurableApplicationContext run(Class<?> primarySource, String... args) {
	    return run(new Class<?>[] { primarySource }, args);
}

一路跟进,到如下的方法中,继续进入到 new SpringApplication(primarySources) 方法中

public static ConfigurableApplicationContext run(Class<?>[] primarySources, String[] args) {
	    return new SpringApplication(primarySources).run(args);
}

紧接着进入到如下的方法中,看到 setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class)); 这一行,注意getSpringFactoriesInstances(ApplicationContextInitializer.class)方法,这就是接下来的加载spring.factories的主要方法

	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();
  setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
  setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
		this.mainApplicationClass = deduceMainApplicationClass();
}

在跟进到getSpringFactoriesInstances(Class type)方法中,该方法首先获取到类加载器,然后在第二行,注意SpringFactoriesLoader.loadFactoryNames(type, classLoader)方法,根据名称我们大概可以猜到,该方法就是加载spring.factories文件的主要方法,我们进去继续分析

	private <T> Collection<T> getSpringFactoriesInstances(Class<T> type, Class<?>[] parameterTypes, Object... args) 
{
  ClassLoader classLoader = getClassLoader();
  Set<String> names = new LinkedHashSet<>(SpringFactoriesLoader.loadFactoryNames(type, classLoader));
  List<T> instances = createSpringFactoriesInstances(type, parameterTypes, classLoader, args, names);
  AnnotationAwareOrderComparator.sort(instances);
		return instances;
}

第一行代码不用关注,这是在加载时根据类型,获取到对应的需要被创建的实际类;我们主要看loadSpringFactories(classLoader)方法,该方法会为我们加载spring.factories文件

	public static List<String> loadFactoryNames(Class<?> factoryType, @Nullable ClassLoader classLoader) 
{
		String factoryTypeName = factoryType.getName();
		return loadSpringFactories(classLoader).getOrDefault(factoryTypeName, Collections.emptyList());
}

接下来这就是springboot加载spring.factories文件的最主要的方法;此处定义的 FACTORIES_RESOURCE_LOCATION 就是 META-INF/spring.factories ,首先从缓存cache中获取,如果没有则通过当前类加载器去加载对应的spring.factories文件;紧接着,创建了一个MultiValueMap对象,该对象一个key可以对应多个value,当一个key对应的value为多个时,value就是一个List的结构;紧接着遍历对应的urls,将对应的url封装成UrlResource对象,然后调用PropertiesLoaderUtilsloadProperties 方法加载对应的spring.factories文件;然后遍历properties对象,将逗号分隔的字符串分割成字符数组,然后再遍历,将对应的字符串添加到MultiValueMap对象中,当遍历完成后将result直接添加到cache中,此后每一次进入到该方法中都会直接从cache缓存中获取,而不会在重新读取我们的spring.factories文件,到此springboot加载spring.factories文件的过程完成。

	private static Map<String, List<String>> loadSpringFactories(@Nullable ClassLoader classLoader) 
{
		   MultiValueMap<String, String> result = cache.get(classLoader);
		         if (result != null) {
        		    	return result;
		    }

		        try {
		    	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 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;
        }
		 catch (IOException ex) {
		        	throw new IllegalArgumentException("Unable to load factories from location [" +
					FACTORIES_RESOURCE_LOCATION + "]", ex);
		}
}
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值