Suppose this is a simple SpringBoot application
If we want to start it, we usually need two steps:
- Package it into a jar file
- Run this jar file
Specifically, it is like this
# Packaged into a separate jar file
$ mvn package
# Run this jar file
$ 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)
...
...
Then the question comes, what happened in the java -jar xx.jar
? Why is our program started?
First, let's unpack the packaged file to see what it is.
The jar files are compressed by the zip format, so extracting the jar package is equivalent to extracting the zip file. Any tool that can extract the zip file can extract the jar file.
$ unzip target/standalone-0.0.1-SNAPSHOT.jar -d temp
$ 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
...
At this point, readers are encouraged to try it on their own computers.
We can see that there are three folders.
First take a look at the BOOT-INF
folder. There are two more folders classes
and lib
in the folder. It can be seen that the classes
folder stores our compiled source code , and the lib
folder stores the jar packages we depend on, including our tomcat, which is also packaged into this folder.
So what does the METE-INFO
folder do?
Here you need to add an extra piece of knowledge:
According to the java specification and the jar specification, when we execute jar -jar xx.jar
, it looks for the META-INFO/MANIFEST.MF file in the jar package and reads the Main-Class field.
In our project, it is org.springframework.boot.loader.JarLauncher
.
In other words, java -jar target/standalone-0.0.1-SNAPSHOT.jar
is equivalent to java org.springframework.boot.loader.JarLauncher
.
Let’s try it:
$ 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
...
...
As you can see, the program can indeed be started in this way.
Now that the new problem is coming again, what happened when java org.springframework.boot.loader.JarLauncher
?
Here we look at the official documentation:
Readers are asked to click on their own.
org.springframework.boot.loader.JarLauncher
will be used as a bootstrap class that will start the class defined in Start-Class
in META-INF/MANIFEST.MF
.
In our project, its value is com.github.codeman.standalone.StandaloneApplication
.
You can see that this is the entry file for our own project.
Then let's start it manually:
$ 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)
...
...
As you can see, our program can also run.
So there is the following inference:
jar -jar xx.jar
is equal to java org.springframework.boot.loader.JarLauncher
equal to java -classpath "BOOT-INF/classes:BOOT-INF/lib/*" com.github.codeman.standalone.StandaloneApplication
.
As for what happened to our SpringApplication.run(StandaloneApplication.class, args)
?
@Controller
@SpringBootApplication
public class StandaloneApplication {
public static void main(String[] args) {
SpringApplication.run(StandaloneApplication.class, args);
}
}
This is a very big topic, I will discuss it with you later, I will ignore it here.
The benefits of SpringBoot doing this are: packaging the project and dependencies from a single jar allows us to deploy it very easily, without having to manually manage the source code and dependent files.
The article here is permanently saved on github.
https://github.com/codeman-cs/SpringBoot/wiki/How-does-SpringBoot-start-on-the-command-line%3F