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

这篇具有很好参考价值的文章主要介绍了手写自己的Springboot-2-从Servlet容器选择彻底理解自动配置。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。


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

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

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

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

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

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

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

1.pom依赖

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

手写自己的Springboot-2-从Servlet容器选择彻底理解自动配置
那为什么使用者在切换的时候还需要额外引入Jetty的依赖呢?

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

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

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

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

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

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

2.判断注入哪个bean

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

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

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

具体代码如下:
以下代码就表示当加载到对应的Tomcat,或 Jetty对象时, 才将对应的bean注入到Spring容器中.
手写自己的Springboot-2-从Servlet容器选择彻底理解自动配置

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

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

手写自己的Springboot-2-从Servlet容器选择彻底理解自动配置
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-2-从Servlet容器选择彻底理解自动配置

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

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

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

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

真实Spring在进行处理的时候会在@SpringBootApplication注解上Import一个类,AutoConfigurationImportSelector, 见名知义,也就是自动配置导入选择器. 这里也模仿Springboot这样处理.
手写自己的Springboot-2-从Servlet容器选择彻底理解自动配置

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

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

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

这里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去实现它.
    手写自己的Springboot-2-从Servlet容器选择彻底理解自动配置

  2. 在resources目录下新建文件夹META-INF,在该文件夹下再新建文件夹services.在services下新建一个文件,文件名就是接口全类名.
    在该文件中配置好对应的实现类.
    手写自己的Springboot-2-从Servlet容器选择彻底理解自动配置

  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项目,引入该项目依赖,如下:

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

可以正常启动tomcat…

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

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

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

现在就可以启动Jetty.

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

关键点总结

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

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

今天的分享就到这里了,有问题可以在评论区留言,均会及时回复呀.
我是bling,未来不会太差,只要我们不要太懒就行, 咱们下期见.
手写自己的Springboot-2-从Servlet容器选择彻底理解自动配置文章来源地址https://www.toymoban.com/news/detail-452076.html

到了这里,关于手写自己的Springboot-2-从Servlet容器选择彻底理解自动配置的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • Kubernetes VS Docker Swarm:选择适合自己的容器编排工具

    容器编排工具是一种自动化部署和管理容器的工具。 利用容器编排工具可以方便地解决容器化应用程序的部署、扩展和高可用性等问题。 Kubernetes 和 Docker Swarm 是目前最流行的两个容器编排工具,因此选用它们来进行比较。 1.1 Kubernetes 的来源和发展背景 Kubernetes 源于 Google 的

    2024年02月06日
    浏览(75)
  • 终于可以彻底告别手写正则表达式了

    这篇文章的目的是让你能得到完美的正则表达式,而且还不用自己拼。 说到正则表达式,一直是令我头疼的问题,这家伙一般时候用不到,等用到的时候发现它的规则是一点儿也记不住, d 表示一个数字, s 表示包括下划线在内的任意单词字符,也就是 [A-Za-z0-9_] ,还有

    2024年02月15日
    浏览(49)
  • Linux小程序: 手写自己的shell

    注意: 本文章只是为了理解shell内部的工作原理, 所以并没有完成shell的所有工作, 只是完成了shell里的一小部分工作

    2024年04月09日
    浏览(37)
  • 深度学习(六):paddleOCR理解及识别手写体,手写公式,表格

    光学字符识别(Optical Character Recognition, OCR),ORC是指对包含文本资料的图像文件进行分析识别处理,获取文字及版面信息的技术,检测图像中的文本资料,并且识别出文本的内容。 那么有哪些应用场景呢? 其实我们日常生活中处处都有ocr的影子,比如在疫情期间身份证识别

    2024年02月05日
    浏览(49)
  • Servlet容器中的会话管理设计说明

    Servlet容器是一种用于运行Java Servlet的容器,它是Web服务器的一部分。它负责处理Servlet的生命周期、请求和响应处理、多线程处理、会话管理等任务。 以下是Servlet容器架构的主要设计说明: Servlet容器架构通常是基于分层结构设计的。最底层是网络层,负责接收和发送请求和

    2024年02月15日
    浏览(32)
  • 【Spring专题】手写简易Spring容器过程分析

    首先必须得声明的是,下面的流程并不代表Spring源码中真正的流程。而是,我们通过现在的Spring提供给我们的某些基础功能,反推过来的流程。所以,并不全面,但是会具有一点参考性。 由于Spring源码分析是一个前后联系比较强的过程,而且这边分析,也是按照代码顺序讲解

    2024年02月13日
    浏览(43)
  • 彻底理解solidity中的事件

    在我之前的几篇关于智能合约的文章中,都有提到事件的用法,比如: 这里定义了两个事件,分别表示最高竞价更新了和拍卖结束了。 然后在需要的位置,调用事件,比如: 我们可以通过emit调用事件方法,然后这个事件就作为日志记录到了以太坊区块链中。日志是以太坊区

    2024年02月02日
    浏览(40)
  • 【框架源码】手写Spring框架IOC容器核心流程

    要是想要了解Spring IOC底层,首先我们就得先了解什么是IOC。 IOC就是控制反转,把对象创建和对象之间的调用过程交给Spring进行管理。 使用IOC目的就是之前我们创建对象的方式都是用new的方式创建,这个方式有个缺点,被创建对象的类的位置一旦被改动,new就会报错,类与类

    2024年02月06日
    浏览(56)
  • 选择什么电容笔比较好?平板手写笔推荐

    由于苹果Pencil的热销,让华国内市场上,也出现了不少的平替式电容笔,这些产品,有好有坏,价格也很公道。不过,也有很多产品的价格都很平价。我是一个拥有多年经验的数码发烧友,在前几年就开始用上了电容笔,因此我对电容笔的了解并不少,所以在这里给各位推荐

    2024年02月06日
    浏览(44)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包