【SpringBoot】SpringBoot自动配置原理

蔚落 2022-10-02 13:49 363阅读 0赞

SpingBoot 自动配置原理解析

SpringBoot框架可以对Spring以及SpringMVC进行自动配置,十分方便。最近研究了一下自动配置的原理,记录一下

1、SpringBoot启动类

SpringBoot有一个主配置类,即AutoconfigApplication类。SpringBoot在启动时会加载主配置类,开启了一些自动配置功能。

  1. @SpringBootApplication
  2. public class AutoconfigApplication {
  3. public static void main(String[] args) {
  4. SpringApplication.run(AutoconfigApplication.class, args);
  5. }
  6. }

我们跟踪进入@SpringBootApplication注解,其中会有一个@EnableAutoConfiguration注解:

  1. @Target({ ElementType.TYPE})
  2. @Retention(RetentionPolicy.RUNTIME)
  3. @Documented
  4. @Inherited
  5. @AutoConfigurationPackage
  6. // 引入了一个AutoConfigurationImportSelector类
  7. @Import({ AutoConfigurationImportSelector.class})
  8. public @interface EnableAutoConfiguration {
  9. String ENABLED_OVERRIDE_PROPERTY = "spring.boot.enableautoconfiguration";
  10. Class<?>[] exclude() default { };
  11. String[] excludeName() default { };
  12. }

@Import注解引入了一个AutoConfigurationImportSelector类,我们进入此类中,发现有如下方法:

  1. public String[] selectImports(AnnotationMetadata annotationMetadata) {
  2. if (!this.isEnabled(annotationMetadata)) {
  3. return NO_IMPORTS;
  4. } else {
  5. AutoConfigurationMetadata autoConfigurationMetadata = AutoConfigurationMetadataLoader.loadMetadata(this.beanClassLoader);
  6. AutoConfigurationImportSelector.AutoConfigurationEntry autoConfigurationEntry = this.getAutoConfigurationEntry(autoConfigurationMetadata, annotationMetadata);
  7. return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());
  8. }
  9. }

我们继续跟进this.getAutoConfigurationEntry()方法:

  1. protected AutoConfigurationImportSelector.AutoConfigurationEntry getAutoConfigurationEntry(AutoConfigurationMetadata autoConfigurationMetadata, AnnotationMetadata annotationMetadata) {
  2. if (!this.isEnabled(annotationMetadata)) {
  3. return EMPTY_ENTRY;
  4. } else {
  5. AnnotationAttributes attributes = this.getAttributes(annotationMetadata);
  6. // 获取候选得配置
  7. List<String> configurations = this.getCandidateConfigurations(annotationMetadata, attributes);
  8. configurations = this.removeDuplicates(configurations);
  9. Set<String> exclusions = this.getExclusions(annotationMetadata, attributes);
  10. this.checkExcludedClasses(configurations, exclusions);
  11. configurations.removeAll(exclusions);
  12. configurations = this.filter(configurations, autoConfigurationMetadata);
  13. this.fireAutoConfigurationImportEvents(configurations, exclusions);
  14. return new AutoConfigurationImportSelector.AutoConfigurationEntry(configurations, exclusions);
  15. }
  16. }

接下来,进入到this.getCandidateConfigurations()方法中找到如下代码:

  1. protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
  2. List<String> configurations = SpringFactoriesLoader.loadFactoryNames(this.getSpringFactoriesLoaderFactoryClass(), this.getBeanClassLoader());
  3. 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.");
  4. return configurations;
  5. }

我们摘出如下关键代码:

  1. SpringFactoriesLoader.loadFactoryNames()

作用: 扫描所有jar包类路径下 META-INF/spring.factories,把扫描到的这些文件的内容包装成properties对象,从properties中获取到EnableAutoConfiguration.class类(类名)对应的值,然后把他们添加在容器中
截取部分spring.factories内容如下:

  1. # Auto Configure
  2. org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
  3. org.springframework.boot.autoconfigure.admin.SpringApplicationAdminJmxAutoConfiguration,\
  4. org.springframework.boot.autoconfigure.aop.AopAutoConfiguration,\
  5. org.springframework.boot.autoconfigure.amqp.RabbitAutoConfiguration,\
  6. org.springframework.boot.autoconfigure.batch.BatchAutoConfiguration,\
  7. org.springframework.boot.autoconfigure.cache.CacheAutoConfiguration,\
  8. org.springframework.boot.autoconfigure.cassandra.CassandraAutoConfiguration,\
  9. org.springframework.boot.autoconfigure.cloud.CloudServiceConnectorsAutoConfiguration,\
  10. org.springframework.boot.autoconfigure.context.ConfigurationPropertiesAutoConfiguration,\
  11. org.springframework.boot.autoconfigure.context.MessageSourceAutoConfiguration,\

到此为止便找到了我们想要得答案:
@EnableAutoConfiguration 注解会导入一个自动配置类AutoConfigurationImportSelector,而这个类又通过selectImports()方法扫描各个jar包路径下的 META-INF/spring.factories 并封装成相对应的properties对象,从中读取到EnableAutoConfiguration.class类所对应的值,即类的全限定类名,然后把他们添加进容器中,至此就完成了自动配置

自动配置案例

如果说上面的源码读的有些晦涩,可以通过下面的小案例来了解一下。

1、application.properties配置文件

在SpringBoot的配置文件中配置一下信息

  1. student.name=张三
  2. student.age=18

2、编写properties对应的JavaBean

  1. @ConfigurationProperties(prefix = "student") //读取配置文件中前缀为student的配置与类中的属性相对应
  2. public class StudentProperties {
  3. private String name;
  4. private Integer age;
  5. @Override
  6. public String toString() {
  7. return "StudentConfig{" +
  8. "name='" + name + '\'' +
  9. ", age=" + age +
  10. '}';
  11. }
  12. public String getName() {
  13. return name;
  14. }
  15. public void setName(String name) {
  16. this.name = name;
  17. }
  18. public Integer getAge() {
  19. return age;
  20. }
  21. public void setAge(Integer age) {
  22. this.age = age;
  23. }
  24. }

3、编写自动配置类

  1. @Configuration //声明这是一个配置类
  2. @EnableConfigurationProperties(StudentProperties.class) // 读入配置对象
  3. @ConditionalOnClass(StudentService.class) // 只有类路径存在时才会加载配置类
  4. public class StudentAutoConfiguration {
  5. @Autowired
  6. private StudentProperties studentConfig;
  7. @Bean
  8. @ConditionalOnMissingBean(StudentService.class)
  9. public StudentService returnStudentService(){
  10. StudentService studentService = new StudentService();
  11. studentService.setName(studentConfig.getName());
  12. return studentService;
  13. }
  14. }

其中的@ConditionalOnClass注解的作用就相当与一个 if 的条件判断,作用就是当类路径存在时才会加载该配置类,如果不存在就跳过。

@ConditionalOnMissingBean(StudentService.class) 这个注解也是 SpringBoot 自动配置的核心所在,它的作用是: 判断如果 StudentService 不存在,就加载配置类,如果存在就跳过 ,这个注解就解释了为什么SpringBoot会优先使用用户自定义的配置类,如果用户没有自定义配置类,才会加载自动配置类。

4、使用自动配置类编写测试代码

  1. @Controller
  2. public class TestController {
  3. @Autowired
  4. StudentService studentService;
  5. @RequestMapping("/getStudentName")
  6. @ResponseBody
  7. public String getName(){
  8. return studentService.getName();
  9. }
  10. }

此时的运行结果如下图所示:
在这里插入图片描述
用于模拟SpringBoot自动配置了StudentService

5、自定义的用户配置类

  1. @Component
  2. public class StudentService {
  3. private String name = "autoConfig";
  4. public String getName(){
  5. return name;
  6. }
  7. public void setName(String name) {
  8. this.name = name;
  9. }
  10. }

定义自己的配置类加入容器中,并且给Name设置初值来区分,运行结果如下:
在这里插入图片描述
两个不同的结果就印证了SpringBoot会优先加载用户自定义的配置类,如果用户没有自定义配置类,才会加载自动配置类。

发表评论

表情:
评论列表 (有 0 条评论,363人围观)

还没有评论,来说两句吧...

相关阅读

    相关 SpringBoot自动配置原理

    SpringBoot可以简化开发的一个主要原因就是采用了默认配置,所谓约定大于配置就是这个意思。在没有自己指定配置的时候使用默认配置的原理大致如下。如有错误,还请指正。 ==

    相关 SpringBoot自动配置原理

            配置文件到底能写什么?怎么写?自动配置原理; [配置文件能配置的属性参照][Link 1] 1、自动配置原理: 1)、SpringBoot启动的时候加