IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> Java知识库 -> SpringBoot自动配置原理(超详细) -> 正文阅读

[Java知识库]SpringBoot自动配置原理(超详细)

0. 简介

学习SpringBoot自动配置原理的时候,学得很迷糊,于是自己搜索资料整理思路。本文为作者一天的成果,搜索查看了大量的资料(建议有的时候还是要自己打打断点看看源码比较好),发现网上大部分都讲得很没用,或者讲得很浅。源代码有一些我也无法理解,但是通过打断点获知其方法调用的返回结果渐渐搞清楚方法的作用,整理如下。
参开资料如下:
一文搞懂🔥SpringBoot自动配置原理
Spring Boot面试杀手锏————自动配置原理
@Import注解的作用

1. SprintBoot的自动配置原理:

SpringBoot启动类的注解@SpringBootApplication中,起到关键作用的注解是@EnableAutoConfiguration。而@EnableAutoConfiguration也是一个复合注解,它包含了@AutoConfigurationPackage@Import({AutoConfigurationImportSelector.class})

  • @AutoConfigurationPackage

    @Target({ElementType.TYPE})
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    @Inherited
    @Import({Registrar.class})
    

    @Import 为spring的注解,用来导入配置类或者一些需要前置加载的类,通过进一步点击查看源代码,可以看到Registrar.class实现了ImportBeanDefinitionRegistrar接口,通过@Import 导入该接口的实现类会执行重写的registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry)方法。

    static class Registrar implements ImportBeanDefinitionRegistrar, DeterminableImports {
        Registrar() {
        }
    
        public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
            AutoConfigurationPackages.register(registry, (String[])(new AutoConfigurationPackages.PackageImports(metadata)).getPackageNames().toArray(new String[0]));
    		//在这里打断点观察
        }
    
        public Set<Object> determineImports(AnnotationMetadata metadata) {
            return Collections.singleton(new AutoConfigurationPackages.PackageImports(metadata));
        }
    }
    

    通过断点测试,我们可以发现(String[])(new AutoConfigurationPackages.PackageImports(metadata)).getPackageNames().toArray(new String[0])的结果是当前启动类所在的包名。而AutoConfigurationPackages.register(BeanDefinitionRegistry registry, String... packageNames)方法的作用是将启动类所在包名/路径名加入容器中,一个专门记录packageNames的集合里。容器会扫描该集合下的路径名,从而去导入相应的组件。

    即,@AutoConfigurationPackage 就是将主配置类(@SpringBootApplication 标注的类)所在的包下面所有的组件都扫描注冊到 spring 容器中。

  • @Import({AutoConfigurationImportSelector.class}):

    @Import 注解导入的该类,实现了DeferredImportSelector接口,而DeferredImportSelector又实现了ImportSelector接口,因此该类实现了ImportSelector接口。通过@Import导入的该接口的实现类会执行重写的selectImports(AnnotationMetadata annotationMetadata)方法,该方法的返回值是String[]类型,即需要自动配置/导入Spring容器中的类的全限定名。

    public String[] selectImports(AnnotationMetadata annotationMetadata) {
        if (!this.isEnabled(annotationMetadata)) {
            return NO_IMPORTS;
        } else {
            AutoConfigurationImportSelector.AutoConfigurationEntry autoConfigurationEntry = this.getAutoConfigurationEntry(annotationMetadata);
            return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());
        }
    }
    

    NO_IMPORTSnew String[0],因此我们关注else部分,而else部分只有两条语句,重点在第一条语句对于getAutoConfigurationEntry(AnnotationMetadata annotationMetadata)的调用。

    protected AutoConfigurationImportSelector.AutoConfigurationEntry getAutoConfigurationEntry(AnnotationMetadata annotationMetadata) {    
    	if (!this.isEnabled(annotationMetadata)) {
            return EMPTY_ENTRY;
        } else {
            AnnotationAttributes attributes = this.getAttributes(annotationMetadata);
            
            // 获取所有jar包下的 META-INF/spring.factories 文件定义中,对应的
            // EnableAutoConfiguration 部分作为候选配置
            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);
            
            // 这里的作用是,过滤掉一些不应该生效的自动配置
            // 部分自动配置类会添加注解:@OnXXXCondition,表示满足xx条件下才可以加入自动配置
            configurations = this.getConfigurationClassFilter().filter(configurations);
            this.fireAutoConfigurationImportEvents(configurations, exclusions);
            return new AutoConfigurationImportSelector.AutoConfigurationEntry(configurations, exclusions);
        }
    

    这里点进去getCandidateConfigurations(annotationMetadata, annotationAttributes)方法,可以看到方法体中调用另外一个方法loadFactoryNames(factoryType, classLoader),其中factoryType的值为EnableAutoConfigurationclassLoader的值为AppClassLoader(打断点可以观察到)。

    在这里插入图片描述

    loadFactoryNames(factoryType, classLoader)最终返回另外一个方法调用的筛选结果,即loadSpringFactories(classLoader),点进去该方法的源码,在该位置打断点进行观察:

    在这里插入图片描述

    可以看到返回值result的结果是一个key-value保存的Map,展开如下。其中就有我们需要并且由loadFactoryNames(factoryType, classLoader)方法返回的EnableAutoConfiguration部分。loadSpringFactories(classLoader)遍历了所有jar包路径下的META-INF/spring.factories文件,这些文件给Spring中的容器组件进行了相关的设置,并汇总成为一个Map集合,最后返回。

    在这里插入图片描述

    从内往外推的顺序如下:

    • loadSpringFactories()方法加载所有jar包下的META-INF/spring.factories文件中的配置,并将其整理为Map,传递出去;

    • loadFactoryNames()接收到loadSpringFactories()的调用结果,挑选出key为EnableAutoConfiguration的部分,即需要自动配置的部分,将其返回;

    • getAutoConfigurationEntry()获得META-INF/spring.factories需要自动配置的部分,在根据@OnXXXCondition注解筛选排除部分不生效的自动配置,将其返回给selectImports()方法;

    • 点进去Map中的EnableAutoConfiguration,可以看到如下全为"xxxxAutoConfiguration"格式的字符串,这就是selectImports()方法返回的需要添加到Spring容器中管理的组件的全限定类名,作用类似于使用@Import引入了@Configuration注解的类,通过这种方式实现自动配置。

      在这里插入图片描述

2. 为什么有的自动配置没有生效/为什么有的配置被过滤掉:

在加载自动配置类的时候,并不是将spring.factories的配置全部加载进来,而是通过@Conditional等注解的判断进行动态加载。@Conditional其实是spring底层注解,意思就是根据不同的条件,来进行自己不同的条件判断,如果满足指定的条件,那么配置类里边的配置才会生效。

常见的Conditional注解:

  • @ConditionalOnClass : classpath中存在该类时起效

  • @ConditionalOnMissingClass : classpath中不存在该类时起效

  • @ConditionalOnBean: DI容器中存在该类型Bean时起效

  • @ConditionalOnMissingBean : DI容器中不存在该类型Bean时起效

  • @ConditionalOnSingleCandidate : DI容器中该类型Bean只有一个或@Primary的只有一个时起效

  • @ConditionalOnExpression : SpEL表达式结果为true时

  • @ConditionalOnProperty : 参数设置或者值一致时起效

  • @ConditionalOnResource : 指定的文件存在时起效

  • @ConditionalOnJndi : 指定的JNDI存在时起效

  • @ConditionalOnJava : 指定的Java版本存在时起效

  • @ConditionalOnWebApplication : Web应用环境下起效

  • @ConditionalOnNotWebApplication : 非Web应用环境下起效

  Java知识库 最新文章
计算距离春节还有多长时间
系统开发系列 之WebService(spring框架+ma
springBoot+Cache(自定义有效时间配置)
SpringBoot整合mybatis实现增删改查、分页查
spring教程
SpringBoot+Vue实现美食交流网站的设计与实
虚拟机内存结构以及虚拟机中销毁和新建对象
SpringMVC---原理
小李同学: Java如何按多个字段分组
打印票据--java
上一篇文章      下一篇文章      查看所有文章
加:2022-02-07 13:35:18  更:2022-02-07 13:36:18 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/24 11:32:51-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码