千家信息网

[SpringBoot]深入浅出剖析SpringBoot的应用类型识别机制

发表于:2026-11-01 作者:千家信息网编辑
千家信息网最后更新 2026年11月01日,微信号:GitShare微信公众号:爱折腾的稻草如有问题或建议,请在公众号留言[1]前续为帮助广大SpringBoot用户达到"知其然,更需知其所以然"的境界,作者将通过SpringBoot系列文章全
千家信息网最后更新 2026年11月01日[SpringBoot]深入浅出剖析SpringBoot的应用类型识别机制

微信号:GitShare
微信公众号:爱折腾的稻草
如有问题或建议,请在公众号留言[1]

前续

为帮助广大SpringBoot用户达到"知其然,更需知其所以然"的境界,作者将通过SpringBoot系列文章全方位对SpringBoot2.0.0.RELEASE版本深入分解剖析,让您深刻的理解其内部工作原理。

  • 1、[SpringBoot]利用SpringBoot快速构建并启动项目

  • 2、[SpringBoot]详解SpringBoot应用的启动过程

推断应用的类型

SpringBoot启动时,在创建SpringApplication对象时,会自动去识别并设置当前应用是普通web应用、响应式web应用还是非web应用,其内部实现原理是什么?
首先看看源代码

/**
* 推断应用的类型
*/

private WebApplicationType deduceWebApplicationType()
{
if (ClassUtils.isPresent(REACTIVE_WEB_ENVIRONMENT_CLASS, null)
&& !ClassUtils.isPresent(MVC_WEB_ENVIRONMENT_CLASS, null)) {
return WebApplicationType.REACTIVE;
}
for (String className : WEB_ENVIRONMENT_CLASSES) {
if (!ClassUtils.isPresent(className, null)) {
return WebApplicationType.NONE;
}
}
return WebApplicationType.SERVLET;
}
  • ClassUtils.isPresent()方法:
    其作用是判断所提供的类名的类是否存在,且可以被加载。源代码如下:

public static boolean isPresent(String className, @Nullable ClassLoader classLoader) {
try {
forName(className, classLoader);
return true;
}
catch (Throwable ex) {
// Class or one of its dependencies is not present...
return false;
}
}

调用了forName()方法,如果出现异常,则返回false,也就是提供目标类不存在。

  • forName()方法的源码剖析:

public static Class forName(String name, @Nullable ClassLoader classLoader)
throws ClassNotFoundException, LinkageError {

Assert.notNull(name, "Name must not be null");
//根据基本类的JVM命名规则(如果合适的话),将给定的类名name解析为基本类型的包装类
Class clazz = resolvePrimitiveClassName(name);
if (clazz == null) {
//commonClassCache是包含java.lang包下所有类,将类的类名作为键,对应类作为值的一个Map集合。
clazz = commonClassCache.get(name); //根据类名,获取commonClassCache集合中的值,如果为空,表示目标类不是java.lang包的下类,即不是原始类型。
}
if (clazz != null) {
//如果根据类名,已经获取到了类,则直接返回该类。
return clazz;
}

//判断clas属性值是否为数组对象。比如:java.lang.String[]
// "java.lang.String[]" style arrays
if (name.endsWith(ARRAY_SUFFIX)) {
//如果是,则将类名后的"[]"方括号截去,返回java.lang.String,递归查找类名,找到后,将Class类型转换为数组。
String elementClassName = name.substring(0, name.length() - ARRAY_SUFFIX.length());
Class elementClass = forName(elementClassName, classLoader);
return Array.newInstance(elementClass, 0).getClass();
}

//class属性值是否为数组对象的二进制表示。比如:[Ljava.lang.String
// "[Ljava.lang.String;" style arrays
if (name.startsWith(NON_PRIMITIVE_ARRAY_PREFIX) && name.endsWith(";")) {
//如果是,则将值的"[L"部分截去,递归查找类名,找到后,将对应的Class类型转换为数组
String elementName = name.substring(NON_PRIMITIVE_ARRAY_PREFIX.length(), name.length() - 1);
Class elementClass = forName(elementName, classLoader);
return Array.newInstance(elementClass, 0).getClass();
}

//class属性值是否为二维数组
// "[[I" or "[[Ljava.lang.String;" style arrays
if (name.startsWith(INTERNAL_ARRAY_PREFIX)) {
String elementName = name.substring(INTERNAL_ARRAY_PREFIX.length());
Class elementClass = forName(elementName, classLoader);
return Array.newInstance(elementClass, 0).getClass();
}

//获取classLoader
ClassLoader clToUse = classLoader;
if (clToUse == null) {
//如果classLoader为空,则获取默认的classLoader对象。
clToUse = getDefaultClassLoader();
}
try {
//返回加载后的类
return (clToUse != null ? clToUse.loadClass(name) : Class.forName(name));
}
catch (ClassNotFoundException ex) {
//用于处理内部类的情况。
int lastDotIndex = name.lastIndexOf(PACKAGE_SEPARATOR);
if (lastDotIndex != -1) {
//拼接内部类的名字。
String innerClassName = name.substring(0, lastDotIndex) + INNER_CLASS_SEPARATOR + name.substring(lastDotIndex + 1);
try {
return (clToUse != null ? clToUse.loadClass(innerClassName) : Class.forName(innerClassName));
}
catch (ClassNotFoundException ex2) {
// Swallow - let original exception get through
}
}
throw ex;
}
}
  • 再来看看使用到的常量值:

    • REACTIVE_WEB_ENVIRONMENT_CLASS:org.springframework.web.reactive.DispatcherHandler

    • MVC_WEB_ENVIRONMENT_CLASS:org.springframework.web.servlet.DispatcherServlet

    • WEB_ENVIRONMENT_CLASSES:{"javax.servlet.Servlet", "org.springframework.web.context.ConfigurableWebApplicationContext" }
      也就是说,

  • 1、如果应用程序中存在org.springframework.web.reactive.DispatcherHandler这个类,则表示是一个响应式web应用,项目在启动时,需要去
    加载启动内嵌的响应式web服务器。

  • 2、如果应用程序中既不存在javax.servlet.Servlet类,也不存在org.springframework.web.context.ConfigurableWebApplicationContext这个类,则
    表示当前应用不是一个web应用,启动时无需加载启动内嵌的web服务器。

  • 3、除上述两种情况外,则表示当前应用是一个servlet的web应用,启动时需要加载启动内嵌的servlet的web服务器(比如Tomcat)。

推断并设置main方法的定义类(启动类)

SpringBoot启动时,在创建SpringApplication对象时,最后会推断并设置main方法的定义类(启动类),其实现原理是什么呢?
先看看源代码;

private Class deduceMainApplicationClass() {
try {
StackTraceElement[] stackTrace = new RuntimeException().getStackTrace();
for (StackTraceElement stackTraceElement : stackTrace) {
if ("main".equals(stackTraceElement.getMethodName())) {
return Class.forName(stackTraceElement.getClassName());
}
}
}
catch (ClassNotFoundException ex) {
// Swallow and continue
}
return null;
}
  • java.lang.StackTraceElement是什么?
    类元素代表一个堆栈帧。除了一个在堆栈的顶部所有的栈帧代表一个方法调用。在堆栈顶部的帧表示在将其生成的堆栈跟踪的执行点。

    • stackTraceElement.getMethodName() 返回一个包含由该堆栈跟踪元素所表示的执行点的方法的名称。

    • stackTraceElement.getClassName() 返回一个包含由该堆栈跟踪元素所表示的执行点类的完全限定名。

  • java.lang.Class.forName()的作用是什么?
    java.lang.Class.forName(String name, boolean initialize, ClassLoader loader) 方法返回与给定字符串名的类或接口的Class对象,使用给定的类加载器。

参数说明
- name :这是所需类的完全限定名称。
- initialize : 这说明这个类是否必须初始化。
- loader : 这是必须加载的类的类加载器。
异常说明
- LinkageError : 如果联动失败。
- ExceptionInInitializerError : 如果这种方法所引发的初始化失败。
- ClassNotFoundException : 如果类不能位于由指定的类加载器。
参数使用
- ClassLoader loader:如果该参数加载器loader 为空,通过引导类加载器加载类。
- boolean initialize:如果它没有被初始化,则initialize参数为true。

通过上面知识点的讲解,deduceMainApplicationClass的作用就非常清晰了,主要是获取当前方法调用栈,遍历调用堆栈信息找到main函数的类,并返回该类。

总结
  • 1、SpringBoot是通过调用ClassUtils类的isPresent方法,检查classpath中是否存在org.springframework.web.reactive.DispatcherHandler类、
    javax.servlet.Servlet类和org.springframework.web.context.ConfigurableWebApplicationContext类来判断当前应用是响应式Web应用,还是普通的Servlet的Web应用,还是非Web应用。

  • 2、SpringBoot是通过获取当前方法的调用栈信息,来判断当前main函数所在的类。

后记

为帮助广大SpringBoot用户达到"知其然,更需知其所以然"的境界,作者将通过SpringBoot系列文章全方位对SpringBoot2.0.0.RELEASE版本深入分解剖析,让您深刻的理解其内部工作原理。

敬请关注[爱折腾的稻草(GitShare)]公众号,为您提供更多更优质的技术教程。


图注:爱折腾的稻草


应用 方法 类名 堆栈 类型 对象 数组 原理 参数 推断 剖析 作用 元素 公众 属性 服务器 源代码 稻草 还是 服务 数据库的安全要保护哪些东西 数据库安全各自的含义是什么 生产安全数据库录入 数据库的安全性及管理 数据库安全策略包含哪些 海淀数据库安全审计系统 建立农村房屋安全信息数据库 易用的数据库客户端支持安全管理 连接数据库失败ssl安全错误 数据库的锁怎样保障安全 百单网互联网科技有限公司 深圳科技互联网 互联网 科技服务平台 互联网科技创新企业排名 衡水科技产业互联网平台 移动互联网科技股 阿里互联网科技公司 互联网经济与金融科技 千方科技属于互联网企业吗 崇明区互联网教育科技售后服务 中国互联网有什么科技 互联网科技资金流入 张召忠谈中国互联网科技 互联网科技快讯热点 互联网与数字科技关系 半导体行业属于互联网高科技类吗 昆明中奇联合互联网科技 国产互联网科技公司 玉溪新华互联网科技怎么选 利用互联网等科技手段 互联网科技未来 电子科技大学互联网 西安互联网科技公司 新华互联网科技2019 湖南诚度互联网科技有限公司 996高科技互联网公司 锡山区互联网智能科技产品供应 上海同邻互联网科技有限公司规模 保山浩联互联网科技有限公司 广州全民伙伴互联网科技有限公司
0