【SpringBoot1】Spring Boot是如何推断你的工程类型的

我们的工程一般都是基于java的Servlet的,但是除此之外还有其他类型的,

在WebApplicationType里定义了常见的类型:

public enum WebApplicationType {/*** The application should not run as a web application and should not start an* embedded web server.*/NONE,/*** The application should run as a servlet-based web application and should start an* embedded servlet web server.*/SERVLET,/*** The application should run as a reactive web application and should start an* embedded reactive web server.*/REACTIVE;

从定义上也能看到,第一种是None类型,表示不是网络服务的类型,如果我们的服务不是web服务可以这么设置,但是这个应用场景极少。

第二种就是我们常用的SERVLRT方式,一般我们工程都要采用这种方式,即使不需要对外部用户提供服务,也需要与监控系统等交互,因此一般我们设置为Servet就行了。

第三种是REACTIVE,这个应该是支持响应式编程的,这个目前接触不多,暂且略过。

那么SpringBoot是如何判断我们的服务是哪种类型的呢?

一般我们SpringBoot的main入口都是这么写的:

@SpringBootApplication
@MapperScan("com.online_education.storage.mapper;")
public class TeacherServiceApp {public static void main(String[] args) {SpringApplication.run(TeacherServiceApp.class,args);}
}

其中@SpringBootApplication注解里其实是集成了三个:

@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan(...)

这三个注解的功能我们后面分析,这里我们先回到main方法里的run()继续看:

SpringApplication.run(TeacherServiceApp.class,args);

此时内容是这样的:

public static ConfigurableApplicationContext run(Class<?> primarySource, String... args) {return run(new Class<?>[] { primarySource }, args);}

这里的run没做什么,我们继续看run里的代码:

return new SpringApplication(primarySources).run(args);

这里主要是用反射创建我们的Main方法所在的对象,我们首先进入SpringApplication类里看一下,

	public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {this.resourceLoader = resourceLoader;Assert.notNull(primarySources, "PrimarySources must not be null");this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));this.webApplicationType = WebApplicationType.deduceFromClasspath();setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));this.mainApplicationClass = deduceMainApplicationClass();}

这里有一行很重要的代码:

this.webApplicationType = WebApplicationType.deduceFromClasspath();

这一行就是获取类型的,那具体如何获取的呢?我们继续看:

	static WebApplicationType deduceFromClasspath() {if (ClassUtils.isPresent(WEBFLUX_INDICATOR_CLASS, null) && !ClassUtils.isPresent(WEBMVC_INDICATOR_CLASS, null)&& !ClassUtils.isPresent(JERSEY_INDICATOR_CLASS, null)) {return WebApplicationType.REACTIVE;}for (String className : SERVLET_INDICATOR_CLASSES) {if (!ClassUtils.isPresent(className, null)) {return WebApplicationType.NONE;}}return WebApplicationType.SERVLET;}

这里的isPresent()做什么的我们后面再细看,这里通过名字也能推测出来,大致是要判断是否存在的意思,第一个if部分就是判断是否为响应式REACTIVE类型,后面的for循环就是判断是否存在

SERVLET_INDICATOR_CLASSES里标记的类型,里面两个参数是web服务必须的,只要有一个不存在就会返回类型是空。

private static final String[] SERVLET_INDICATOR_CLASSES = { "javax.servlet.Servlet","org.springframework.web.context.ConfigurableWebApplicationContext" };

可以看到,这里其实是两个写死的类的地址,因此这里其实主要到某个地方查是否加载了这两个类。

如果都存在,那就返回Servlet类型。

那具体到哪里查的呢?我们继续看ClassUtils.isPresent(className, null)的实现:

  public static boolean isPresent(String className, @Nullable ClassLoader classLoader) {try {forName(className, classLoader);return true;} catch (IllegalAccessError var3) {throw new IllegalStateException("Readability mismatch in inheritance hierarchy of class [" + className + "]: " + var3.getMessage(), var3);} catch (Throwable var4) {return false;}}

到这里,根据定义和forName这种写法,有经验的同学就知道了,这里是到JVM里查装载器是否载入了。

public static Class<?> forName(String name, @Nullable ClassLoader classLoader){
... 
Class elementClass;String elementName;if (name.endsWith("[]")) {elementName = name.substring(0, name.length() - "[]".length());elementClass = forName(elementName, classLoader);return Array.newInstance(elementClass, 0).getClass();} else if (name.startsWith("[L") && name.endsWith(";")) {elementName = name.substring("[L".length(), name.length() - 1);elementClass = forName(elementName, classLoader);return Array.newInstance(elementClass, 0).getClass();} else if (name.startsWith("[")) {elementName = name.substring("[".length());elementClass = forName(elementName, classLoader);return Array.newInstance(elementClass, 0).getClass();} else {ClassLoader clToUse = classLoader;if (classLoader == null) {clToUse = getDefaultClassLoader();}try {return Class.forName(name, false, clToUse);
... 
}

这里的写法就比较负责了,上面是一个递归,本质上是在读字节码文件,例如name.startsWith("[L") 这些都是读字节码用的。

 那字节码是保存在哪里的呢?学过JVM虚拟机原理的同学应该知道,字节码是保存在元空间的,所以这里SpringBoot在启动的时候是去元空间里查,看看我们有没有将Servlet的类信息加载进来。

到此我们就知道了,如果我们要启动web服务,需要先将Servlet加载进来,SpringBoot还会以此类型来判断是否启用web服务。

而且通过这个代码,我们也可以知道Servlet和响应式只能装载一个,如果两个都有,仍然会当做Servelt服务。

if (ClassUtils.isPresent(WEBFLUX_INDICATOR_CLASS, null) && !ClassUtils.isPresent(WEBMVC_INDICATOR_CLASS, null)&& !ClassUtils.isPresent(JERSEY_INDICATOR_CLASS, null)) {return WebApplicationType.REACTIVE;}

很明显,如果我们启动SpringBoot的时候还没有将Servlet相关类加载进来,那自然启动就失败了。

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

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

相关文章

XGB-2: Boosted Trees(提升树)简介

XGBoost代表“Extreme Gradient Boosting”&#xff0c;其中术语“Gradient Boosting”来源于Friedman的论文《Greedy Function Approximation: A Gradient Boosting Machine》。 梯度提升树已经存在一段时间&#xff0c;关于这个主题有很多资料。本部分将使用监督学习的元素&…

【Chrono Engine学习总结】1-安装配置与程序运行

本文仅用于个人安装记录。 官方安装教程 https://api.projectchrono.org/8.0.0/tutorial_install_chrono.html Windows下安装 windows下安装就按照教程好了。采用cmake-gui进行配置&#xff0c;建议首次安装只安装核心模块。然后依此configure下irrlicht&#xff0c;sensor…

6款超好用的IDEA插件,开发必备!

今天给大家介绍几款开发必备的IDEA插件&#xff1a; JRebel 热部署插件&#xff0c;让你在修改完代码后&#xff0c;不用再重新启动&#xff0c;很实用&#xff01;但是&#xff0c;不是免费的&#xff0c;需要大家继续发挥下自己的聪明才智才能happy的使用 Json Parser 厌倦…

Vivado Tri-MAC IP端口说明

Trri-mac Ethernet IP端口详细说明 序号 Signal Name Direction Description 备注 Clock&#xff0c;Reset Definition 1 gtx_clk I MAC IP全局时钟输入&#xff0c;125MHz。使用时输入125MHz全局时钟。 2 refclk I MAC IP参考时钟输入&#xff0c;200MHz&#xff0c;该时钟用…

每周AI新闻(2024年第5周)ChatGPT等多应用登陆 Vision Pro | 字节Coze国内版上线等

我是陌小北&#xff0c;一个正在研究硅基生命的、有趣儿的碳基生命。每周日20:00&#xff0c;准时解读每周AI大事件。 大厂动向 【1】ChatGPT、钉钉、Firefly AI 登陆 Vision Pro OpenAI发布了新的ChatGPT App&#xff0c;允许Apple Vision Pro用户与GPT-4 Turbo模型进行自然…

Abp 从空白的WebApplication中添加EntityFrameworkCore生成数据库

书接上文&#xff1a;Abp 从空白WebApplication开始 开发环境&#xff1a;.NET6、Volo.Abp 数据库&#xff1a;Sqlite 说明&#xff1a;纯属个人强行入门。我个人觉得按照官网的操作不舒服&#xff0c;所以自己研究着来&#xff0c;请读者根据自己的需要进行参考。我能保证的…

Ubuntu+GPU搭建Stable-Diffusion教程

【前序】已经安装anaconda 1.git拉取项目到本地 执行git clone https://github.com/AUTOMATIC1111/stable-diffusion-webui.git 进入项目目录下 cd stable-diffusion-webui/ 2. 安装对应Python依赖包 首先安装pytorch和torchvision&#xff0c;若是GPU环境的用户需要安装与cu…

2024年最新幻兽帕鲁服务器搭建教程

玩转幻兽帕鲁服务器&#xff0c;阿里云推出新手0基础一键部署幻兽帕鲁服务器教程&#xff0c;傻瓜式一键部署&#xff0c;3分钟即可成功创建一台Palworld专属服务器&#xff0c;成本仅需26元&#xff0c;阿里云服务器网aliyunfuwuqi.com分享2024年新版基于阿里云搭建幻兽帕鲁服…

Day59 单调栈part02 503. 下一个更大元素 II 42. 接雨水

Day59 单调栈part02 503. 下一个更大元素 II 42. 接雨水 503. 下一个更大元素 II class Solution { public:vector<int> nextGreaterElements(vector<int>& nums) {vector<int> result(nums.size(),-1); stack<int> st;st.push(0);for(int i 1;…

深度解析源码,Spring 如何使用三级缓存解决循环依赖

目录 一. 前言 二. 基础知识 2.1. 什么是循环依赖&#xff1f; 2.2. 三级缓存 2.3. 原理执行流程 三. 源码解读 3.1. 代码入口 3.2. 第一层 3.3. 第二层 3.4. 第三层 3.5. 返回第二层 3.6. 返回第一层 四. 原理深度解读 4.1. 什么要有三级缓存&#xff1f; 4.2.…

【HarmonyOS 4.0 应用开发实战】ArkTS 快速入门

个人名片&#xff1a; &#x1f43c;作者简介&#xff1a;一名大三在校生&#xff0c;喜欢AI编程&#x1f38b; &#x1f43b;‍❄️个人主页&#x1f947;&#xff1a;落798. &#x1f43c;个人WeChat&#xff1a;hmmwx53 &#x1f54a;️系列专栏&#xff1a;&#x1f5bc;️…

python中[[]] * (n)和[[] for _ in range(n)]的区别

1、现象 刷leetcode207的时候碰到一个坑&#xff0c;用[[]] * (n)初始化二维数组&#xff0c;逻辑是正确的&#xff0c;但是结果始终不对。 2、原因 最后定位是初始化语句使用错误导致的&#xff0c;我使用的是[[]] * (n)&#xff0c;应该使用[[] for _ in range(n)] 3、解…

Stable Diffusion 模型下载:ReV Animated

模型介绍 该模型能够创建 2.5D 类图像生成。此模型是检查点合并&#xff0c;这意味着它是其他模型的产物&#xff0c;以创建从原始模型派生的产品。 条目内容类型大模型基础模型SD 1.5来源CIVITAI作者s6yx文件名称revAnimated_v122EOL.safetensors文件大小5.13GB 生成案例 …

redis数据库设置对象的过期时间,到期后自动删除该条数据

redis数据库设置对象的过期时间,到期后自动删除该条数据&#xff01;过期时间的事情经常发生。比如大家领到了一些购物券。这张购物券有一个过期时间。必须在某节点之前&#xff08;某年某月末日&#xff09;之前&#xff0c;使用掉&#xff0c;否则该券就会过期&#xff0c;无…

Ruby详解及安装流程

文章目录 一、Ruby详解二、Ruby安装流程三、Ruby案例四、Ruby常见问题五、Ruby优缺点六、热门文章 一、Ruby详解 Ruby是一种高级编程语言&#xff0c;具有简单易学、灵活多变、优雅美丽的语法特点。它是一种面向对象的编程语言&#xff0c;具有动态类型和解释型语言的特性。在…

Aigtek高压放大器用途是什么呢

高压放大器在电子领域中扮演着至关重要的角色&#xff0c;其主要作用是将低电压信号放大到更高的电压水平。这种类型的放大器广泛用于各种应用中&#xff0c;以下是高压放大器的用途以及其关键作用的详细介绍。 1、科学研究和实验室应用&#xff1a; 高压放大器在科学研究和实验…

【Linux】Linux权限

Hello everybody!在介绍完Linux操作系统常用的基本指令后&#xff0c;咱们就要开始权限的讲解了。如果没有看过我之前文章的宝子们可以去看看呦&#xff01;有比较系统的指令讲解&#xff01; 回到正题&#xff0c;关于权限&#xff0c;实际上没有大家想象中的那么神秘&#x…

解决问题(Tensorflow框架):ImportError: cannot import name ‘merge‘ from ‘keras.layers‘

看了一圈解决方案&#xff0c;没有找到跟我这个相关的 这就是版本兼容性问题 说句最简单的&#xff0c;针对我这个问题 直接把merge删除点就完事了&#xff0c;因为新版的tensorflow框架这个里面不包含merge&#xff0c;所以直接删掉问题就解决了

Linux进程信号处理:深入理解与应用(2​​)

&#x1f3ac;慕斯主页&#xff1a;修仙—别有洞天 ♈️今日夜电波&#xff1a;its 6pm but I miss u already.—bbbluelee 0:01━━━━━━️&#x1f49f;──────── 3:18 &#x1f504; ◀️…

Android 无操作之后定时退出

android定时器监用户听对页面无操作5分钟退出登录实现 - 简书 private long advertisingTime 600000;///定时结束退出登录10分(分钟)600000毫秒public CountDownTimer countDownTimer;Overrideprotected void onResume() {super.onResume();//启动定时if (isTimedExitApp()) …