手写自己的Springboot-2-从Servlet容器选择彻底理解自动配置

news2024/11/29 22:46:45

文章目录

    • 在Springboot工程中怎样对Servlet容器进行切换
    • 底层原理猜想,需要做哪些事情
      • 1.pom依赖
      • 2.判断注入哪个bean
      • 3.让使用者能够加载到自动配置类
        • 目前问题及通用处理方式
        • 真实Springboot处理方式
    • 进行测试
    • 关键点总结

如果还没有看第一篇文章的伙伴,建议先看第一篇文章 手写自己的Springboot-1-整合tomcat,该文章在第一篇文章基础上进行扩展.

在Springboot工程中怎样对Servlet容器进行切换

在我们使用Springboot时,如果不想用Tomcat,想用Jetty,应该怎么办呢?
其实很简单,我们只需要把Tomcat的依赖排除掉,然后引入Jetty即可.

在这里插入图片描述

那Springboot底层究竟是怎样处理的呢?

底层原理猜想,需要做哪些事情

首先要明确,Springboot的自动配置其实就是帮我们自动配置注入了很多bean, 比如原来的Spring需要整合Tomcat,一定需要配置一些Tomcat的bean, 需要整合Jetty,就一定需要配置一些Jetty相关的bean.

1.pom依赖

Springboot能够启动Jetty,能够启动tomcat,那它在自己内部工程一定是同时引入这两个依赖的.

在这里插入图片描述
那为什么使用者在切换的时候还需要额外引入Jetty的依赖呢?

其实这里利用了依赖的传递性,在Springboot内部工程pom中,其实对于可选择的依赖加上了<optional>true</optional>,加上该配置后,表示该依赖并不会传递,而tomcat依赖是没有加该配置的,会进行依赖的传递.

如果不理解这部分的maven的知识,可以看一下这个文章Maven常见知识、冲突解决.

在这里插入图片描述

所以,使用者默认是不需要额外引入Tomcat依赖的,而进行Jetty的切换时,需要额外引入Jetty的依赖.

那为什么要这么做呢?为什么不也直接让使用者也都引入这些依赖呢?

对于使用者而言,比如说Servlet容器,其实一般最多都只用一个的,大部分都是Tomcat,那我就不需要引入其他的多余的依赖,那样会使项目变得更加庞大, 还会极大增加依赖冲突的可能性.

2.判断注入哪个bean

知道了怎么进行切换,那现在就应该进行自动配置bean, 那怎么知道应该注入哪个bean呢?是Tomcat的,还是Jetty的?

其实这里也非常简单,就是通过判断能不能加载到某一个类进行处理的.

比如说使用Tomcat,那我是一定有这个依赖,能够加载到这个对象的.然后才把这个对象注入到Spring容器中.
也就是说配置这个bean是需要满足一定的条件的.

具体代码如下:
以下代码就表示当加载到对应的Tomcat,或 Jetty对象时, 才将对应的bean注入到Spring容器中.
在这里插入图片描述

@BlingConditionOnClass注解是模仿Springboot中的@ConditionOnClass注解自己定义的,他俩起到的效果是一样的.都是加载到某一个类才生效.

@BlingConditionOnClass 这个注解其实底层最关键的其实是运用了Spring的原生注解@Conditional

在这里插入图片描述
Conditional注解需要传入一个实现Condition接口的实现类, 实现matches方法.

笔者这里是传入了一个OnClassCondition类.在这个matches方法中就是做了一件事情,
就是判断注解传入的类路径能不能被正常加载.

public class  OnClassCondition implements Condition {

    public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
       //获取BlingConditionOnClass注解中的所有属性
        MultiValueMap<String, Object> attributes = metadata.getAllAnnotationAttributes(BlingConditionOnClass.class.getName());
        //获取其中的value属性对应的值
        List<Object> classPathList = attributes.get("value");
        if(classPathList == null || classPathList.isEmpty()){
            return false;
        }
        
        try {
            //加载传入的类路径
            Objects.requireNonNull(context.getClassLoader()).loadClass((String) classPathList.get(0));
            return true;
        } catch (ClassNotFoundException e) {
            return false;
        }
    }
}

总体思路是这样的:

  1. 当Spring扫描加载到WebServerAutoConfigration这个类的时候.会解析类上的注解.该类上标识了@Configuration注解.Spring就会解析调用这个类中标识了@Bean的方法.
  2. 当解析到tomcatWebServer()方法时,则通过反射可以解析到该方法上的注解@BlingConditionOnClass(“org.apache.catalina.startup.Tomcat”),并记录传入注解的值org.apache.catalina.startup.Tomcat
  3. 然后会解析到@BlingConditionOnClass注解中的注解@Conditional(OnClassCondition.class)
  4. 接下来会调用Condition接口实现类OnClassCondition的matches方法,根据方法该方法的返回值判断是否将该对象注入到Spring容器中.

根据以上流程就可以达到引入不同maven依赖而自动注入不同bean的效果.

3.让使用者能够加载到自动配置类

目前问题及通用处理方式

现在看起来好像是已经能达到我们刚开始想要的效果了, 通过引入不同的maven依赖向Spring容器注入不同的bean,但是还有一个很关键的点.

就是当使用者引入该项目,他怎么能够扫描加载到项目中WebServerAutoConfigration这个自动配置类呢?

我们知道Spring会默认扫描传入启动类所在的包路径.比如说下面这个工程,传入的是TestApplication, 这个类所在的包路径是com.bling.test,那么Spring就会扫描加载这个路径下所有的类.

在这里插入图片描述

而我们自己的写的Springboot项目需要加载的自动配置类,在com.bling.springboot路径下.默认当然是不会扫描加载到的.

在这里插入图片描述

此时,最常见的有两种方法,可以从使用者的角度去加载的这个自动配置类 :

  1. 使用Import注解.直接导入
    在这里插入图片描述
  2. 指定扫描路径,从而去扫描加载对应的类.
    在这里插入图片描述
    那Springboot真实是怎样做的呢? 以上两种方法都没有采用, 因为在我们的工程中可能会导入很多的依赖, 有很多很多的自动配置类,也有很多很多不同的项目路径, 难道我们要把这些配置类和路径全都写一遍吗? 这是非常不方便的.

真实Springboot处理方式

真实Spring在进行处理的时候会在@SpringBootApplication注解上Import一个类,AutoConfigurationImportSelector, 见名知义,也就是自动配置导入选择器. 这里也模仿Springboot这样处理.
在这里插入图片描述

这个类实现了ImportSelector接口,重写了selectImports方法,这个方法要返回什么呢?
其实就是返回需要被Spring扫描管理的类的全类名.返回的类将会被Spring容器加载管理.

在这里插入图片描述

那在这个方法中又应该怎样获取到所有自动配置类的全类名呢?

这里Spring利用的是SPI机制,SPI (Service Provider Interface)本身是一个概念,有很多的框架会去实现自己的SPI.比如Spring,它自己实现加载的spring.factories, 就是SPI机制, 当然JDK本身也有实现的一套SPI的机制.

我们这里就不去实现Spring的这一套SPI,比较麻烦,我们这里直接使用JDK的SPI进行演示处理.核心理解为什么要在这里用到SPI就行.

使用JDK SPI机制步骤:
首先明确JDK SPI是针对接口进行处理的,就是当你传入一个接口, 它可以获取这个接口对应的所有配置的实现类.

  1. 所以先定义一个AutoConfigration接口.让WebServerAutoConfigration去实现它.
    在这里插入图片描述

  2. 在resources目录下新建文件夹META-INF,在该文件夹下再新建文件夹services.在services下新建一个文件,文件名就是接口全类名.
    在该文件中配置好对应的实现类.
    在这里插入图片描述

  3. 使用JDK中的ServiceLoader类进行加载.

/**
 * @ClassName:     
 * @Description: 该类标识在启动类中,使用Import注解导入,所以Spring会对该类进行加载
 * @author: 
 * @date:        
 *   
 */
public class AutoConfigurationImportSelector implements ImportSelector {
    /**
     * @param importingClassMetadata
     * @return 返回需要加载到Spring容器的全类名
     */
    @Override
    public String[] selectImports(AnnotationMetadata importingClassMetadata) {
        //这里暂时不引入Spring中的SPI,比较复杂
        //直接使用Java中的SPI
        ArrayList<String> result = new ArrayList<>();
        ServiceLoader<AutoConfigration> load = ServiceLoader.load(AutoConfigration.class);
        for (AutoConfigration autoConfigration : load) {
            result.add(autoConfigration.getClass().getName());
        }
        return result.toArray(new String[0]);
    }
}

至此,就完完全全的能够达到我们在最开始提到的想法,想切换不同的Servlet容器,直接切换maven依赖就可以.

进行测试

想看该项目完整代码的可以到Github上下载:手写自己的springboot
如果有网络不通的小伙伴也可以到CSDN中下载CSDN地址下载.

我们还是将该项目进行打包, 额外新建一个非常简单的maven项目,引入该项目依赖,如下:

在这里插入图片描述

可以正常启动tomcat…

在这里插入图片描述

然后,排除Tomcat依赖,加入Jetty依赖.

在这里插入图片描述

现在就可以启动Jetty.

在这里插入图片描述

关键点总结

整个流程下来,其实主要有三点:

  1. springboot本身是引入所有组件依赖的,只不过利用maven依赖的传递性, 让使用者不需要引入额外的依赖
  2. 关于自动配置bean,其实大量运用了条件注解, 通过一定的条件判断该bean是否需要注入到Spring容器
  3. 为了让使用者能够较为方便的加载到自动配置类, 使用了SPI技术进行处理.

今天的分享就到这里了,有问题可以在评论区留言,均会及时回复呀.
我是bling,未来不会太差,只要我们不要太懒就行, 咱们下期见.
在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/523655.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

C++ OpenMesh拉普拉斯光顺平滑网格模型

程序示例精选 C OpenMesh拉普拉斯光顺平滑网格模型 如需安装运行环境或远程调试&#xff0c;见文章底部个人QQ名片&#xff0c;由专业技术人员远程协助&#xff01; 前言 这篇博客针对<<C OpenMesh拉普拉斯光顺平滑网格模型>>编写代码&#xff0c;代码整洁&#x…

【HTML5系列】第一章 · HTML5新增语义化标签

写在前面 Hello大家好&#xff0c; 我是【麟-小白】&#xff0c;一位软件工程专业的学生&#xff0c;喜好计算机知识。希望大家能够一起学习进步呀&#xff01;本人是一名在读大学生&#xff0c;专业水平有限&#xff0c;如发现错误或不足之处&#xff0c;请多多指正&#xff0…

面试题打卡30天-day28

1、什么是 Git 的 fork 命令&#xff1f;它和 clone 命令有什么区别&#xff1f; 回答一&#xff1a; 在 Git 中&#xff0c;fork 命令是指将其他用户的代码仓库完全复制一份到当前用户自己的账户下&#xff0c;成为一个新的独立代码仓库。与此相对&#xff0c;clone 命令是指…

【CNN】深度卷积神经网络(AlexNet)是什么?如何实现AlexNet?

系列文章目录 第二章 深度学习 CNN中的深度卷积神经网络&#xff08;AlexNet&#xff09; 目录 系列文章目录 文章目录 前言 一、深度卷积神经网络&#xff08;AlexNet&#xff09;是什么&#xff1f; 二、AlexNet的网络结构 三、实现AlexNet模型 总结 前言 本文主要是…

在autoDL上运行github的代码

大佬们的教程&#xff1a;(249条消息) AutoDL使用教程&#xff1a;1&#xff09;创建实例 2&#xff09;配置环境上传数据 3&#xff09;PyCharm2021.3专业版下载安装与远程连接完整步骤 4&#xff09;实时查看tensorboard曲线情况_孟孟单单的博客-CSDN博客 (227条消息) 「炼丹…

智能的本质人工智能与机器人领域的64个问题

以艾伦纽厄尔&#xff08;Allen Newell&#xff09;和赫伯特西蒙&#xff08;Herbert Simon&#xff09;为代表&#xff0c;他们基本上倾向于智能已经达到数理逻辑的最高形式&#xff0c;并将符号处理作为研究重点&#xff0c;他们共同发表了著名论文《逻辑理论家》&#xff08…

“灵光一现”的动态特征:EEG研究

导读 尤里卡效应(Eureka effect&#xff1b;灵光一现)是指突然解决问题的常见经验。在这里&#xff0c;本文以一种模式识别范式来研究这种效应&#xff0c;该范式要求在格式塔规则和先验知识的基础上分割复杂场景和识别物体。受试者必须在模糊的场景中检测物体&#xff0c;并通…

【刷题】栈与队列:滑动窗口的最大值

题目 首先先来看题目描述&#xff1a; 这是一道栈与队列相关的题&#xff0c;给定我们一个整型数组&#xff0c;有一个长度为k的滑动窗口&#xff0c;让我们计算每次窗口的最大值。 我们的想法可以是得到一队列数据结构&#xff0c;让进入这个队列的第一个数据一定是最大的&a…

使用无线传感器网络进行源定位(Matlab代码实现)

目录 &#x1f4a5;1 概述 &#x1f4da;2 运行结果 &#x1f389;3 参考文献 &#x1f468;‍&#x1f4bb;4 Matlab代码 &#x1f4a5;1 概述 本文提出了一种用于反狙击应用的源定位过程&#xff1a;​使用广义互相关&#xff08;GCC&#xff09;方法确定到达时间差&…

uboot fatal error: configs/.h: No such file or directory 解决方法

环境 Win10 64位 ubuntu 20.04 虚拟机 VMware Workstation 16 Pro 开发板&#xff1a;NK-980IOT&#xff08;NUC980DK61Y&#xff09; gcc 交叉编译工具链&#xff1a; ARM 官方 gcc version 11.2.1 20220111 NUC980 uboot 版本 &#xff1a;尝试移植到 u-boot-2019.10&am…

【游戏逆向】《某山奇缘》发包函数

一个游戏我们拿来以后第一件事肯定是要去分析发包函数。 因为发包函数可以给我们很多有用的信息。 第一&#xff0c;所有的功能都可以通过发包实现。 第二&#xff0c;发包中的参数可以获悉游戏中的数据形式&#xff0c;例如某些参数是类型&#xff0c;ID是什么样的值。 第…

Java 中同步机制的几种方式及使用

在Java中&#xff0c;同步机制是用来解决多线程并发访问共享资源的问题。如果多个线程同时访问共享资源&#xff0c;可能会导致数据不一致、死锁等问题。Java中提供了多种同步机制&#xff0c;例如synchronized关键字、Lock接口、Semaphore类、CountDownLatch类等。本文将介绍J…

Vivado综合属性系列之二 SRL_STYLE

目录 一、前言 二、SRL_STYLE 2.1 移位寄存器实现方式 2.2 工程代码 2.3 参考资料 一、前言 移位寄存器SRL在工程中属于使用频率较高个模块&#xff0c;可用于存储数据&#xff0c;实现串并转换&#xff1b; 根据数据移动方向可分为左移寄存器&#xff0c;右移寄存器&#x…

Vue3-黑马(八)

目录&#xff1a; &#xff08;1&#xff09;vue3-进阶-antdv-分页 &#xff08;2&#xff09;vue3-进阶-antdv-搜索 &#xff08;3&#xff09;vue3-进阶-antdv-删除1 &#xff08;1&#xff09;vue3-进阶-antdv-分页 分页显示不对&#xff0c;修改下方分页条&#xff1a; …

Web 自动化笔记-第一章 Selenium 环境搭建

1.自动化测试能解决什么问题&#xff1f; 1. 解决-回归测试 2. 解决-压力测试 3. 解决-兼容性测试 4. 提高测试效率,保证产品质量 回归测试&#xff1a; 项目在发新版本之后对项目之前的功能进行验证 压力测试&#xff1a; 可以理解多用户同时去操作软件&#xff0c; 统计软件服…

近1500支队伍、6000名开发者齐聚,第三届Light技术公益创造营收官

在很多老年人看来&#xff0c;融入数字生活并不容易&#xff0c;经常误点广告、不时接到诈骗电话、不会操作智能手机……但有些老年人并不担心这些&#xff0c;“误点了付款&#xff0c;有人会打电话过来&#xff0c;提醒我不要被骗了。”一位老人放心地进行着“网上冲浪”。而…

Python程序员该如何提高薪资水平?我来谈谈实用的五大方法

今天我要跟小伙伴们讨论的话题是&#xff1a;Python程序员该如何提升自己的价值&#xff1f;换句话说&#xff0c;程序员该如何逐渐增加自己的薪资&#xff1f;我想这是大家都关心的一个问题。首先&#xff0c;我们要明确一点&#xff0c;程序员的价值所在是什么&#xff1f;关…

深度学习之图像分类(一):AlexNet

本专栏介绍基于深度学习进行图像识别的经典和前沿模型&#xff0c;将持续更新&#xff0c;包括不仅限于&#xff1a;AlexNet&#xff0c; ZFNet&#xff0c;VGG&#xff0c;GoogLeNet&#xff0c;ResNet&#xff0c;DenseNet&#xff0c;SENet&#xff0c;MobileNet&#xff0c…

var、let、const和执行上下文

let/const/var let 基本语法 ES6新增了let命令&#xff0c;用于声明变量&#xff0c;其语法类似于var&#xff0c;但是所声明的变量只在let命令所在的代码块内有效 不存在变量提升 var命令会发生“变量提升”现象&#xff0c;即变量可以在声明之前使用&#xff0c;值为und…

【部署jar包到服务器】

部署打好的jar包到服务器 首先需要安装对应版本的jdk 查看所有可安装的jdk版本 yum -y list java 选择对应的安装 例如 yum install -y java-1.8.0-openjdk-devel.x86_64 安装好之后&#xff0c;查看jdk版本 java -version 默认安装位置在 usr/lib/jvm之中 倘若你之前的版本不对…