SpringBoot启动探究(二)

文章详细解析了SpringBoot自动配置的实现过程,从@EnableAutoConfiguration注解开始,通过SpringFactoriesLoader加载META-INF/spring.factories文件中的配置,实例化为@Configuration的类,加载到IoC容器。理解这一过程有助于自定义自动配置类。
摘要由CSDN通过智能技术生成

上一小节发现,SpringBoot的启动类上有一个关键的组合注解SpringBootApplication,其中它包含的EnableAutoConfiguration注解实现了启动时的自动配置功能。

具体来说,是通过EnableAutoConfiguration注解里的

@Import(EnableAutoConfigurationImportSelector.class)和Spring自带的工具类SpringFactoriesLoader实现的自动配置。

下面详细探究一下自动配置的实现过程。

自动配置的源码追踪

EnableAutoConfigurationImportSelector类实现了DeferredImportSelector接口,DeferredImportSelector又继承了ImportSelector接口,因此EnableAutoConfigurationImportSelector类里需要对ImportSelector的selectImports()方法做具体的视实现:

EnableAutoConfigurationImportSelector类selectImports()的主要作用是:

public String[] selectImports(AnnotationMetadata annotationMetadata) {
    if (!this.isEnabled(annotationMetadata)) {
        return NO_IMPORTS;
    } else {
        // 获取自动配置的Entry结构的数据
        AutoConfigurationEntry autoConfigurationEntry = this.getAutoConfigurationEntry(annotationMetadata);
        return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());
    }
}

其中AutoConfigurationEntry的数据结构如下:

protected static class AutoConfigurationEntry {
        private final List<String> configurations;
        private final Set<String> exclusions;

        private AutoConfigurationEntry() {
            this.configurations = Collections.emptyList();
            this.exclusions = Collections.emptySet();
        }

        ...
    }

进入this.getAutoConfigurationEntry(annotationMetadata):

protected AutoConfigurationEntry getAutoConfigurationEntry(AnnotationMetadata annotationMetadata) {
    if (!this.isEnabled(annotationMetadata)) {
        return EMPTY_ENTRY;
    } else {
        AnnotationAttributes attributes = this.getAttributes(annotationMetadata);
        // 获得AutoConfigurationEntry中的configurations数据
        List<String> configurations = this.getCandidateConfigurations(annotationMetadata, attributes);
        configurations = this.removeDuplicates(configurations);
        // 获得AutoConfigurationEntry中的exclusions数据
        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 AutoConfigurationEntry(configurations, exclusions);
    }
}

先进入this.getCandidateConfigurations(annotationMetadata, attributes):

protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
    List<String> configurations = new ArrayList(SpringFactoriesLoader.loadFactoryNames(this.getSpringFactoriesLoaderFactoryClass(), this.getBeanClassLoader()));
    ImportCandidates.load(AutoConfiguration.class, this.getBeanClassLoader()).forEach(configurations::add);
    Assert.notEmpty(configurations, "No auto configuration classes found in META-INF/spring.factories nor in META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports. If you are using a custom packaging, make sure that file is correct.");
    return configurations;
}

发现是通过SpringFactoriesLoader.loadFactoryNames()方法得到的configurations,而第一个入参传入的factoryType就是EnableAutoConfiguration.class:

protected Class<?> getSpringFactoriesLoaderFactoryClass() {
    return EnableAutoConfiguration.class;
}
// SpringFactoriesLoader工具类中

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

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

继续进入SpringFactoriesLoader工具类中的loadSpringFactories()方法:

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

        try {
            // 加载了所有META-INF/spring.factories文件下的数据
            Enumeration<URL> 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()) {
                    Map.Entry<?, ?> entry = (Map.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);
        }
    }
}

看到这里我们发现SpringFactoriesLoader#loadSpringFactories()方法就是把所有"META-INF/spring.factories"里的数据都加载进来,放到了一个Map<String, List<String>> result里面。

可能有人会有疑问,我的项目里没有spring.factories文件,springboot从哪读取的文件?

其实springboot的源码里有一个自动配置的库,这个库里的spring.factories文件已经存放了所有可支持自动配置的Configuration清单:

此时再回头看一下SpringFactoriesLoader#loadFactoryNames方法,最后一句return是:

return (List)loadSpringFactories(classLoaderToUse).getOrDefault(factoryTypeName, Collections.emptyList());

还记得factoryTypeName参数是什么吗?就是EnableAutoConfiguration.class。

因此,return的List<String>里面存放的也就是Spring.factories文件里EnableAutoConfiguration所对应的value值,即各种各样的XxxAutoConfiguration。

我们再次回到getAutoConfigurationEntry方法:

protected AutoConfigurationEntry getAutoConfigurationEntry(AnnotationMetadata annotationMetadata) {
    if (!this.isEnabled(annotationMetadata)) {
        return EMPTY_ENTRY;
    } else {
        AnnotationAttributes attributes = this.getAttributes(annotationMetadata);
        // 这里我们拿到了各种各样的XxxAutoConfiguration
    	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);
        // 返回新建的AutoConfigurationEntry
        return new AutoConfigurationEntry(configurations, exclusions);
    }
}

然后再回到最初的起点:

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

所以EnableAutoConfigurationImportSelector#selectImports方法返回的就是所有在spring.factories文件里key为org.springframework.boot.autoconfigure.EnableAutoConfiguration的所有value值class路径,也就是所有被springboot的自动配置支持的Configuration类的路径。

所以,@EnableAutoConfiguration自动配置的原理是:从classpath中搜寻所有的META-INF/spring.factories配置文件,并将其中org.springframework.boot.autoconfigure.EnableutoConfiguration对应的配置项通过反射(Java Refletion)实例化为对应的标注了@Configuration的JavaConfig形式的IoC容器配置类,然后汇总为一个并加载到IoC容器。

那么,EnableAutoConfigurationImportSelector#selectImports方法是在何时被调用的?它所返回的list又是如何被实例化为容器配置类并最终合并到Spring IoC容器的?还得接着往下看。

让Springboot自动配置自定义类

了解上面的自动配置加载过程以后,我们也可以利用这一过程让Springboot自动配置自定义的类:

只需要在项目的META-INF/spring.factories这样写:

org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.example.demo.service.MyOrder

这样我们就可以在spring容器里使用MyOrder的bean了。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值