赵宇博的技术博客 赵宇博的技术博客
首页
前端
后端
数据库专栏
k8s专栏
分布式专栏
Linux网络专栏
手写系列专栏
随笔
关于
GitHub (opens new window)
首页
前端
后端
数据库专栏
k8s专栏
分布式专栏
Linux网络专栏
手写系列专栏
随笔
关于
GitHub (opens new window)
  • JVM专题

  • 源码专题

  • Activi6专题

  • 杂谈

    • jar启动和IDE里启动Sprintboot的区别
      • 多线程场景解决方案汇总
    • 后端
    • 杂谈
    zhaoyb
    2024-01-25
    目录

    jar启动和IDE里启动Sprintboot的区别

    # Jar启动和IDE里启动Sprintboot的区别

    想聊明白这个问题,需要补充一些前提条件,比如Fat jar、类加载机制等


    # 1、Fat jar

    我们在开发业务程序的时候,经常需要引用第三方的jar包,最终程序开发完成之后,通过打包程序,会把自己的代码和三方jar包一起打成同一个jar包,这种jar就称之为Fat jar

    SpringBoot的maven插件,打包方式就是把整体项目打包成一个Fat jar,其中把应用程序代码打包到BOOT-INF/classes中,把三方jar包打包到BOOT-INF/lib中,把jar包的详细信息(启动入口等)放置在META-INF/MANIFEST.MF文件中。

    <plugins>
        <plugin>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-maven-plugin</artifactId>
            <configuration>
                <mainClass>xxxxApplication</mainClass>
            </configuration>
        </plugin>
    </plugins>
    
    1
    2
    3
    4
    5
    6
    7
    8
    9

    整体Fat jar如图所示:

    image-20240125150625708

    # 2、jar启动入口

    如果一个jar内部有多个类都有main方法,那么java -jar启动的话会调用哪个main方法呢,换句话说就是一个jar启动的入口main方法是怎么定义的?

    两种方式:

    • 命令行直接指定,java -jar xxx.jar com.xx.xxxClass(不常用)

    • maven打包的时候插件生成META-INF/MANIFEST.MF文件,文件中的Main-Class:后面定义了当前jar的启动入口

    那么SpringBoot启动的入口是我们应用程序里面写得加了@SpringBootApplication的main方法吗? 显然不是,打开jar查看MANIFEST.MF文件,里面赫然写着Main-Class: org.springframework.boot.loader.JarLauncher

    所以说,通过java -jar 启动一个Springboot项目,启动的入口是 org.springframework.boot.loader.JarLauncher类里面的main方法。

    # 3、类加载

    根据刚才看到的jar内部的目录结构,应用程序依赖的三方jar包都不在classpath目录下,按照已有的类加载器的职能来看,这些jar都不能被加载到JVM中,SpringBoot需要自定义类加载器来加载这些包,具体是怎么做的,还是那句话,源码之下无秘密。打开JarLauncher类,如图所示:

    image-20240125151725411

    main方法很简单,就一行代码, (new JarLauncher()).launch(args); 跟代码进入launch方法。

    image-20240125151854376

    由代码得知,SpringBoot是自定义了一个LaunchedURLClassLoader来加载SpringBoot应用的所有类

    这个结论也可以程序实现得知:IDEA启动输出类加载和jar启动输出类加载器 ,结果一目了然

    image-20240125152121765

    image-20240125152215185

    # 4、结论

    Java -Jar是以FAT JAR的方式用LaunchedURLClassLoader来load class。而在IDE中则是直接以ApplicationClassLoader来load的。这种差别会导致调用classloader.getResourceAsStream()得到不一样的结果,这是因为FAT JAR启动时,LaunchedURLClassLoader的load的urls并没有FAT JAR本身,如abc-0.0.1-SNAPSHOT.jar, 但是应用中的src/main/resources/META-INF/resources目录被打包到了FAT JAR里,也就是abc-0.0.1-SNAPSHOT.jar!/META-INF/resources,这样这些resource也就不会被访问到了。

    这也就是为什么有时候在IDE里能读到的resource在Run FAT JAR的情况下读不到了

    #Sprintboot
    上次更新: 2024/11/22, 16:46:14
    Activiti6-业务实现
    多线程场景解决方案汇总

    ← Activiti6-业务实现 多线程场景解决方案汇总→

    最近更新
    01
    Activiti6-业务实现
    12-06
    02
    Activiti6-API详解
    11-28
    03
    SpringBoot集成Activiti和UI
    11-21
    更多文章>
    Theme by Vdoing | Copyright © 2022-2024 赵宇博 | MIT License
    • 跟随系统
    • 浅色模式
    • 深色模式
    • 阅读模式