美文网首页
为什么我要写spring.factories文件?

为什么我要写spring.factories文件?

作者: HanssonLiu | 来源:发表于2020-03-15 21:44 被阅读0次

    在阅读spring-boot相关源码时,常常见到spring.factories文件,里面写了自动配置(AutoConfiguration)相关的类名,因此产生了一个疑问:“明明自动配置的类已经打上了@Configuration的注解,为什么还要写spring.factories文件?”

    这个话题需要从@SpringBootApplication注解开始说起。

    查看@SpringBootApplication源码,我们能看到继承的以下注解:

    @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 {
        ……
    }
    

    其中比较重要的是@EnableAutoConfiguration和@ComponentScan两个注解。@ComponentScan注解的作用是扫描@SpringBootApplication所在的Application类(即spring-boot项目的入口类)所在的包(basepackage)下所有的@component注解(或拓展了@component的注解)标记的bean,并注册到spring容器中。

    注意:分包情况下,只要与Application类所在的包下所有的@Component(包括@Configuration)标记的Bean都会注册到spring容器中。

    看到这里也许会有个疑问,在spring-boot项目中pom文件里面添加的依赖中的bean(spring-boot项目外的bean)是如何注册到spring-boot项目的spring容器中的呢?

    这就需要讨论@EnableAutoConfiguration的作用。查看@EnableAutoConfiguration源码,

    @Target(ElementType.TYPE)
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    @Inherited
    @AutoConfigurationPackage
    @Import(AutoConfigurationImportSelector.class)
    public @interface EnableAutoConfiguration {
        String ENABLED_OVERRIDE_PROPERTY = "spring.boot.enableautoconfiguration";
        Class<?>[] exclude() default {};
        String[] excludeName() default {};
    }
    

    我们可以看到比较关键的代码是@Import(AutoConfigurationImportSelector.class),而AutoConfigurationImportSelector.class做了什么呢?通过其源码可以看出关键的部分为,

    @Override
    public String[] selectImports(AnnotationMetadata annotationMetadata) {
        if (!isEnabled(annotationMetadata)) {
            return NO_IMPORTS;
        }
        AutoConfigurationMetadata autoConfigurationMetadata = AutoConfigurationMetadataLoader
                .loadMetadata(this.beanClassLoader);
        AutoConfigurationEntry autoConfigurationEntry = getAutoConfigurationEntry(autoConfigurationMetadata,
                annotationMetadata);
        return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());
    }
    

    其中,getAutoConfigurationEntry方法获取了spring-boot项目中需要自动配置的项(bean),查看其源码发现,

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

    其中最重要的部分为getCandidateConfigurations方法,它获取了所有可能参与到项目的候选配置bean,与之对应的,getExclusions获取了所有不需要加载的配置bean。进一步查看getCandidateConfigurations方法的源码,

    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;
    }
    

    这个方法的具体实现为,读取spring-boot项目的classpath下META-INF/spring.factories的内容,这个文件常常以K/V的形式存储数据,例如:

    org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
    cn.test.TestConfig
    

    getCandidateConfigurations方法获取需要自动配置的类,除去上面讲到的需要排除(exclude)的配置类,其他类将会注册到spring-boot项目的spring容器中。

    看到这里,想必已经了解@EnableAutoConfiguration注解的工作原理,回到最初的话题,“为什么要写spring.factories文件?”

    结合前面提出的疑问——“在spring-boot项目中pom文件里面添加的依赖中的bean是如何注册到spring-boot项目的spring容器中的呢?”,不难得出spring.factories文件是帮助spring-boot项目包以外的bean(即在pom文件中添加依赖中的bean)注册到spring-boot项目的spring容器的结论。由于@ComponentScan注解只能扫描spring-boot项目包内的bean并注册到spring容器中,因此需要@EnableAutoConfiguration注解来注册项目包外的bean。而spring.factories文件,则是用来记录项目包外需要注册的bean类名。

    ————————————————

    原文链接:https://blog.csdn.net/SkyeBeFreeman/article/details/96291283

    相关文章

      网友评论

          本文标题:为什么我要写spring.factories文件?

          本文链接:https://www.haomeiwen.com/subject/thabehtx.html