SpringBoot自动配置源码分析

1 预备知识

1.1 Bean扫描

注解@ComponentScan,默认扫描启动类所在包及其子包。

想要自定义可以修改注解参数,例如@ComponentScan(baesePackages = 'com.henry')

1.2 Bean注册

  • 需要注册的bean是自定义,使用以下注解注册bean对象。
注解 说明 位置
@Component 声明bean的基础注解 不属于以下三类时,用此注解
@Controller @Component的衍生注解 标注在控制器类上
@Service @Component的衍生注解 标注在业务类上
@Repository @Component的衍生注解 标注在数据访问类上(由于与mybatis整合,用的少)
  • 如果要注册的bean对象来自于第三方,使用下面两个注解
    • @Bean
    • @Import

示例:

1
2
3
4
5
6
7
@Configuration
public class config {
    @Bean //默认名是方法名
    public Country country() {
        return  new Country();
    }
}

假设上面写的config.java不在启动类所在包及其子包,那么可以使用import注解进行bean的注册。

1
2
3
4
5
6
7
@SpringBootApplication
@Import(config.class) //默认名是全类名
public class xxxApplication {
    public static void main(String[] args) {
        xxxApplication.run(xxxApplication.class, args);
    } 
}

此时会将config放入ioc容器,并将config中声明的bean也注册到ioc容器。

同时,import可以导入多个配置类,一种是@Import({config.class, xx.class}),或者导入ImportSelector的接口实现类,接口实现类要实现selectImports方法,在方法内部返回数组,数组内容为要导入的Bean对象的全类名。第二种方法示例如下:

1
2
3
4
5
6
public class CommonImportSelector implements ImportSelector {
    @Override
    public String[] selectImports(AnnotationMetadata importingClassMetadata) {
        return new String[]{"com.itheima.config.CommonConfig"};
    }
}

1.3 Bean管理

注解 说明
@ConditionalOnProperty 配置文件中存在对应的属性,才声明该bean
@ConditionalOnMissingBean 当不存在当前类型的bean时,才声明该bean
@ConditionalOnClass 当前环境存在指定的这个类时,才声明该bean

2 源码解读

  1. Springboot注解包含以下注解@SpringBootConfiguration@EnableAutoConfiguration@ComponentScan三个核心注解。
1
2
3
4
5
@...
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan(...)
public @interface SpringBootApplication {
  1. @EnableAutoConfiguration注解包括了@Import({AutoConfigurationImportSelector.class})注解。该注解导入ImportSelector的接口实现类。
1
2
3
@Import({AutoConfigurationImportSelector.class})
public @interface EnableAutoConfiguration {
}
  1. 该类重写了selectImports方法,调用过程指向getAutoConfigurationEntry
1
2
3
4
5
6
7
public String[] selectImports(AnnotationMetadata annotationMetadata) {
	if (!this.isEnabled(annotationMetadata)) {
		return NO_IMPORTS;
	} else {
		AutoConfigurationEntry autoConfigurationEntry = this.getAutoConfigurationEntry(annotationMetadata);			return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());
	}
}
  1. getAutoConfigurationEntry,显然configurations来自于getCandidateConfigurations
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
    protected 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 AutoConfigurationEntry(configurations, exclusions);
        }
    }
  1. getCandidateConfigurations,本方法中出现META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports路径。
1
2
3
4
5
6
	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;
    }
  1. 该路径中存在所有需要导入的自动配置类。在springboot-starter(该starter为所有starterstarter)导入的spring-boot-autoconfigureINF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports路径中存在org.springframework.boot.autoconfigure.web.servlet.WebMvcAutoConfiguration类。以该类为例继续分析。
1
2
3
4
5
6
@AutoConfiguration
public class WebMvcAutoConfiguration {
    @EnableConfigurationProperties({WebProperties.class})
    public static class EnableWebMvcConfiguration extends DelegatingWebMvcConfiguration implements ResourceLoaderAware {
        xxx;
}
  1. 该配置类注册了多个bean,EnableWebMvcConfigurationWebProperties.class绑定,该类中@EnableConfigurationProperties注解可以将@ConfigurationProperties(prefix = "spring.mvc")注解所在类注入。因为即使加了@Component注解在Properties文件下该类依旧无法被扫描,所以采用@EnableConfigurationProperties注解绑定。
1
2
3
@ConfigurationProperties(prefix = "spring.mvc")
public class WebMvcProperties {
}

此外,经实验测试,类路径下所有的META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports都会被读取。

updatedupdated2023-11-302023-11-30