@EnableAutoConfiguration 加载元数据配置
加载元数据配置主要是为后续操作提供数据支持。
我们先来看加载相关源代码的具体实现,该功能的代码依旧日在 selectlmpots 方法内。
@Override
public String[] selectImports (AnnotationMetadata annotationMetadata) {
AutoConfigurat ionMetadata autoConf igurationMetadata = AutoConfigurationMe
tadataLoader
. loadMetadata(this. beanClassLoader);
}
加载元数据的配置用到了
AutoConfigurationMetadataLoader类提供的loadMetaData方法,该方法会默认加载类路径下 META-INF/springautoconfigure-metadata.properties 内的配置。
final class AutoConfigurationMetadataLoader {
//默认加载元数据的路径
protected static final String PATH = "META- INF/spring- autoconfigure - metadata.
properties";
//默认调用改方法,传入默认 PATH
static AutoConfigurat ionMetadata loadMetadata(ClassLoader classLoader )
return loadMetadata(classLoader, PATH);
static AutoConfigurationMetadata loadMetadata(ClassLoader classLoader, St
ring
path) {
try
//获取数据存储 FEnumeration 中
Enumeration<URL> urls = (classLoader != null) ? classLoader . getResources(path)
: ClassLoader . getSys temResources(path);
Properties properties = new Properties();
while (urls.hasMoreElements()) {
//遍历 Enumeration 中的 URL,加载其中的属性, 存储到 Properties 中
properties . putAll(PropertiesLoaderUtils. loadProperties (new UrlResou
rce(urls .nextElement())));
}
return loadMetadata(properties);
} catch (IOException ex) {
throw new IllegalArgumentException("Unable to load @ConditionalOnClas
; location [" + path + "]",ex);
//创建 AutoConfigurat ionMe tadata 的实现类 PropertiesAutoConf igurat ionMetadat
static AutoConfigurationMetadata loadMetadata(Properties properties) {
return new PropertiesAutoConfigurationMetadata(properties);
// AutoConfigurationMetadata 的内部实现类
private static class PropertiesAutoConfigurationMetadata implements AutoC
on-
figurat ionMetadata {。。。
}。。。
}
在上面的代码中
AutoConfigurationMetadataLoader 调用 ladMetadaClassLoadar cassLoaden)方法,会获取默认变量 PATH 指定的文件,然后加载并存储于 Enumeration 数据结构中。随后从变量 PATH 指定的文件中获取其中配置的属性存诸 Poperties 内,最终调用在该类内部实现的 AutoConfigurationMetadata 的子类的构造方法。
spring-autoconfigure-metadata.properties 文件内的配置格式如下。
自动配置类的全限定名.注解名称=值
如果 spnaotningre-etadata properties 文件内有多个值,就用英文逗号分隔,例如:
org. springframework . boot . autoconfigure . data. jdbc . IdbcRepositoriesAutoConfiguration . ConditionalOnClass=org. springframework. data. jdbc . repos itory. config.JdbcConfigurat ion, org. springframework. jdbc . core . namedpar am .NamedParameterJdbcOperations
。。。
为什么要加载此元数据呢?加载元数据主要是为了后续过滤自动配置使用。Spring Boot 使用-Annlation 的处理器来收集自动加载的条件,这些条件可以在元数据文件中进行配置。SpingBoot 会将收集好的 C@Confguration 进行一 次过滤,进而剔除不满足条件的配置类。
在官方文档中已经明确指出,使用这种配置方式可以有效缩短 SpringBoot 的启动时间,减少@oniguraio 类的数量,从而减少初始化 ean 的耗时。后续章节中我们会看到过滤自动配置的具体使用方法。
@EnableAutoConfiguration 加载自动配置 组件
加载自动配置组件是自动配置的核心组件之一,这些自动配置组件在类路径中 METAINF 目录下的 Ssping fctories 文件中进行注册。Spring Boof 预置了-部分常用组件,如果我们需要创建自己的组件,可参考 SpringBoot 预置组件在自己的 Starters 中进行配置,在后面的章节中会专门对此进行讲解。
通过 Sping Core 提供的 Soingacaorestoaodar 类可以读取 spring. fctories 文件中注册的类。下面我们通过源代码来看一下如何在
AutoConfigurationlmportSelector 类中通过 getCateConfigurations 方法来读取 spring.factories 文件中注册的类。
protected List<String> getCandidateConfigurat ions (Annotat ionMetadata metadata,
AnnotationAttributes attr
ibutes) {
List<String> configurations = SpringFactoriesLoader . loadF actoryNames(
getSpringFactoriesLoaderFactoryClass(), getBeanClassLoader());
Assert. notEmpty (configurations ,
"No auto configuration classes found in META-INF/spring.f
actories. If you
+ "are using a custom packaging, make sure that file is C
orrect.");
return configurations;
protected Class<?> getSpringF actoriesLoaderFactoryClass() {
return EnableAutoConfiguration.class;
}
getCandidateConfigurations 方 法 使 用 SpringFactoriesL oader 类 提 供 的loadFactoryNames 方法来读取 META-INF/spring.factories 中的配置。如果程序未读取到任何 配 置 内 容 , 会 抛 出 异 常 信 息 。 而 loadFactoryNames 方 法 的 第 一 个 参 数 为getSpringFactoriesL oaderFactoryClass 方法返回的 EnableAutoConfiguration.class,也就是说 loadFactoryNames 只会读取配置文件中针对自动配置的注册类。
SpringFactoriesLoader 类的 loadFactoryNames 方法相关代码如下。
public final class SpringFactoriesLoader {
//概类加载文件的路径, 可能存在多个
public static final String FACTORIES_ RESOURCE LOCATION = "META- INF/spring. factories";
//加载所有的 META- INF/spring. factories.文件,封装成 Map, 并从中获取指定类名的列表
public static List<String> loadFactoryNames(Class<?> factoryClass, @Nullabl
e ClassLoader classLoader) {
String factoryClassName = factoryClass . getName();
return loadSpringFactories (classLoader) . getOrDefault(factoryClassName ,
Collections . emptylis
t());
//加载所有的 META- INF/spring. factories 文件,封装成 Map, Key 为接口的全类名,Valu
e 为对应配置值的 ist 集合
private static Map<String, List<String>> loadSpringFactories (@Nullable Clas
sLoader classLoader) {
MultiValueMap<String, String> result = cache.get(classLoader);
if (result != null) {
return result;
try {
Enumeration<URL> urls = (classLoader != null ?
classLoader . getResources(FACTORIES_ RESOURCE_ LO
CATION) :
ClassLoader . getSystemResources (FACTORIES_ RESOU
RCE_ LOCATION));
result = new LinkedMultiValueMap<>();
while (urls .hasMoreElements()) {
URL url = urls .nextElement();
UrlResource resource = new UrlResource(url);
Properties properties = PropertiesLoaderUtils . loadProperties ( resourc
e);
for (Map.Entry<?, ?> entry : properties .entrySet()) {
String factoryClassName = ((String) entry . getKey()).trim();
for (String factoryName : StringUtils . commaDelimitedL istTo-
StringArray((String) entry.getValue())) {
result . add(factoryClassName, factoryName . trim());
cache. put(classLoader, result);
return result;
} catch (I0Exception ex)
throw new IllegalArgumentException("Unable to load factories from location [" +
FACTORIES_ RESOURCE_ _LOCATION + "]",e
x);
}
}
。。。
}
简单描述以上加载的过程就是: SpringFactoriesLoader 加载 器加载指定 ClassL oader 下面的所有 META-INF/spring.factories 文件,并将文件解析内容存于 Map<String,List<String>>内。然后,通过 loadFactoryNames 传递过来的 class 的名称从 Map 中获得该类的配置列表。
结 合 下 面 spring.factories 文 件 的 内 容 格 式 , 我 们 可 以 更 加 清 晰 地 了 解Map<String,List<String>>中都存储了什么。
# Auto Configure
org. springframework . boot . autoconfigure . EnableAutoConf iguration=\
org. springframework . boot . autoconfigure . admin. Spr ingApplicat ionAdminJmxAutoC
onfig :
uration, \
org. springframework . boot . autoconfigure . aop . AopAutoConfiguration, \
org . springframework . boot . autoconfigure . amqp . RabbitAutoConfiguration, \
org. springframework . boot. autoconfigure . batch . BatchAutoConfiguration,\
org. springfr amework . boot . autoconfigure . cache . CacheAutoConfiguration, \
org. springframework . boot . autoconfigure . cassandra . CassandraAutoConfiguratio
n,\
。。。
以上代码仅以 EnableAutoConfiguration 配置的部分内容为例,spring.factories 文件的基本格式为自动配置类的全限定名=值,与 2.3.5 节中介绍的元数据的格式很相似,只不过缺少了“,注解名称”部分,如果包含多个值,用英文逗号分隔。
我们继续以 EnableAutoConfiguration 的配置为例 Map<String,List<String>>内存储的对应数据就是key值为\/nliin/古
org.springframework.boot.autoconfigure .EnableAutoConfiguration,Value 值为其等号后面以分号分割的各种 AutoConfiguration 类。
当然,spring.factories 文件内还有其他的配置,比如用于监听的 Listeners 和用于过滤的Filters 等。很显然,在加载自动配置组件时,此方法只用到了 EnableAutoConfiguration 对应的配置。
因为程序默认加载的是 ClassLoader 下面的所有 META-INF/spring.factories文件中的配置,所以难免在不同的 jar 包中出现重复的配置。我们可以在源代码中使用 Set 集合数据不可重复的特性进行去重操作。
protected final <T> List<T> removeDuplicates(List<T> list) {
return new ArrayList<>(new LinkedHashSet<>(list));
}
本文给大家讲解的内容是AutoConfiguration加载元数据配置和加载自动配置组件
- 下篇文章给大家讲解的是AutoConfiguration 排除指定组件和过滤自动配置组件;
- 觉得文章不错的朋友可以转发此文关注小编;
- 感谢大家的支持!