您好,登录后才能下订单哦!
在Spring Boot中,spring.factories
文件扮演着非常重要的角色。它通过SPI(Service Provider Interface)机制,允许开发者在不修改Spring Boot源码的情况下,扩展和定制Spring Boot的行为。本文将深入分析spring.factories
文件的加载时机及其背后的源码实现。
spring.factories
文件是Spring Boot中用于自动配置的核心文件之一。它位于META-INF
目录下,通常包含一系列的键值对,用于指定Spring Boot在启动时需要加载的自动配置类、监听器、初始化器等。
例如,一个典型的spring.factories
文件内容如下:
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.example.MyAutoConfiguration
org.springframework.context.ApplicationListener=\
com.example.MyApplicationListener
在这个例子中,MyAutoConfiguration
类将在Spring Boot启动时被自动加载,而MyApplicationListener
将在应用上下文初始化时被注册为监听器。
spring.factories
文件的加载时机主要发生在Spring Boot应用的启动过程中。具体来说,它是在SpringApplication
类的初始化阶段被加载的。
SpringApplication
是Spring Boot应用的入口类,负责启动Spring应用上下文。在SpringApplication
的构造函数中,会调用SpringFactoriesLoader.loadFactoryNames
方法来加载spring.factories
文件中的配置。
public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
this.resourceLoader = resourceLoader;
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
方法会调用SpringFactoriesLoader.loadFactoryNames
来加载spring.factories
文件中的配置。
SpringFactoriesLoader
是Spring Boot中用于加载spring.factories
文件的核心工具类。它通过loadFactoryNames
方法从META-INF/spring.factories
文件中加载指定类型的工厂类。
public static List<String> loadFactoryNames(Class<?> factoryType, @Nullable ClassLoader classLoader) {
String factoryTypeName = factoryType.getName();
return loadSpringFactories(classLoader).getOrDefault(factoryTypeName, Collections.emptyList());
}
loadSpringFactories
方法会遍历所有META-INF/spring.factories
文件,并将文件中的配置加载到一个Map
中。
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);
}
}
在上述代码中,loadSpringFactories
方法会从类路径中加载所有的META-INF/spring.factories
文件,并将其内容解析为一个Map
。这个Map
的键是工厂类的全限定名,值是该工厂类的实现类的全限定名列表。
在Spring Boot启动过程中,自动配置类的加载是通过SpringFactoriesLoader
完成的。具体来说,SpringApplication
会调用SpringFactoriesLoader.loadFactoryNames
方法来加载spring.factories
文件中定义的自动配置类。
protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
List<String> configurations = SpringFactoriesLoader.loadFactoryNames(getSpringFactoriesLoaderFactoryClass(),
getBeanClassLoader());
Assert.notEmpty(configurations, "No auto configuration classes found in META-INF/spring.factories. If you "
+ "are using a custom packaging, make sure that file is correct.");
return configurations;
}
在上述代码中,getCandidateConfigurations
方法会调用SpringFactoriesLoader.loadFactoryNames
来加载spring.factories
文件中定义的自动配置类。
spring.factories
文件的加载顺序是由类加载器的加载顺序决定的。通常情况下,类加载器会按照以下顺序加载类路径中的资源:
因此,spring.factories
文件的加载顺序也是按照类加载器的加载顺序进行的。如果多个spring.factories
文件中定义了相同的键,那么后加载的文件会覆盖先加载的文件。
为了提高性能,SpringFactoriesLoader
会对加载的spring.factories
文件进行缓存。具体来说,loadSpringFactories
方法会将加载的spring.factories
文件内容缓存到一个ConcurrentReferenceHashMap
中。
private static final Map<ClassLoader, MultiValueMap<String, String>> cache = new ConcurrentReferenceHashMap<>();
在这个缓存中,键是类加载器,值是spring.factories
文件的内容。这样,在下次加载相同的spring.factories
文件时,可以直接从缓存中获取,而不需要重新加载。
spring.factories
文件的扩展机制允许开发者在自己的项目中定义自定义的自动配置类、监听器、初始化器等。只需要在项目的META-INF/spring.factories
文件中添加相应的配置即可。
例如,假设我们有一个自定义的自动配置类MyAutoConfiguration
,我们可以在META-INF/spring.factories
文件中添加如下配置:
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.example.MyAutoConfiguration
这样,当Spring Boot启动时,MyAutoConfiguration
类将被自动加载并应用到Spring应用上下文中。
spring.factories
文件是Spring Boot中用于自动配置的核心文件之一。它通过SPI机制,允许开发者在不需要修改Spring Boot源码的情况下,扩展和定制Spring Boot的行为。本文详细分析了spring.factories
文件的加载时机及其背后的源码实现,包括SpringApplication
的初始化、SpringFactoriesLoader
的加载过程、加载顺序、缓存机制以及扩展机制。
通过深入理解spring.factories
文件的加载机制,开发者可以更好地利用Spring Boot的自动配置功能,定制和扩展自己的Spring Boot应用。
本文通过对spring.factories
文件的加载时机及其源码的深入分析,帮助开发者更好地理解Spring Boot的自动配置机制。希望本文能为你在Spring Boot开发中提供有价值的参考和帮助。
免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。