浅析SpringBoot的约定大于配置原理

516 阅读2分钟

携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第11天,点击查看活动详情

约定大于配置原理

结论:

SpringBoot给我们提供了大量自动配置类. 从而让我们能快速启动一个web项目.

那么在底层, 加载自动配置类的入口是什么? --就是SpringBoot的启动类

 @SpringBootApplication
 public class DemoApplication {
 ​
     public static void main(String[] args) {
         SpringApplication.run(DemoApplication.class, args);
     }
 ​
 }

启动类中有一个`@SpringBootApplication注解, 在main方法中调用静态方法SpringAllication.run(), 传入了该类, 从而能获得该类的注解@SpringBootApplication, 该注解中其实包含了很多底层注解, 如

  • @EnableAutoConfiguration, 表示允许自动配置,
  • 还有@ComponentScan, 用来进行扫描包的配置
 @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注解中, 有一个@Import注解:

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

会将注解中对应的类注册为Spring中的组件, 而如果该class实现了ImportSelector接口的话, 会去找该接口中的selectImports()方法的返回值, 是一个String数组, 表示众多类的全限定类名.

 // DeferredImportSelector是ImportSelector的子类
 public class AutoConfigurationImportSelector implements DeferredImportSelector {
   // ...
   public String[] selectImports(AnnotationMetadata annotationMetadata) {
         if (!this.isEnabled(annotationMetadata)) {
             return NO_IMPORTS;
         } else {
             AutoConfigurationImportSelector.AutoConfigurationEntry autoConfigurationEntry = this.getAutoConfigurationEntry(annotationMetadata);
             return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());
         }
     }
   //...
 }

这个方法的功能是可以通过注解@EnableAutoConfiguration, 可以一次性加载很多Spring预设的组件.

该方法会加载配置文件, 读取里面的值, 然后加载为String数组, 它加载哪里的配置文件呢?

  • 原先的版本加载autoconfigure依赖下的/META-INF/spring.factories文件中, 保存了很多键值对, key是EnableAutoConfiguration的全限定类名, value是String[]数组, 通过逗号来分割众多元素
  • 现在的版本产生了一点变化, 还是在autoconfigure依赖下/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports文件, 包含的是spring提供的自动配置类的全限定类名.
  • 当然之前位置autoconfigure依赖下的/META-INF/spring.factories里的配置还是会读取的, 而新位置的是更多的补充.

下面调用了getAutoConfigurationEntry()方法加载数组, 87行加载为一个list, 然后转换为一个String数组, 其中的getCandateConfigurations()中, 有一行文件名, 会加载指定目录下的配置文件, 并读取其中的值.

这就是自动配置类是如何被加载的.

这些自动配置类里有一些注解

  • @ConditionalOnXXX 当满足xxx条件时, 其他的注解会生效
  • @ConditionalOnMissingXXX 当满足没有xxx条件时, 其他的注解会生效

这两类注解会和其他注解一起使用, 并决定这其他注解是否生效