springboot 初始加载自动配置类

Posted 霜序0.2℃

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了springboot 初始加载自动配置类相关的知识,希望对你有一定的参考价值。

springboot 初始加载自动配置类

@SpringBootApplication --> @EnableAutoConfiguration

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan(
    excludeFilters = {@Filter(
    type = FilterType.CUSTOM,
    classes = {TypeExcludeFilter.class}
), @Filter(
    type = FilterType.CUSTOM,
    classes = {AutoConfigurationExcludeFilter.class}
)}
)
public @interface SpringBootApplication {}
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import({AutoConfigurationImportSelector.class})
public @interface EnableAutoConfiguration {}

@AutoConfigurationPackage只是指定了包,并获取信息封装到数组里

EnableAutoConfiguration源码中有@Import

我们打开AutoConfigurationImportSelector

源码中有一个方法:

public String[] selectImports(AnnotationMetadata annotationMetadata) {
    if (!this.isEnabled(annotationMetadata)) {
        return NO_IMPORTS;
    } else {
        AutoConfigurationImportSelector.AutoConfigurationEntry autoConfigurationEntry = this.getAutoConfigurationEntry(annotationMetadata);
        return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());
    }
}

返回值是一个字符串数组,即要导入的组件的名字

如果annotationMetadata存在,那么从this.getAutoConfigurationEntry(annotationMetadata);,中文翻译即通过注解变化信息自动找到配置入口,最终得到所有配置并通过StringUtils.toStringArray封装返回

再来看看getAutoConfigurationEntry

protected AutoConfigurationImportSelector.AutoConfigurationEntry getAutoConfigurationEntry(AnnotationMetadata annotationMetadata) {
    if (!this.isEnabled(annotationMetadata)) {
        return EMPTY_ENTRY;
    } else {
        AnnotationAttributes attributes = this.getAttributes(annotationMetadata);
        List<String> configurations = this.getCandidateConfigurations(annotationMetadata, attributes);
        configurations = this.removeDuplicates(configurations);
        Set<String> exclusions = this.getExclusions(annotationMetadata, attributes);
        this.checkExcludedClasses(configurations, exclusions);
        configurations.removeAll(exclusions);
        configurations = this.getConfigurationClassFilter().filter(configurations);
        this.fireAutoConfigurationImportEvents(configurations, exclusions);
        return new AutoConfigurationImportSelector.AutoConfigurationEntry(configurations, exclusions);
    }
}

这个方法通过List<String> configurations = this.getCandidateConfigurations(annotationMetadata, attributes);获取到所有需要导入到容器的配置类

protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
    List<String> configurations = SpringFactoriesLoader.loadFactoryNames(this.getSpringFactoriesLoaderFactoryClass(), this.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;
}

我们可以看到第一行有一个spring工厂加载器

List<String> configurations = SpringFactoriesLoader.loadFactoryNames(this.getSpringFactoriesLoaderFactoryClass(), this.getBeanClassLoader());

点进SpringFactoriesLoader.loadFactoryNames

public static List<String> loadFactoryNames(Class<?> factoryType, @Nullable ClassLoader classLoader) {
    ClassLoader classLoaderToUse = classLoader;
    if (classLoader == null) {
        classLoaderToUse = SpringFactoriesLoader.class.getClassLoader();
    }

    String factoryTypeName = factoryType.getName();
    return (List)loadSpringFactories(classLoaderToUse).getOrDefault(factoryTypeName, Collections.emptyList());
}

我们看return的loadSpringFactories

private static Map<String, List<String>> loadSpringFactories(ClassLoader classLoader)

返回的是一个Map,用来加载所有组件

看它完整的源码:

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

        try {
            Enumeration urls = classLoader.getResources("META-INF/spring.factories");

            while(urls.hasMoreElements()) {
                URL url = (URL)urls.nextElement();
                UrlResource resource = new UrlResource(url);
                Properties properties = PropertiesLoaderUtils.loadProperties(resource);
                Iterator var6 = properties.entrySet().iterator();

                while(var6.hasNext()) {
                    Entry<?, ?> entry = (Entry)var6.next();
                    String factoryTypeName = ((String)entry.getKey()).trim();
                    String[] factoryImplementationNames = StringUtils.commaDelimitedListToStringArray((String)entry.getValue());
                    String[] var10 = factoryImplementationNames;
                    int var11 = factoryImplementationNames.length;

                    for(int var12 = 0; var12 < var11; ++var12) {
                        String factoryImplementationName = var10[var12];
                        ((List)result.computeIfAbsent(factoryTypeName, (key) -> {
                            return new ArrayList();
                        })).add(factoryImplementationName.trim());
                    }
                }
            }

            result.replaceAll((factoryType, implementations) -> {
                return (List)implementations.stream().distinct().collect(Collectors.collectingAndThen(Collectors.toList(), Collections::unmodifiableList));
            });
            cache.put(classLoader, result);
            return result;
        } catch (IOException var14) {
            throw new IllegalArgumentException("Unable to load factories from location [META-INF/spring.factories]", var14);
        }
    }
}

我们可以看到有一行Enumeration urls = classLoader.getResources("META-INF/spring.factories");

相当于从这个文件META-INF/spring.factories中扫描

那么这个文件在哪呢?连包名都没有,我们到导入的jar里找,有些没有这个文件,有些有,打开spring-boot-2.5.5.jar下我们发现了文件,然后我们还发现spring-boot-autoconfigure-2.5.5.jar包下面也有这个文件

一个是默认启动类配置包(就是初始化之类的),一个是加载所需的100多个组件包

spring-boot-autoconfigure-2.5.5.jar/META-INF/spring.factories中发现了

# Auto Configure
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\\

也就是EnableAutoConfiguration注解,注解要加载哪些类,就从这里进入

然后我们在后面也可以看到,后缀都是xxxAutoConfiguration

org.springframework.boot.autoconfigure.admin.SpringApplicationAdminJmxAutoConfiguration,\\
org.springframework.boot.autoconfigure.aop.AopAutoConfiguration,\\
org.springframework.boot.autoconfigure.amqp.RabbitAutoConfiguration,\\
org.springframework.boot.autoconfigure.batch.BatchAutoConfiguration,\\
org.springframework.boot.autoconfigure.cache.CacheAutoConfiguration,\\

文件里面写死了一启动就要给容器中加载的所有配置类

按需加载

当然其实也不是一开始全部加载,我们需要按需开启,springboot中有个组件条件依赖的注解ConditionalXXX,例如:ConditionalOnClass,在类路径里面有某个类才生效,只有导入这个包才生效

以上是关于springboot 初始加载自动配置类的主要内容,如果未能解决你的问题,请参考以下文章

Springboot之加载自动配置类

springboot属性类自动加载配置文件中的值

Springboot2.6.x的启动流程和自动配置

SPRINGBOOT02_自动配置原理入门Lombokdev-tools快速初始化boot项目

springBoot 自动配置原理

从SpringBoot源码分析 主程序配置类加载过程