SpringBoot自动装配原理解析

首先对于一个SpringBoot工程来说,最明显的标志的就是 @SpringBootApplication它标记了这是一个SpringBoot工程,所以今天的 SpringBoot自动装配原理也就是从它开始说起。

自动装配流程

首先我们来看下@SpringBootApplication 这个注解的背后又有什么玄机呢,我们按下 ctrl + 鼠标左键,轻轻的点一下,此时见证奇迹的时刻..

我们看到如下优雅的代码:

SpringBoot自动装配原理解析
这其中有两个比较容易引起我们注意的地方,一个是 @SpringBootConfiguration注解,另一个是 @EnableAutoConfiguration注解;之所以说这个两个注解比较吸引我们的眼球, 不是因为它们长大的好看,而是因为其他的注解太难看了(主要是因为其他的注解我们都是比较熟悉,即使不知道他们是干什么的,可以肯定更自动装配是没有关系的)。 然后我们又伸出了邪恶的小手,开启了熟悉的操作,按下了Ctrt + 鼠标左键,瞪着色咪咪的小眼睛,瞳孔放大了百倍等待着奇迹的出现… 擦… 擦…擦…

SpringBoot自动装配原理解析
什么也没有…

那我要你有何用,这么顶级的世界级的开源项目,怎么会让一个没用的家伙存在呢? 于是动用了上亿的脑细胞大军,经过复杂的运算,得出了一个不靠谱的结论: 它可能使用来标记这是一个SpringBoot工程的配置。因为 SpringBootConfiguration翻译过来就是SpringBoot的配置,于是心中又是几万只羊驼在万马奔腾,大漠飞扬。

气定神闲之后,秉承着·失败是成功之母”的信念, 熟练的左手行云流水般的按下了 Ctrl + Table 键,回到了最初的的地方。眼睛盯着 @EnableAutoConfiguration ,环顾左右,在地址栏输入了谷歌翻译, 结果显示 自动装配。我找的就是你,真是众里寻他千百度,那人却在灯火阑珊处。 熟练的按下了 Ctrl +左键,迫不及待的想要进入; 心里默默背诵起了《桃花源记》的经典诗句 ∶

林尽水源,便得一山,山有小口,仿佛若有光。便舍船,从口入。初极狭,才通人。复行数十步,豁然开朗

SpringBoot自动装配原理解析
此时此刻心情愉悦,有过前面的经历之后,在面对新的世界时候,我们淡定了许多。 此时大脑高速运转,没有再纠结,直捣黄龙,进入了 AutoConfigurationImportSelector.class 类,因为谷歌翻译告诉我们,这个是 自动配置导入选择器。 于是我们发现了—片新天地
public class AutoConfigurationImportSelector implements DeferredImportSelector, BeanClassLoaderAware,
        ResourceLoaderAware, BeanFactoryAware, EnvironmentAware, Ordered {

    @Override
    public String[] selectImports(AnnotationMetadata annotationMetadata) {
        if (!isEnabled(annotationMetadata)) {
            return NO_IMPORTS;
        }
                // 获取自动配置的实体
        AutoConfigurationEntry autoConfigurationEntry = getAutoConfigurationEntry(annotationMetadata);
        return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());
    }

        // 具体用来加载自动配置类得方法
    protected AutoConfigurationEntry getAutoConfigurationEntry(AnnotationMetadata annotationMetadata) {
        if (!isEnabled(annotationMetadata)) {
            return EMPTY_ENTRY;
        }
        AnnotationAttributes attributes = getAttributes(annotationMetadata);
                // 获取候选的配置类,即使后宫佳丽三千,也是要筛选的
        List<string> configurations = getCandidateConfigurations(annotationMetadata, attributes);
                // &#x6839;&#x636E;&#x60C5;&#x51B5;&#xFF0C;&#x81EA;&#x52A8;&#x914D;&#x7F6E;&#x9700;&#x8981;&#x7684;&#x914D;&#x7F6E;&#x7C7B;&#x548C;&#x4E0D;&#x9700;&#x8981;&#x7684;&#x914D;&#x7F6E;&#x4E86;
        configurations = removeDuplicates(configurations);
        Set<string> exclusions = getExclusions(annotationMetadata, attributes);
        checkExcludedClasses(configurations, );
        configurations.removeAll(exclusions);
        configurations = getConfigurationClassFilter().filter(configurations);
        fireAutoConfigurationImportEvents(configurations, exclusions);
                // &#x8FD4;&#x56DE;&#x6700;&#x7EC8;&#x9700;&#x8981;&#x7684;&#x914D;&#x7F6E;
        return new AutoConfigurationEntry(configurations, exclusions);
    }
}
</string></string>

而这个自动配置的实体 AutoConfigurationEntry里面有两个属性, configurationsexclusions

    protected static class AutoConfigurationEntry {
                // &#x7528;&#x6765;&#x5B58;&#x50A8;&#x9700;&#x8981;&#x7684;&#x914D;&#x7F6E;&#x9879;
        private final List<string> configurations;
                // &#x7528;&#x6765;&#x5B58;&#x50A8;&#x6392;&#x9664;&#x7684;&#x914D;&#x7F6E;&#x9879;
        private final Set<string> exclusions;

        private AutoConfigurationEntry() {
            this.configurations = Collections.emptyList();
            this.exclusions = Collections.emptySet();
        }
        }
</string></string>

在后面可以看到 getAutoConfigurationEntry&#xFF08;&#xFF09;方法返回了一个对象 return new AutoConfigurationEntry(configurations, exclusions);这里也就是把我们需要的配置都拿到了。

那他是怎么拿到的候选的配置类呢? 我们接着看这个获取候选配置类的方法 List<string> configurations = getCandidateConfigurations(annotationMetadata, attributes);</string>
进到方法后我们看到下面这个方法具体获取候选配置类的方法内容

SpringBoot自动装配原理解析

这里我们跟着断点去走,首先进入 getSpringFactoriesLoaderFactoryClass()方法

    protected Class<?> getSpringFactoriesLoaderFactoryClass() {
                // &#x8FD4;&#x56DE;&#x7684;&#x662F;EnableAutoConfiguration&#x5B57;&#x8282;&#x7801;&#x5BF9;&#x8C61;
        return EnableAutoConfiguration.class;
    }

接着我们在进入 getBeanClassLoader()方法,这里就是一个类加载器

protected ClassLoader getBeanClassLoader() {
        return this.beanClassLoader;
    }

最后我们在进入 loadFactoryNames()方法,这个方法就是根据刚才的字节码文件和类加载器来找到候选的配置类。传递过来的字节码

    public static List<string> loadFactoryNames(Class<?> factoryType, @Nullable ClassLoader classLoader) {
        ClassLoader classLoaderToUse = classLoader;
        if (classLoaderToUse == null) {
            classLoaderToUse = SpringFactoriesLoader.class.getClassLoader();
        }
                // &#x83B7;&#x53D6;&#x7684;EnableAutoConfiguration.class&#x7684;&#x6743;&#x9650;&#x5B9A;&#x540D;
                //org.springframework.boot.autoconfigure.EnableAutoConfiguration
        String factoryTypeName = factoryType.getName();
        return loadSpringFactories(classLoaderToUse).getOrDefault(factoryTypeName, Collections.emptyList());
    }
</string>

如下图:

SpringBoot自动装配原理解析

最后通过 loadSpringFactories()来获取到所有的配置类

    private static Map<string, list<string>> loadSpringFactories(ClassLoader classLoader) {
                // &#x7F13;&#x5B58;&#x52A0;&#x8F7D;&#x7684;&#x914D;&#x7F6E;&#x7C7B;
        Map<string, list<string>> result = cache.get(classLoader);
        if (result != null) {
            return result;
        }

        result = new HashMap<>();
        try {
                        // &#x53BB;&#x8D44;&#x6E90;&#x76EE;&#x5F55;&#x4E0B;&#x627E;
            Enumeration<url> urls = classLoader.getResources(FACTORIES_RESOURCE_LOCATION);
            while (urls.hasMoreElements()) {
                URL url = urls.nextElement();
                UrlResource resource = new UrlResource(url);
                Properties properties = PropertiesLoaderUtils.loadProperties(resource);
                for (Map.Entry<?, ?> entry : properties.entrySet()) {
                    String factoryTypeName = ((String) entry.getKey()).trim();
                    String[] factoryImplementationNames =
                            StringUtils.commaDelimitedListToStringArray((String) entry.getValue());
                    for (String factoryImplementationName : factoryImplementationNames) {
                        result.computeIfAbsent(factoryTypeName, key -> new ArrayList<>())
                                .add(factoryImplementationName.trim());
                    }
                }
            }

            // Replace all lists with unmodifiable lists containing unique elements
            result.replaceAll((factoryType, implementations) -> implementations.stream().distinct()
                    .collect(Collectors.collectingAndThen(Collectors.toList(), Collections::unmodifiableList)));
                        // &#x52A0;&#x8F7D;&#x5B8C;&#x6210;&#x653E;&#x5230;&#x7F13;&#x5B58;&#x4E2D;
            cache.put(classLoader, result);
        }
        catch (IOException ex) {
            throw new IllegalArgumentException("Unable to load factories from location [" +
                    FACTORIES_RESOURCE_LOCATION + "]", ex);
        }
                // &#x8FD4;&#x56DE;&#x52A0;&#x8F7D;&#x5230;&#x7684;&#x914D;&#x7F6E;&#x7C7B;
        return result;
    }
</url></string,></string,>

这里我们要看下怎么从资源目录下 FACTORIES_RESOURCE_LOCATION 加载的。下面是加载配置文件的路径:

SpringBoot自动装配原理解析

也就是项目启动的时候会去加载所有 META-INF 下的所有的 spring.factories 文件,我们搜一下这个这个文件,我搭建的是一个很简单的 SpringBoot 工程,它会去这几个 jar 里面找相关的配置类

SpringBoot自动装配原理解析

但是最后自动装配的类是这个 spring-boot-autoconfigure-2.4.3.RELEASE.jar

SpringBoot自动装配原理解析

而根据 EnabLeAutoConfiguration.class字节码加载的配置类就只有这118自动配置类

SpringBoot自动装配原理解析
小结
实际上SpringBoot的自动装配原理,其实就是在项目启动的时候去加载META-INF下的 spring.factories 文件,好像也没有那么高大上。当然在启动的过程中还会有其他的配置项的加载,这里咱么直说了自动装配的加载过程。希望对大家可以有所启发。
问题∶ 明白了SpringBoot的自动装配原理, 如果我们需要让项目启动的时候就加载我们自定义的配置类, 该如何写呢?

Original: https://www.cnblogs.com/reminis/p/14488795.html
Author: 小懒编程日记
Title: SpringBoot自动装配原理解析

原创文章受到原创版权保护。转载请注明出处:https://www.johngo689.com/571494/

转载文章受原作者版权保护。转载请注明原作者出处!

(0)

大家都在看

  • 【力扣】523. 连续的子数组和

    给你一个整数数组 nums 和一个整数 k ,编写一个函数来判断该数组是否含有同时满足下述条件的连续子数组:子数组大小 至少为 2 ,且子数组元素总和为 k 的倍数。如果存在,返回…

    Java 2023年6月8日
    042
  • springboot1.5.4 log4j

    resources下面添加: log4j.properties: # log4j.rootCategory=INFO, stdout, file, errorfile log4j….

    Java 2023年5月30日
    0115
  • fastposter发布1.4.5 跨语言的海报生成器

    fastposter发布1.4.5 跨语言的海报生成器 v1.4.5 增加了右键菜单,修复了跨域bug 一分钟完成海报开发任务 future: 增加了右键菜单 删除 图层上移 图层…

    Java 2023年6月5日
    0110
  • 《二十三种设计模式》通俗的创建型之单例设计模式

    单例模式(Singleton Pattern)是Java中 最简单的设计模式之一。 这种模式涉及到一个 单一的类,该类负责创建自己的对象,同时确保只有 单个对象被创建。这个类提供了…

    Java 2023年6月7日
    0104
  • Node.js(四)json

    html > 汽车管理系统 汽车管理系统 {{car.id}} {{car.name}} {{car.price}} {{car.speed}} {{car.color}} …

    Java 2023年6月15日
    063
  • Java:字符分割

    Java:字符分割 // 分割方法String str = “|||||||||||”;log.info(StringUtils.split(str,&#8…

    Java 2023年5月29日
    058
  • Node更丝滑的打开方式

    Node更丝滑的打开方式 1. 使用背景 最近前端的一个项目,使用gulp作为工程化。在运行过程中出现如下错误 gulp[3192]: src\node_contextify.cc…

    Java 2023年6月13日
    073
  • VMware16安装Ubuntu 18.04 LTS

    一、准备工作 VMware16安装: 下载地址:https://download3.vmware.com/software/WKST-1623-WIN-New/VMware-wor…

    Java 2023年6月9日
    078
  • NO3系统升级-资产棚卸

    Powered by 博客园 | | | | 发表于2016-03-02 09:03 大哉乾元万物资始 阅读(129 ) 评论() 编辑 Original: https://www…

    Java 2023年6月8日
    080
  • C++基础-程序4区

    内存分区模型 C++中程序在执行时,将程序大方向分为4个区域 代码区:存放函数体的二进制代码,由系统操作进行管理 全局区:存放全局变量,静态变量和常量 栈区:由编译器自动分配和释放…

    Java 2023年6月5日
    070
  • java selenium (十) 操作浏览器

    本篇文章介绍selenium 操作浏览器 操作cookie Original: https://www.cnblogs.com/TankXiao/p/5260557.htmlAut…

    Java 2023年5月29日
    0101
  • Java应用工程结构

    分层的本质是关注点分离,隔离对下层的变化,可以简化复杂性,使得层次结构更加清晰。 1. 主流分层结构介绍 目前业界存在两种主流的应用工程结构:一种是阿里推出的《Java开发手册》中…

    Java 2023年5月29日
    089
  • 深入MySQL(二):MySQL的数据类型

    对于MySQL中的数据类型的选择,不同的数据类型看起来可能是相同的效果,但是其实很多时候天差地别。本章从MySQL中的 常用类型出发,结合 类型选择的常见错误,贯彻MySQL的常用…

    Java 2023年6月7日
    0101
  • 《Apache RocketMQ 深入浅出》系列文章

    Apache RocketMQ是一个纯Java、分布式、队列模型的开源消息中间件,前身是MetaQ,是阿里参考Kafka特点研发的一个队列模型的消息中间件,后开源给apache基金…

    Java 2023年5月29日
    074
  • 操作系统 文件管理 实验(C语言)

    实验要求和提示 1、利用内存或外存存储结构实现文件系统的树型目录结构,并通过交互式命令完成文件与目录管理。 2 、至少提供如下命令(大小写都能识别):MD(创建空目录)、CD(切换…

    Java 2023年6月5日
    093
  • 1_day01_操作系统安装

    操作系统安装 内容介绍 1.制作U盘启动器 2.备份驱动 3.安装操作系统 4.驱动更新 5.依赖库检测 6.系统漏洞修复 7.系统布局优化 一、制作U盘启动器 1.1 下载老毛桃…

    Java 2023年6月8日
    078
亲爱的 Coder【最近整理,可免费获取】👉 最新必读书单  | 👏 面试题下载  | 🌎 免费的AI知识星球