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

发布时间:2020-07-18 19:58:44 作者:GitShare
来源:网络 阅读:1803

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

前续

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

推断应用的类型

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;
}
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,也就是提供目标类不存在。

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

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

总结
后记

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

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


[SpringBoot]深入浅出剖析SpringBoot的应用类型识别机制图注:爱折腾的稻草


推荐阅读:
  1. springBoot(12):集成Druid
  2. springBoot(11):集成Mybatis

免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。

boot spring 微服务

上一篇:发送数据包和发arp应答包欺骗别人

下一篇:thinkphp 自动验证学习

相关阅读

您好,登录后才能下订单哦!

密码登录
登录注册
其他方式登录
点击 登录注册 即表示同意《亿速云用户服务条款》