Skip to content

SpringBoot在命令行是如何启动的? #11

@bitfishxyz

Description

@bitfishxyz

SpringBoot在命令行是如何启动?

假设这是一个简单的SpringBoot应用程序

我们要是想启动它,一般需要两个步骤:

  • 打包成jar文件
  • 运行jar文件

具体来说就是这样的

# 打包成单独的jar文件
$ mvn package

20190627231420

# 运行jar文件
$ java -jar target/standalone-0.0.1-SNAPSHOT.jar

  .   ____          _            __ _ _
 /\\ / ___'_ __ _ _(_)_ __  __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
 \\/  ___)| |_)| | | | | || (_| |  ) ) ) )
  '  |____| .__|_| |_|_| |_\__, | / / / /
 =========|_|==============|___/=/_/_/_/
 :: Spring Boot ::        (v2.1.6.RELEASE)

2019-06-27 23:15:12.965  INFO 93939 --- [           main] c.g.c.standalone.StandaloneApplication   : Starting StandaloneApplication v0.0.1-SNAPSHOT on appledeiMac.local with PID 93939 (/Users/apple/Downloads/x1hnd1rk/hello-springboot/target/standalone-0.0.1-SNAPSHOT.jar started by apple in /Users/apple/Downloads/x1hnd1rk/hello-springboot)
...
...

那么问题来了,java -jar xx.jar的时候,到底发生了什么?为什么
我们的程序就启动了呢?

jar文件内容

首先我们来解压一下打包后的文件,来看看到底是什么东西。

jar文件都是通过zip格式来压缩的,所以解压jar包等于解压zip文件。
凡是可以解压zip文件的工具,都可以解压jar文件。

$ unzip target/standalone-0.0.1-SNAPSHOT.jar -d temp
$ tree ./temp -L 2
./temp
├── BOOT-INF
│   ├── classes
│   └── lib
├── META-INF
│   ├── MANIFEST.MF
│   └── maven
└── org
    └── springframework

$ tree ./temp
./temp
├── BOOT-INF
│   ├── classes
│   │   ├── application.properties
│   │   └── com
│   │       └── github
│   │           └── codeman
│   │               └── standalone
│   │                   └── StandaloneApplication.class
│   └── lib
│       ├── classmate-1.4.0.jar
│       ├── hibernate-validator-6.0.17.Final.jar
        ....

├── META-INF
│   ├── MANIFEST.MF
│   └── maven
│       └── com.github.codeman
│           └── standalone
│               ├── pom.properties
│               └── pom.xml
└── org
    └── springframework
        └── boot
            └── loader
                ├── ExecutableArchiveLauncher.class
                ├── JarLauncher.class
                ...

到了这一步骤,请读者在自己的计算机上尝试一下。

我们可以看到有三个文件夹。

首先来看看BOOT-INF这个文件夹。这里文件夹里面又有两个文件夹classeslib。可以看到,class文件夹就是存放我们的源代码编译后的class文件,
lib文件夹存放的是我们依赖的jar包,包括我们的tomcat
也被打包到这个文件夹了。

那么METE-INFO这个文件夹是做什么的呢?

这里就需要补充一个额外的知识了:

根据java规范和jar规范,当我们执行jar -jar xx.jar的时候,
它会寻找jar包里面的META-INFO/MANIFEST.MF文件,并读取Main-Class这个字段。

在我们的项目中,它就是
20190628080913

org.springframework.boot.loader.JarLauncher

也就是说,java -jar target/standalone-0.0.1-SNAPSHOT.jar
等同java org.springframework.boot.loader.JarLauncher

口说无凭,我们来试一下:

$ cd temp
$ java org.springframework.boot.loader.JarLauncher
  .   ____          _            __ _ _
 /\\ / ___'_ __ _ _(_)_ __  __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
 \\/  ___)| |_)| | | | | || (_| |  ) ) ) )
  '  |____| .__|_| |_|_| |_\__, | / / / /
 =========|_|==============|___/=/_/_/_/
 :: Spring Boot ::        (v2.1.6.RELEASE)

2019-06-28 08:14:01.518  INFO 95178 --- [           main] c.g.c.standalone.StandaloneApplication   : Starting StandaloneApplication on appledeiMac.local with PID 95178 (/Users/apple/Downloads/x1hnd1rk/hello-springboot/temp/BOOT-INF/classes started by apple in /Users/apple/Downloads/x1hnd1rk/hello-springboot/temp)
2019-06-28 08:14:01.521  INFO 95178 --- [           main] c.g.c.standalone.StandaloneApplication   : No active profile set, falling back to default profiles: default
...
...

可以看到,程序确实也可以通过这个方式来启动。

JarLauncher

可以现在新的问题又来了,java org.springframework.boot.loader.JarLauncher的时候
发生了什么?

这里我们查看一下官方文档

https://docs.spring.io/spring-boot/docs/current/reference/htmlsingle/#executable-jar-launcher-manifest

请读者自行点击查看。

org.springframework.boot.loader.JarLauncher
会作为一个引导类了,它会启动META-INF/MANIFEST.MF
Start-Class中定义的类。

在我们这个项目中,它的值就是com.github.codeman.standalone.StandaloneApplication

可以看到这就是我们自己编写的项目的入口文件。

那么我们来手动的启动下

$ java -classpath "temp/BOOT-INF/classes:temp/BOOT-INF/lib/*" com.github.codeman.standalone.StandaloneApplication

  .   ____          _            __ _ _
 /\\ / ___'_ __ _ _(_)_ __  __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
 \\/  ___)| |_)| | | | | || (_| |  ) ) ) )
  '  |____| .__|_| |_|_| |_\__, | / / / /
 =========|_|==============|___/=/_/_/_/
 :: Spring Boot ::        (v2.1.6.RELEASE)

2019-06-27 23:43:25.117  INFO 94198 --- [           main] c.g.c.standalone.StandaloneApplication   : Starting StandaloneApplication on appledeiMac.local with PID 94198 (/Users/apple/Downloads/x1hnd1rk/hello-springboot/temp/BOOT-INF/classes started by apple in /Users/apple/Downloads/x1hnd1rk/hello-springboot)
...
...

可以看到,我们的程序也可以运行。

所以有下面的推论

jar -jar xx.jar 等于 java org.springframework.boot.loader.JarLauncher 等于 java -classpath "BOOT-INF/classes:BOOT-INF/lib/*" com.github.codeman.standalone.StandaloneApplication

至于我们的

@Controller
@SpringBootApplication
public class StandaloneApplication {

    public static void main(String[] args) {
        SpringApplication.run(StandaloneApplication.class, args);
    }
}

发生了什么?这是一个非常大的主题,我会在以后和大家讨论。这里就忽略了。

我们可以,SpringBoot把项目和依赖都打包从一个单独的
jar,可以让我们非常方便的部署,不需要再手动的管理源代码和依赖的文件,大大方便了我们的开发。

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions