SpringBoot源码解析之应用类型识别

栏目: Java · 发布时间: 6年前

内容简介:创建SpringBoot项目时,如果不选择starter-web,创建的SpringBoot项目可以正常运行,但运行结束程序便终止了。如果配置starter-web,则正常启动web应用。那么,SpringBoot是如何分辨出来当前应用是为web应用还是其他类型的应用呢?本篇文章带领大家从源码层面进行相应分析。SpringBoot使用枚举类WebApplicationType来定义可支持的应用类型以及相关推断应用类型的常量(数组)及静态方法。下面对该枚举类进行详细的讲解。枚举WebApplicationTy

创建SpringBoot项目时,如果不选择starter-web,创建的SpringBoot项目可以正常运行,但运行结束程序便终止了。如果配置starter-web,则正常启动web应用。那么,SpringBoot是如何分辨出来当前应用是为web应用还是其他类型的应用呢?本篇文章带领大家从源码层面进行相应分析。

枚举WebApplicationType

SpringBoot使用枚举类WebApplicationType来定义可支持的应用类型以及相关推断应用类型的常量(数组)及静态方法。下面对该枚举类进行详细的讲解。

应用类型

枚举WebApplicationType中定义了三个应用类型:

  • NONE:应用程序不作为web应用启动,不启动内嵌的服务。
  • SERVLET:应用程序以基于servlet的web应用启动,需启动内嵌servlet web服务。
  • REACTIVE:应用程序以响应式web应用启动,需启动内嵌的响应式web服务。

推断应用类型

SpringBoot启动时,在创建SpringApplication的构造方法内会调用枚举WebApplicationType的deduceFromClasspath方法获得应用类型并设置当前应用是普通web应用、响应式web应用还是非web应用。

SpringApplication的构造方法中调用并设置源代码:

this.webApplicationType = WebApplicationType.deduceFromClasspath();

deduceFromClasspath方法由枚举WebApplicationType提供,具体实现如下:

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;
}

推断的过程中重点调用了ClassUtils.isPresent()方法,用来判断指定类名的类是否存在,是否可以进行加载。ClassUtils.isPresent()方法源代码如下:

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

isPresent()方法调用了forName()方法,如果在调用forName()方法的过程中出现异常则返回false,也就是目标类不存在。否则,返回true。

看一下forName()方法的部分代码:

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

    // 此处省略一些非空和基础类型的判断逻辑代码

    ClassLoader clToUse = classLoader;
    if (clToUse == null) {
        //如果为空则获取默认classLoader
        clToUse = getDefaultClassLoader();
    }
    try {
        // 返回加载户的Class。
        return Class.forName(name, false, clToUse);
    } 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 Class.forName(innerClassName, false, clToUse);
            }
            catch (ClassNotFoundException ex2) {
                // Swallow - let original exception get through
            }
        }
        throw ex;
    }
}

通过以上核心代码,可得知forName()方法主要做的事情就是获得类加载器,尝试直接加载类,如果失败则尝试加载该类的内部类,如果依旧失败,则抛出异常。

因此,整个应用类型的推断分以下步骤:

  • SpringBoot调用SpringApplication构造方法;
  • SpringApplication构造方法调用枚举类的类型推断方法deduceFromClasspath()。
  • deduceFromClasspath()方法通过ClassUtils.isPresent()返回结果为true或false来确定是否加载成功指定的类。
  • ClassUtils.isPresent()方法通过调用forName()方法并捕获异常来确定是否能够成功加载该类。
  • forName()方法通过尝试加载指定类和指定类的内部类来确定该类是否存在,存在则返回该类,不存在则抛异常。

在类型推断的过程中枚举类WebApplicationType定义了具体去加载哪些类:

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

    private static final String WEBMVC_INDICATOR_CLASS = "org.springframework."
            + "web.servlet.DispatcherServlet";

    private static final String WEBFLUX_INDICATOR_CLASS = "org."
            + "springframework.web.reactive.DispatcherHandler";

    private static final String JERSEY_INDICATOR_CLASS = "org.glassfish.jersey.servlet.ServletContainer";
  • 如果应用程序存在DispatcherHandler并且不存在DispatcherServlet和ServletContainer则为响应式web应用,需加载并启动内嵌的响应式web服务。
  • 如果应用程序不包含Servlet和ConfigurableWebApplicationContext则为普通应用程序。
  • 其他情况则为基于servlet的web应用,需加载并启动内嵌的web web服务。

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

算法详解(卷1)——算法基础

算法详解(卷1)——算法基础

[美]蒂姆·拉夫加登(Tim Roughgarden) / 徐波 / 人民邮电出版社 / 2019-1-1 / 49

算法是计算机科学领域最重要的基石之一。算法是程序的灵魂,只有掌握了算法,才能轻松地驾驭程序开发。 算法详解系列图书共有4卷,本书是第1卷——算法基础。本书共有6章,主要介绍了4个主题,它们分别是渐进性分析和大O表示法、分治算法和主方法、随机化算法以及排序和选择。附录A和附录B简单介绍了数据归纳法和离散概率的相关知识。本书的每一章均有小测验、章末习题和编程题,这为读者的自我检查以及进一步学习提......一起来看看 《算法详解(卷1)——算法基础》 这本书的介绍吧!

CSS 压缩/解压工具
CSS 压缩/解压工具

在线压缩/解压 CSS 代码

图片转BASE64编码
图片转BASE64编码

在线图片转Base64编码工具

UNIX 时间戳转换
UNIX 时间戳转换

UNIX 时间戳转换