• spring.factories与@ComponentScan注解辨析


    我们再启动spring工程时,会从启动类开始,而启动类都会有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 {
    	……
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    其中比较重要的是@EnableAutoConfiguration和@ComponentScan两个注解。

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

    那么@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 {};
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    这里出现了非常关键的@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());
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    其中,getAutoConfigurationEntry方法获取了spring-boot项目中需要自动配置的项(bean),
    进一步深入到getAutoConfigurationEntry方法内部

    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);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    其中最重要的部分为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;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

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

    org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
    com.ruyuan.eshop.common.config.CommonAutoConfiguration
    
    • 1
    • 2

    所以到这里我们就知道了除去上面讲到的需要排除(exclude)的配置类,getCandidateConfigurations方法获取需要自动配置的类,并将其注册到spring容器中,而这些自动配置的类就是再spring.factories里面注册的类

    总结

    1 在spring-boot项目中spring.factories文件的作用是什么?
    其实就是帮助项目包以外的bean注册到spring-boot项目中
    而这些项目包以外的bean其实就是pom文件里面添加的依赖中的那些bean

    2 SpringBootApplication注解是如何发挥作用的?
    即先是通过@ComponentScan注解扫描spring-boot项目包内的bean并注册到spring容器中,
    然后基于@EnableAutoConfiguration注解来注册项目包外的bean。而spring.factories文件,则是用来记录项目包外需要注册的bean类名。

  • 相关阅读:
    安全漏洞扫描工具
    2024年11月1日Github流行趋势
    Qt5.15添加dll库如何指定库文件路径?
    chapter2——时钟和复位
    规则调优必备技能——捞取更多好人,卡住更多坏人
    组合模式,宏指令和普通指令的聚合应用(设计模式与开发实践 P10)
    HashMap
    【ARMv8/ARMv9 硬件加速系列 3.4 -- SVE 复制指令CPY 使用介绍】
    matlab相机标定实验
    截图神器Snipaste,错过真的太可惜
  • 原文地址:https://blog.csdn.net/m0_37900506/article/details/126075623