0% completed
Running and Packaging an Application
On This Page
Three Ways to Run the Application
Inside the Executable Jar
Reading the Startup Log
Changing the Port
Fixing the Opening Failure
Key Takeaways
Practice Questions
A developer starts the booking service from a terminal, and it stops at once with this message:
*************************** APPLICATION FAILED TO START *************************** Description: Web server failed to start. Port 8080 was already in use. Action: Identify and stop the process that's listening on port 8080 or configure this application to listen on another port.
An earlier copy of the service was still running in another window. That copy held port 8080, so the second one could not open it, and the process exited with code 1. The message names the cause and gives two fixes, and both are easy once you know how the application runs and where its settings come from.
That knowledge is the subject here: three ways to start the application, what the packaged jar contains, how to read the startup log, and how to change the port.
Three Ways to Run the Application
A Spring Boot application is an ordinary Java program, so anything that can call its main method can start it. Three ways are common, and each suits a different task:
| Way | Command | When to use it |
|---|---|---|
| From the IDE | Run BookingApplication like any class with main | Daily development and debugging |
| Through the build tool | ./mvnw spring-boot:run | Running without an IDE, from compiled classes |
| As a packaged jar | ./mvnw package, then java -jar target/booking-0.0.1-SNAPSHOT.jar | Testing what production will run, and production itself |
The first two run the compiled classes directly from the project folder. When we used spring-boot:run on the booking service, Maven checked that the compiled classes were up to date and started the application. The log reported the application ready after 1.016 seconds, and GET /hello answered as before.
The third way is the one that matters for deployment. The command ./mvnw package compiles the code, runs the tests, and writes one file, target/booking-0.0.1-SNAPSHOT.jar. That single file is the whole application.
Inside the Executable Jar
The packaged file is an executable jar: one archive that holds your classes, every library they need, and a small launcher that starts them. For the booking service it is about 20 MB, and it contains three groups of files:
- BOOT-INF/classes holds your compiled code and resources: BookingApplication.class, HelloController.class, and application.properties.
- BOOT-INF/lib holds the 34 library jars that the starter brought in, such as spring-webmvc and tomcat-embed-core.
- org/springframework/boot/loader holds the launcher, the code that knows how to read this layout.
The launcher exists because of a gap in Java itself. The Spring Boot documentation states that Java does not provide any standard way to load jar files that are nested inside another jar. The launcher fills that gap.
The jar's manifest (META-INF/MANIFEST.MF, the file that java -jar reads first) connects the parts:
Main-Class: org.springframework.boot.loader.launch.JarLauncher Start-Class: com.example.booking.BookingApplication Spring-Boot-Classes: BOOT-INF/classes/ Spring-Boot-Lib: BOOT-INF/lib/
The command java -jar starts the class named by Main-Class, which is the launcher, not your code. The launcher makes the nested libraries and classes loadable, and then it calls the main method of the Start-Class, BookingApplication.
Notice that your own code is the smallest part of the jar: two classes and one settings file next to 34 libraries. Almost everything inside this archive comes from the web starter listed in the build file.
Reading the Startup Log
The startup log is the record of what Spring Boot did while starting, one event per line, and reading it is the first step when something goes wrong. Each line has a timestamp, a level such as INFO, the process number, the application name [booking], the thread, the class that wrote it, and a message. These are the main messages from one run of the jar, with the timestamp and the other fields removed:
:: Spring Boot :: (v4.1.2) Starting BookingApplication v0.0.1-SNAPSHOT using Java 21.0.12.1 with PID 17620 No active profile set, falling back to 1 default profile: "default" Tomcat initialized with port 8080 (http) Starting Servlet engine: [Apache Tomcat/11.0.27] Root WebApplicationContext: initialization completed in 636 ms Tomcat started on port 8080 (http) with context path '/' Started BookingApplication in 1.265 seconds (process running for 1.678) Initializing Spring DispatcherServlet 'dispatcherServlet' Completed initialization in 2 ms
Read from the top, the log answers four questions:
- Which versions are running? Spring Boot 4.1.2, Java 21.0.12.1, and Tomcat 11.0.27.
- Which settings were used? No profile was active, so Spring Boot used the profile named default, and no profile-specific settings applied.
- Which port is open? The line "Tomcat started on port 8080" is the proof that the server is listening.
- How long did startup take? The application was ready after 1.265 seconds.
The last two lines appeared only when the first request arrived, not during startup. The DispatcherServlet, the part of Spring MVC that routes requests to controllers, is prepared on first use, which took 2 ms in this run. The first request therefore does a little one-time work that later requests skip.
Changing the Port
The port is the setting server.port, and Spring Boot can read it from several places, with a fixed order deciding which value wins. Three places are common:
- The settings file: a line
server.port=8081in application.properties. - An environment variable:
SERVER_PORT=8082, set before the command. - A command-line argument:
java -jar target/booking-0.0.1-SNAPSHOT.jar --server.port=8083.
To see the order, we set all three at once and started the jar. Before each of the next two starts, we removed the highest remaining source:
| Sources set | Tomcat started on port |
|---|---|
| File, environment variable, and argument | 8083 |
| File and environment variable | 8082 |
| File only | 8081 |
The command-line argument beats the environment variable, and the environment variable beats the file. This order lets one jar run everywhere. The file holds the normal value, and each environment changes only what it must, without a new build.
Notice that removing a source never fails the start. The next source down supplies the value, and the default of 8080 applies only when no source sets the port.
Fixing the Opening Failure
The failure at the start now has two clear fixes. The developer can stop the earlier copy that holds port 8080. Or the developer can start the second copy on another port without editing any file, with --server.port=8081 on the command line or SERVER_PORT=8081 in the environment.
Key Takeaways
- A Spring Boot application starts from the IDE, through ./mvnw spring-boot:run, or as a packaged jar with java -jar.
- The executable jar holds your classes in BOOT-INF/classes, the libraries in BOOT-INF/lib, and a launcher that loads both.
- The manifest names the launcher as Main-Class and your application class as Start-Class.
- The startup log shows the versions, the active profile, the open port, and the startup time.
- For server.port, a command-line argument beats an environment variable, which beats the settings file.
Running a single endpoint proves the setup works, but a service needs something to serve. The Booking Service describes what this course builds and the first problem it must solve.
Practice Questions
1. Why does java -jar start the class JarLauncher instead of BookingApplication?
<details><summary>Show answer</summary>Because Java cannot load jar files nested inside a jar, and the libraries of the application are nested in BOOT-INF/lib. The launcher is the Main-Class, so it runs first and makes the nested libraries and classes loadable. It then calls the main method of the Start-Class, BookingApplication.
</details>2. The service runs in production with SERVER_PORT=9090 set in the environment. An operator also adds server.port=9091 to application.properties and rebuilds. Which port does the service use?
Port 9090. The environment variable is higher in the order than the settings file, so its value wins. The change to the file has no effect while SERVER_PORT is set, which is a common source of confusion when a setting seems to be ignored.
</details>3. A developer measures the response time of the first request after startup and finds it slower than the following ones. Is something wrong?
<details><summary>Show answer</summary>No. The first request does one-time work that later requests skip, such as preparing the DispatcherServlet. The log shows "Initializing Spring DispatcherServlet" only when the first request arrives. A slower first request is therefore expected.
</details>4. A teammate tests changes with spring-boot:run only and never builds the jar. What could they miss?
<details><summary>Show answer</summary>Problems that appear only in the packaged application. The command spring-boot:run starts the compiled classes from the project folder, while production runs the jar through the launcher. A resource that is missing from the jar, or a test that fails during ./mvnw package, shows up only when the jar is built. Building and running the jar before a release tests what production will run.
</details>Reading Progress
0%
On This Page
Three Ways to Run the Application
Inside the Executable Jar
Reading the Startup Log
Changing the Port
Fixing the Opening Failure
Key Takeaways
Practice Questions