首页 > 编程语言 >源码拆解SpringBoot的自动配置机制

源码拆解SpringBoot的自动配置机制

时间:2024-07-26 12:26:31浏览次数:13  
标签:SpringBoot 配置 private class AutoConfigurationEntry 拆解 注解 源码 configurations

SpringBoot相比于Spring系列的前作,很大的一个亮点就是将配置进行了简化,引入了自动化配置,仅靠几个注解和yml文件就取代了之前XML的繁琐配置机制,这也是SpringBoot的独有特点,下面我们从源码角度,一点点拆开自动配置的机制是如何实现的。

从@SpringBootApplication开始

从SpringBoot项目初始类上的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 {
    @AliasFor(
        annotation = EnableAutoConfiguration.class
    )
    Class<?>[] exclude() default {};

    @AliasFor(
        annotation = EnableAutoConfiguration.class
    )
    String[] excludeName() default {};

    @AliasFor(
        annotation = ComponentScan.class,
        attribute = "basePackages"
    )
    String[] scanBasePackages() default {};

    @AliasFor(
        annotation = ComponentScan.class,
        attribute = "basePackageClasses"
    )
    Class<?>[] scanBasePackageClasses() default {};

    @AliasFor(
        annotation = ComponentScan.class,
        attribute = "nameGenerator"
    )
    Class<? extends BeanNameGenerator> nameGenerator() default BeanNameGenerator.class;

    @AliasFor(
        annotation = Configuration.class
    )
    boolean proxyBeanMethods() default true;
}

发现SpringBootApplication注解是一个入口,里面涵盖了很多关于SpringBoot的启动注解,和自动化配置相关的主要是@EnableAutoConfiguration@ComponentScan

  • @EnableAutoConfiguration:这个注解是Spring Boot自动配置的入口点。它指示Spring Boot启动自动配置过程。
  • @ComponentScan:允许你指定一个或多个包,Spring容器将扫描这些包以及其子包中带有@Component@Service@Repository@Controller等注解的类。

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

作为主要管控SpringBoot自动配置化的复合注解,抛开常用的,@EnableAutoConfiguration中主要的注解是@AutoConfigurationPackage
@Import其中@AutoConfigurationPackage 用于指示Spring Boot自动配置应该从哪个包开始扫描,是一个Spring扫描类的注解。 @Import则是自动配置的核心逻辑入口,源码中通过@Import导入了一个名为 AutoConfigurationImportSelector的类,而这个类 就是自动配置的加载选择器。

自动配置类的筛选流程

下面看一下AutoConfigurationImportSelector这个选择器主要干了什么:

public class AutoConfigurationImportSelector implements DeferredImportSelector, BeanClassLoaderAware, ResourceLoaderAware, BeanFactoryAware, EnvironmentAware, Ordered {
  
    private static final AutoConfigurationEntry EMPTY_ENTRY = new AutoConfigurationEntry();
    private static final String[] NO_IMPORTS = new String[0];
    private static final Log logger = LogFactory.getLog(AutoConfigurationImportSelector.class);
    //……省略部分成员变量

   protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
        List<String> configurations = SpringFactoriesLoader.loadFactoryNames(this.getSpringFactoriesLoaderFactoryClass(), this.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;
    }
   //……省略部分方法

   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);
        }
    } 

}

这里AutoConfigurationImportSelector的内容太多,我只摘取了与自动配置相关的逻辑,关于自动化配置加载,主要是getAutoConfigurationEntry(AutoConfigurationMetadata autoConfigurationMetadata,AnnotationMetadata annotationMetadata)方法,该方法决定哪些自动配置类应该被导入到Spring的Bean定义中。方法的入参为AutoConfigurationMetadataAnnotationMetadata对象,这两个参数提供了自动配置的元数据和注解的元数据,对接外部配置注解与加载机制的连接。后续的逻辑处理需要用到配置信息。

核心流程

首先根据外部参数annotationMetadata来确定整个系统是否开启了自动化加载机制,这里其实是判断@EnableAutoConfiguration注解或spring.boot.enableautoconfiguration属性的设置,在SpringBoot中可以通过这两种方式决定是否启用自动化配置

然后读取@EnableAutoConfiguration注解的控制信息,根据配置信息进行确定加载哪些自动化配置类(携带@Configuration注解的类或者XML文件),排除哪些不需要的配置类(@EnableAutoConfiguration注解指定控制排除一些自动配置);

后面进行容错机制,包含去除重复的类、筛选出要排除的自动化配置类。根据自动配置元数据进一步过滤配置列表;

触发自动配置导入事件,允许其他监听器在自动配置类被导入之前进行操作后创建并返回一个AutoConfigurationEntry对象,它包含了最终确定要导入的自动配置类列表和被排除的类列表。 

返回对象的处理

返回的AutoConfigurationEntry 实体对象,它包含了最终确定要导入的自动配置类列表和被排除的类列表。这个对象最终会被注册为Spring容器中的一个Bean(Spring中万物皆bean的思想)

AutoConfigurationEntry 是一个内部类,在AutoConfigurationImportSelector类中定义源码如下:

protected static class AutoConfigurationEntry {
        private final List<String> configurations;
        private final Set<String> exclusions;

        private AutoConfigurationEntry() {
            this.configurations = Collections.emptyList();
            this.exclusions = Collections.emptySet();
        }

        AutoConfigurationEntry(Collection<String> configurations, Collection<String> exclusions) {
            this.configurations = new ArrayList(configurations);
            this.exclusions = new HashSet(exclusions);
        }

        public List<String> getConfigurations() {
            return this.configurations;
        }

        public Set<String> getExclusions() {
            return this.exclusions;
        }
    }

随后Spring的Bean管理会对AutoConfigurationEntry 对象内的候选配置类集合(List<String> configurations)中的类逐一加载,加载中处理每个类上的特殊逻辑, 例如,某个自动配置类的触发条件等等(上篇文章中的LogNoteCondition 的逻辑即是在此处处理的);最后这些自动配置类也会变成bean被加载到Spring的上下文中。被排除的自动配置类(Set<String> exclusions)会被记录,但不会被导入。Spring Boot 会确保这些类在自动配置过程中被忽略。

我们随便启动一个SpringBoot项目,在Debug模式下可以看到处理结束的候选配置类和排除的配置类: 

XXXConfiguration和ConfigurationProperties的注入

debug 里,我们看到了成功装配了AutoConfigurationEntry 对象内的候选配置类,但是这里只是配置类的路径,却还没有配置类的实例对象以及对象内的自动配置值,基于上述的debug,我们选择ServletWebServerFactoryAutoConfiguration进行跟踪

@Configuration(
    proxyBeanMethods = false
)
@AutoConfigureOrder(Integer.MIN_VALUE)
@ConditionalOnClass({ServletRequest.class})
@ConditionalOnWebApplication(
    type = Type.SERVLET
)
@EnableConfigurationProperties({ServerProperties.class})
@Import({BeanPostProcessorsRegistrar.class, ServletWebServerFactoryConfiguration.EmbeddedTomcat.class, ServletWebServerFactoryConfiguration.EmbeddedJetty.class, ServletWebServerFactoryConfiguration.EmbeddedUndertow.class})
public class ServletWebServerFactoryAutoConfiguration {
   //……省略部分代码
}

这里对于ServletWebServerFactoryAutoConfiguration在进行bean加载时需要先处理@EnableConfigurationProperties(ServerProperties.class) ,它的意思是启用指定类的@ConfigurationProperties注解功能;目的是将配置文件中对应的值和 ServerProperties 绑定起来;并把
ServerProperties 加入到 IOC 容器中。

ServerProperties源码:

@ConfigurationProperties(
    prefix = "server",
    ignoreUnknownFields = true
)
public class ServerProperties {
    private Integer port;
    private InetAddress address;
    @NestedConfigurationProperty
    private final ErrorProperties error = new ErrorProperties();
    private ForwardHeadersStrategy forwardHeadersStrategy;
    private String serverHeader;
    private DataSize maxHttpHeaderSize = DataSize.ofKilobytes(8L);
    private Shutdown shutdown;
    @NestedConfigurationProperty
    private Ssl ssl;
    @NestedConfigurationProperty
    private final Compression compression;
    @NestedConfigurationProperty
    private final Http2 http2;
    private final Servlet servlet;

   // ……省略
}

ServerProperties中使用 @ConfigurationProperties注解绑定属性映射文件中的 server 开头的属性。

也就是大部分SpringBoot的yml文件中的:

server:
  port: 8081
  servlet:
    context-path: /test
    encoding:
      charset: UTF-8
      enabled: true
      force: true
spring:
  #省略后面配置

补充:自定义starter与SpringBoot的合并过程

在 上篇自定义starter中我们对于基本启动类,需要创建META-INFO下的spring.factories文件和主启动类中的@ComponentScan(basePackages = "org.example.lognote.*")注解,这里就连上了。

对于外部使用自定义的starter的SpringBoot项目,它在@EnableAutoConfiguration注解时便会触发自动配置类的筛选流程,通过筛选流程中的getAutoConfigurationEntry方法,根据外部starter项目中的spring.factories配置找到其启动类加载到AutoConfigurationEntry 对象中,随后SpringBean加载机制处理AutoConfigurationEntry 对象中的候选配置类时,starter中的启动类自然作为这个外部SpringBoot项目中的一个简单@Configuration被加载。通过starter项目启动类上的@ComponentScan注解将整个starter中的其他bean都加载到SpringBoot项目中的上下文里。

标签:SpringBoot,配置,private,class,AutoConfigurationEntry,拆解,注解,源码,configurations
From: https://blog.csdn.net/qq_40690073/article/details/140652581

相关文章

  • 网站源码装饰公司pbootcms模板网页设计主题
    装饰公司的网站设计分享我很高兴向大家介绍我刚刚制作的装饰公司的网站设计。友好的站点界面,是打动访客的第一步。装饰公司网站的主题网站设计通常需要考虑多个方面,以确保网站能够有效地展示公司形象、吸引潜在客户并提升业务。以下是对装饰公司网站主题设计的详细介绍:一、......
  • 基于SpringBoot+Vue+uniapp的教学资料管理系统(源码+lw+部署文档+讲解等)
    文章目录前言详细视频演示具体实现截图技术栈后端框架SpringBoot前端框架Vue持久层框架MyBaitsPlus系统测试系统测试目的系统功能测试系统测试结论为什么选择我代码参考数据库参考源码获取前言......
  • 基于SpringBoot+Vue+uniapp的旅游推荐系统(源码+lw+部署文档+讲解等)
    文章目录前言详细视频演示具体实现截图技术栈后端框架SpringBoot前端框架Vue持久层框架MyBaitsPlus系统测试系统测试目的系统功能测试系统测试结论为什么选择我代码参考数据库参考源码获取前言......
  • 基于SpringBoot+Vue+uniapp的校园二手书交易平台(源码+lw+部署文档+讲解等)
    文章目录前言详细视频演示具体实现截图技术栈后端框架SpringBoot前端框架Vue持久层框架MyBaitsPlus系统测试系统测试目的系统功能测试系统测试结论为什么选择我代码参考数据库参考源码获取前言......
  • Java毕业设计-基于springboot开发的ONLY在线商城系统设计与开发-代码-毕业论文(附毕设
    文章目录前言一、毕设成果演示(源代码在文末)二、毕设摘要展示1、开发说明2、需求/流程分析3、系统功能结构三、系统实现展示1、用户信息管理2、商品分类管理3、商品信息管理4、轮播图管理四、毕设内容和源代码获取总结Java毕业设计-基于springboot开发的ONLY在线商......
  • Java毕业设计-基于springboot开发的在线课程管理系统-毕业论文(附毕设源代码)
    文章目录前言一、毕设成果演示(源代码在文末)二、毕设摘要展示1、开发说明2、需求/流程分析3、系统功能结构三、系统实现展示1、管理员模块的实现1.1教师信息管理1.2学生信息管理1.3在线课程管理1.4班级分类管理2、教师模块的实现2.1在线课程信息2.2教学计划管理2.......
  • 基于springboot的学科竞赛管理-毕业设计+springboot+VUE
    介绍基于Springboot的学科竞赛管理系统是一款专为学科竞赛活动设计的综合管理平台,旨在提高竞赛组织和参与的效率。该系统分为管理端、教师端和学生端,每个角色均提供了丰富的功能模块,以满足不同用户的需求和职责。技术栈后端技术栈:Springboot+Mysql+Maven前端技术栈:Vue+Html......
  • 基于SpringBoot+Vue的人事系统 毕业设计 springboot+Vue+mysql
    介绍本人事系统基于SpringBoot和Vue框架开发,旨在为企业提供高效、便捷、准确的人事管理解决方案。通过现代化的技术手段,实现了人事数据的集中管理、流程的自动化处理以及信息的实时共享,从而提高企业的人事管理效率和决策科学性。技术栈后端技术栈:Springboot+Mysql+Maven......
  • 数字化时代下高校程序设计类课程综合实践平台-计算机毕业设计源码41020
    摘要在数字化时代浪潮的推动下,高校程序设计类课程的教学与实践面临着前所未有的挑战与机遇。为适应这一时代变革,培养具备创新能力和实践精神的高素质程序设计人才,综合实践平台的设计与开发显得尤为重要。本文旨在探讨高校程序设计类课程综合实践平台的设计与开发,通过深入分析......
  • springboot农产品报价系统-计算机毕业设计源码37300
    基于鸿蒙的农产品报价系统小程序摘 要本研究基于鸿蒙系统,设计开发了一款农产品报价系统小程序,旨在帮助商家与买家更便捷、高效地进行交易。该系统利用鸿蒙系统的优势,实现了跨平台应用程序的开发,同时利用定位技术和数据采集技术,为用户提供了个性化的农产品推荐和交易信息。......