zoukankan      html  css  js  c++  java
  • 震惊!你还不知道SpringBoot真正的启动引导类

    引言

    SpringBoot项目中的启动类,一般都是XXApplication,例如「StatsApplication」「UnionApplication」

    每个项目的启动类名称都不一样。但是它的启动类真的是XXApplication吗?

    META-INF/****Manifest.mf文件

    jar文件实际上是class文件的zip压缩存档。jar并不能表达应用程序的便签信息.

    「META-INF/Manifest.mf文件提供存档的便签信息.」

    Manifest.mf有 「Main-Class,用来标明jar文件的入口类。」

    解压jar包,查看META-INF/Manifest.mf过程如下:

    重要信息如下

    Main-Class: org.springframework.boot.loader.JarLauncher
    Start-Class: com.shanyuan.StatsApplication
    

    也就是说:「org.springframework.boot.loader.JarLauncher 是 SpringBoot 的启动类!」

    下面浏览下 JarLauncher

    浏览JarLauncher

    3.1 找到JarLauncher

    进入IDEA,Ctrl+N查找JarLauncher,竟然找不到!!

    进入 https://search.maven.org/classic/#advancedsearch 查询JarLauncher

    在查询结果找到spring下的项目

    确定 JarLauncher 位于 spring-boot-loader 下。为了方便查看源码,在 pom 中引入

    <dependency>
     <groupId>org.springframework.boot</groupId>
     <artifactId>spring-boot-loader</artifactId>
     <scope>provided</scope>
    </dependency>
    

    3.2.JarLauncher说明

    JarLauncher作为引导类 ,当调用java -jar 命令时,将调用 main 方法,实际上调用的是 「JarLauncher#launch」方法,该方法继承于 org.springframework.boot.loader.Launcher

    简化层次关系为:

    JarLauncher#launch代码如下

    protected void launch(String[] args) throws Exception {
       JarFile.registerUrlProtocolHandler();
       ClassLoader classLoader = createClassLoader(getClassPathArchives());
       launch(args, getMainClass(), classLoader);
    }
    

    「聚句解析」

    「1,.JarFile.registerUrlProtocolHandler();」

    Spring Boot 生成的 FAT jar,在被 java -jar 引导时,其内部的 jar 文件无法被 sun.net.www.protocol.jar.Handler 处理。微信公众号搜索, [Java学习之道] ,回复 '福利' 2T 资料等你来拿~

    所以 SpringBoot 实现了,org.springframework.boot.loader.jar.Handler

    JarFile.registerUrlProtocolHandler(), 就注册 org.springframework.boot.loader.jar.Handler

    「2.ClassLoader classLoader = createClassLoader(getClassPathArchives());」

    创建ClassLoader。

    getClassPathArchives 核心判断是 isNestedArchive 方法。

    isNestedArchive 被 JarLauncher 覆写了。其实现如下:

    static final String BOOT_INF_CLASSES = "BOOT-INF/classes/";
    
    static final String BOOT_INF_LIB = "BOOT-INF/lib/";
    @Override
    protected boolean isNestedArchive(Archive.Entry entry) {
       if (entry.isDirectory()) {
          return entry.getName().equals(BOOT_INF_CLASSES);
       }
       return entry.getName().startsWith(BOOT_INF_LIB);
    }
    

    也就是说,只要 「满足以BOOT-INF/classes/和BOOT-INF/lib/都是classLoader加载」的范围。

    解压的jar,查看也与只对应

    3. launch(args, getMainClass(), classLoader);

    protected void launch(String[] args, String mainClass, 
                    ClassLoader classLoader)
          throws Exception {
       Thread.currentThread().setContextClassLoader(classLoader);
       createMainMethodRunner(mainClass, args, classLoader).run();
    }
    

    查看 createMainMethodRunner 的 run 方法,如下:

    public class MainMethodRunner {
        // 省略部分代码
        public void run() throws Exception {
       Class<?> mainClass = Thread.currentThread().getContextClassLoader()
             .loadClass(this.mainClassName);
           Method mainMethod = 
               mainClass.getDeclaredMethod("main", String[].class);
           mainMethod.invoke(null, new Object[] { this.args });
        }
    }
    

    其中 mainClass,来自 /META-INF/MANIFEST.MF 中的 Start-Class 属性。

    「即,JarLauncher 是同进程内,通过反射调用 Start-Class 对应类,即 XXXApplication 的 main 方法。」

    4.总结

    SpringBoot 项目的实际启动类是 org.springframework.boot.loader.JarLauncher

    「在 JarLauncher 内部通过反射调用 XXApplication 类的 main 方法。具体实现位于 MainMethodRunner中。」

    写在最后

    欢迎大家关注我的公众号【风平浪静如码】,海量Java相关文章,学习资料都会在里面更新,整理的资料也会放在里面。

    觉得写的还不错的就点个赞,加个关注呗!点关注,不迷路,持续更新!!!

  • 相关阅读:
    Scala编程基础
    大数据学习环境搭建(CentOS6.9+Hadoop2.7.3+Hive1.2.1+Hbase1.3.1+Spark2.1.1)
    hadoop2.7.3编译,支持snappy、bzip2本地压缩
    ABAP非Unicode系统中字符串拼接(CONCATENATE)时吃字符问题
    Hadoop学习笔记
    HIVE开发总结
    中文字符截取乱码问题
    替换空字符NULL(字符编码为0的字符)
    Generate Time Data(普通日期主数据)
    Generate Time Data(财务日期主数据)
  • 原文地址:https://www.cnblogs.com/fengpinglangjingruma/p/13916297.html