• Spring Boot 自动配置原理


    前言

    使用 Spring Boot 可以高效地开发各种场景,特别是 web 场景下,导入一个 web-starter 依赖就可以完成 web 开发,并且我们并不需要做过多的配置。

    简而言之,Spring Boot 的自动配置帮助我们将某些场景的通用配置自动加载到系统中,从而节省时间去完成主要功能

    那么其实现原理是什么呢?

    原理

    首先查看 Spring Boot 的主启动程序的注解 @SpringBootApplication

    我们查看源码可以看到它其实是一个组合注解:

    1. @Target(ElementType.TYPE)
    2. @Retention(RetentionPolicy.RUNTIME)
    3. @Documented
    4. @Inherited
    5. 1@SpringBootConfiguration
    6. 2@EnableAutoConfiguration
    7. 3@ComponentScan(excludeFilters = { @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
    8. @Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })
    9. public @interface SpringBootApplication{}
    10. 复制代码

    【1】@SpringBootConfiguration:标注当前类为一个配置类

    【2】@EnableAutoConfiguration:开启自动配置功能,自动配置实现的重点❗

    【3】@ComponentScan:包扫描,将哪些类作为容器注入到 Spring Boot 中

    重点查看 @EnableAutoConfiguration ,查看源码可以看到:

    1. @Target(ElementType.TYPE)
    2. @Retention(RetentionPolicy.RUNTIME)
    3. @Documented
    4. @Inherited
    5. 1@AutoConfigurationPackage // 将主启动类所在的包的组件注入
    6. 2@Import(AutoConfigurationImportSelector.class)// 自动配置的原理
    7. public @interface EnableAutoConfiguration {}
    8. 复制代码

    查看 @AutoConfigurationPackage ,查看源码:

    1. @Target(ElementType.TYPE)
    2. @Retention(RetentionPolicy.RUNTIME)
    3. @Documented
    4. @Inherited
    5. // 导入的是这个类,继续查看
    6. @Import(AutoConfigurationPackages.Registrar.class)
    7. public @interface AutoConfigurationPackage {}
    8. //===========================================================================
    9. // AutoConfigurationPackage 所导入的类
    10. static class Registrar implements ImportBeanDefinitionRegistrar, DeterminableImports {
    11. @Override
    12. public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
    13. // 这个方法会将主启动类所在包下的组件加入容器,
    14. // 在此处打个端点调试运行,可以看到 getPackageNames() 即为主启动类所在的包下的所有包名
    15. register(registry, new PackageImports(metadata).getPackageNames().toArray(new String[0]));
    16. }
    17. @Override
    18. public Set<Object> determineImports(AnnotationMetadata metadata) {
    19. return Collections.singleton(new PackageImports(metadata));
    20. }
    21. }
    22. 复制代码

    ❗ 自动配置的重点:@Import(AutoConfigurationImportSelector.class),重点查看这个类 AutoConfigurationImportSelector

    1. public class AutoConfigurationImportSelector implements ...... {
    2. // 获取自动配置类的集合,这些集合即为要注入的组件,排除掉部分组件后,再封装返回最后要注入的组件
    3. protected AutoConfigurationEntry getAutoConfigurationEntry(AnnotationMetadata annotationMetadata){
    4. if (!isEnabled(annotationMetadata)) {
    5. return EMPTY_ENTRY;
    6. }
    7. AnnotationAttributes attributes = getAttributes(annotationMetadata);
    8. // 【1】获取一系列自动配置类集合,在这里打个断点,可以查看获取到的自动配置类有哪些。可以查看 22 行的源码
    9. List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes);
    10. configurations = removeDuplicates(configurations);
    11. // 【2】被设置禁用自动配置的类
    12. Set<String> exclusions = getExclusions(annotationMetadata, attributes);
    13. checkExcludedClasses(configurations, exclusions);
    14. configurations.removeAll(exclusions);
    15. configurations = getConfigurationClassFilter().filter(configurations);
    16. fireAutoConfigurationImportEvents(configurations, exclusions);
    17. // 【3】封装返回
    18. return new AutoConfigurationEntry(configurations, exclusions);
    19. }
    20. // 获取一系列自动配置类集合
    21. protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
    22. // 获取候选的自动配置类集合,通过工厂类完成,继续查看 SpringFactoriesLoader.loadFactoryNames() 源码,跳转到第 32 行
    23. List<String> configurations = SpringFactoriesLoader.loadFactoryNames(getSpringFactoriesLoaderFactoryClass(),
    24. getBeanClassLoader());
    25. Assert.notEmpty(configurations, "No auto configuration classes found in META-INF/spring.factories. If you "
    26. + "are using a custom packaging, make sure that file is correct.");
    27. return configurations;
    28. }
    29. }
    30. //===========================================================================
    31. public static List<String> loadFactoryNames(Class factoryType, @Nullable ClassLoader classLoader) {
    32. String factoryTypeName = factoryType.getName();
    33. // 调用了 loadSpringFactories,继续查看源码,跳转到第 38 行
    34. return loadSpringFactories(classLoader).getOrDefault(factoryTypeName, Collections.emptyList());
    35. }
    36. //===========================================================================
    37. // 为了方便阅读,逻辑有些许改动
    38. private static Map<String, List<String>> loadSpringFactories(@Nullable ClassLoader classLoader) {
    39. MultiValueMap<String, String> result = new LinkedMultiValueMap<>();
    40. // 可以看到,urls 后面被反复操作并将结果封装为 result 返回,可见 urls 是获取候选的自动配置类的重点
    41. // 查看源码,得知 FACTORIES_RESOURCE_LOCATION 的值为 META-INF/spring.factories
    42. Enumeration<URL> urls = (classLoader != null ?
    43. classLoader.getResources(FACTORIES_RESOURCE_LOCATION) :
    44. ClassLoader.getSystemResources(FACTORIES_RESOURCE_LOCATION));
    45. while (urls.hasMoreElements()) {
    46. URL url = urls.nextElement();
    47. UrlResource resource = new UrlResource(url);
    48. Properties properties = PropertiesLoaderUtils.loadProperties(resource);
    49. for (Map.Entry entry : properties.entrySet()) {
    50. String factoryTypeName = ((String) entry.getKey()).trim();
    51. for (String factoryImplementationName : StringUtils.commaDelimitedListToStringArray((String) entry.getValue())) {
    52. result.add(factoryTypeName, factoryImplementationName.trim());
    53. }
    54. }
    55. }
    56. cache.put(classLoader, result);
    57. return result;
    58. }
    59. 复制代码

    通过上面的源码查看,我们能够大致地知道 Spring Boot 自动配置的原理为:

    1. 获取META-INF/spring.factories配置文件,从而得到一系列的候选组件
    2. 排除掉部分候选组件,最后封装返回

    META-INF/spring.factories 在哪呢?一般能够实现自动配置功能的依赖都会有这个文件,而 Spring Boot 就提供了自动配置功能,查看 spring-boot-autoconfigure-2.3.7.RELEASE.jar,可以看到:

    查看文件,果不其然有各种配置类定义:

    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.context.ConfigurationPropertiesAutoConfiguration,\
    10. org.springframework.boot.autoconfigure.context.LifecycleAutoConfiguration,\
    11. ......
    12. 复制代码

    但是,这些自动配置类是否会全部加载,并且其中定义的 Bean 是否也会一并注入到容器中呢?

    Spring Boot 是按需加载的,我们打开 AopAutoConfiguration的源码查看一下

    1. @Configuration(proxyBeanMethods = false)
    2. @ConditionalOnProperty(prefix = "spring.aop", name = "auto", havingValue = "true", matchIfMissing = true)
    3. public class AopAutoConfiguration {
    4. @Configuration(proxyBeanMethods = false)
    5. @ConditionalOnClass(Advice.class)
    6. static class AspectJAutoProxyingConfiguration {}
    7. }
    8. 复制代码

    可以看到使用了注解@ConditionalOnXxx,也就是说,Spring Boot 会根据条件选择是否加载这些配置类、组件到容器中。

    因此,当我们需要启用某一块功能时,可以查看是否需要引入相应的 jar 包、启用特定的配置项。

    并且有些自动配置类还绑定了配置类XxxPropertiesXxxProperties又与 Spring Boot配置文件绑定,而自动配置类注入的组件默认就是从 XxxProperties获取默认值进行设置。如

    1. @AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE)
    2. @Configuration(proxyBeanMethods = false)
    3. @ConditionalOnWebApplication(type = Type.SERVLET)
    4. @ConditionalOnClass(DispatcherServlet.class)
    5. @AutoConfigureAfter(ServletWebServerFactoryAutoConfiguration.class)
    6. public class DispatcherServletAutoConfiguration {
    7. @Configuration(proxyBeanMethods = false)
    8. @Conditional(DefaultDispatcherServletCondition.class)
    9. @ConditionalOnClass(ServletRegistration.class)
    10. @EnableConfigurationProperties(WebMvcProperties.class)
    11. protected static class DispatcherServletConfiguration {
    12. @Bean(name = DEFAULT_DISPATCHER_SERVLET_BEAN_NAME)
    13. public DispatcherServlet dispatcherServlet(WebMvcProperties webMvcProperties) {
    14. DispatcherServlet dispatcherServlet = new DispatcherServlet();
    15. // 获取配置信息:webMvcProperties
    16. dispatcherServlet.setDispatchOptionsRequest(webMvcProperties.isDispatchOptionsRequest());
    17. dispatcherServlet.setDispatchTraceRequest(webMvcProperties.isDispatchTraceRequest());
    18. dispatcherServlet.setThrowExceptionIfNoHandlerFound(webMvcProperties.isThrowExceptionIfNoHandlerFound());
    19. dispatcherServlet.setPublishEvents(webMvcProperties.isPublishRequestHandledEvents());
    20. dispatcherServlet.setEnableLoggingRequestDetails(webMvcProperties.isLogRequestDetails());
    21. return dispatcherServlet;
    22. }
    23. }
    24. }
    25. 复制代码

    总结

    SpringBoot 主启动类的注解@SpringBootApplication,其实是组合注解,其组成为:

    • @SpringBootConfiguration 表明是一个配置类
    • @EnableAutoConfiguration 启用自动配置功能,自动配置的核心
      • @AutoConfigurationPackage 扫描主启动类所在包下的所有组件
      • @Import(AutoConfigurationImportSelector.class) 完成自动配置功能的类的核心组件
    • @ComponentScan 包扫描配置

    SpringBoot 的自动配置流程大致为:

    1. 启动 SpringBoot 程序,SpringBoot 会扫描 jar 包下的 META_INF/spring.factories中定义的一系列自动配置
    2. 将这些自动配置类扫描进来后,排除掉部分自动配置类
    3. 按需加载,根据条件@ConditionalXxx决定是否加载该自动配置类
      1. 通常也会绑定配置类XxxProperties.class
    4. 生效的配置文件会往容器中注入各种组件
    5. 由于通过自动配置注入了各种组件,所以某个场景的功能所需的配置就被设置好了,可以直接使用。例如 Spring MVC、DataSource 等等
    6. 可以自定义配置
      1. 使用@Bean覆盖掉 SpringBoot 底层使用的组件
      2. 查看底层XxxProperties源码使用到的哪些配置项,在配置文件自行配置
      3. 查看官方文档中各个配置项的说明 Common Application Properties

  • 相关阅读:
    Apache Ignite 作为 MySql的加速层
    《向量数据库》——向量数据库Milvus Cloud 和Dify比较
    小说推文、短剧cps推广和带影达人好做吗
    九、Nacos集群搭建
    C/C++内存管理
    操作系统——复习笔记
    C Primer Plus(6) 中文版 第2章 C语言概述 2.2 示例解释
    淘宝/天猫获取购买到的商品订单物流信息 API分享
    NAT地址转换,路由器作为出口设备,实现负载分担
    LeetCode 2343. 裁剪数字后查询第 K 小的数字 暴力+语法考察
  • 原文地址:https://blog.csdn.net/YYniannian/article/details/125995688