← Back to list

How to Create a Spring Boot Entry File That Supports Both JAR and WAR Deployment

Learnings from my GSoC project: Migrating Apache Thrift to Spring Boot REST

Shivamrut · 2026-06-03 06:11 · 3 claps · 4.6 min read
#spring-boot #java #jar #war #strangler-fig-pattern
Open on Medium ↗
Wiki topics: GEN · Genomics & Sequencing EDU · Education & Learning 🔒 · Cybersecurity

How to Create a Spring Boot Entry File That Supports Both JAR and WAR Deployment

Learnings from my GSoC project: Migrating Apache Thrift to Spring Boot REST

When I started working on this migration in the GSoC project, the first thing that looked deceptively simple was the Spring Boot entry class. It is usually just a file with a main() method, but in my case it had a second personality depending on how the system is deployed.

We were not starting from a clean slate. The system was already running in a shared, central Tomcat server where multiple backend services coexist. Each service is deployed as a WAR. That immediately changes how you think about Spring Boot, because the default mental model (run a JAR, embedded server, everything isolated) no longer applies.

At the same time, the long-term direction is clear: move away from Apache Thrift based service communication and gradually replace it with modern Spring REST APIs. Eventually, everything should be runnable as independent JARs with embedded servers. But right now, that is not possible.

So we are effectively following a strangler fig approach; new parts grow around the old system until the old parts can slowly be removed.

That context is exactly why this entry class exists in its current form.

@SpringBootApplication
public class ConfigurationsApplication extends SpringBootServletInitializer {

This line looks normal, but it represents two deployment worlds living in the same codebase.

The @SpringBootApplication annotation is the Spring Boot “activation switch”. When Spring sees this, it understands three things at once:

  1. This class is a configuration source
  2. It should automatically configure dependencies based on what is present in the classpath
  3. It should scan the surrounding package structure to discover controllers, services, and repositories.

Spring Boot decides behavior based on what exists in the “classpath”, essentially the set of libraries loaded at runtime through Maven dependencies defined in pom.xml. If a dependency exists, Spring Boot assumes a capability and auto-configures it.

In simpler terms, this annotation is what allows Spring to behave like an opinionated framework instead of a manually wired configuration system. Without it, we would be writing a lot of explicit bean definitions and XML-style wiring. With it, Spring looks at what libraries exist (web, JPA, etc.) and sets up a working application context automatically.

But the interesting part starts after that.

The Legacy World: Tomcat and the WAR

The class extends SpringBootServletInitializer.

This is not needed if you only plan to run Spring Boot as a standalone JAR. But in our case, the application is deployed into an external Tomcat server. That means Tomcat is responsible for starting the application, not the main() method. This is a classic enterprise setup where a single server hosts multiple WAR files, and each WAR is just one module inside a larger ecosystem.

By extending SpringBootServletInitializer, I am essentially telling Spring Boot:

“If someone starts me inside a servlet container, here is how you should initialize the application context.”

This leads to the overridden method:

@Override
protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
    return application.sources(ConfigurationsApplication.class);
}

This method is only used in WAR deployments. It is part of a controlled startup lifecycle. Instead of Java calling main(), the servlet container calls into Spring Boot through this initializer.

What happens internally is roughly this flow:

Tomcat starts → it detects deployed WAR → it triggers Spring’s initializer → Spring calls configure() → SpringApplicationBuilder is given the application class → Spring builds the application context and wires everything.

So this method is not business logic at all. It is deployment glue. It exists purely to bridge Spring Boot with external servlet containers.

Now the second entry point exists alongside it:

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

This is the classic Spring Boot bootstrap method. If we run this class directly (outside Tomcat), Spring creates an embedded server (usually Tomcat by default), builds the application context, and starts listening for HTTP requests.

So effectively, this single class supports two runtime modes:

  • In legacy mode (current system): Tomcat starts everything and uses configure()
  • In modern mode (future direction): main() starts everything as a standalone JAR

This dual behavior is extremely important in a strangler fig migration. It allows us to slowly introduce Spring REST controllers while still living inside the old infrastructure without breaking deployment expectations.

The annotation @SpringBootApplication quietly supports both worlds. It is what ensures that whether Spring is started by main() or by Tomcat, the same auto-configuration and component scanning rules apply. So controllers written for new REST endpoints behave consistently regardless of deployment model.

From a design perspective, this entry class sits at the intersection of a few important patterns that become more visible once you work in legacy migration systems.

One is the Front Controller pattern. All incoming REST requests handled by Spring MVC, whether for old Thrift-based endpoints or new REST APIs, eventually pass through Spring’s DispatcherServlet. It acts as the central routing layer. This is part of Spring MVC’s Front Controller design. This is why adding REST endpoints does not require touching the servlet infrastructure directly.

Another is Inversion of Control. This entry class does not manually construct services or controllers. Instead, it triggers a system where Spring builds the entire object graph at runtime. The entry class only starts the process; it does not participate in object creation itself.

The SpringBootServletInitializer part is essentially a Template Method pattern implementation. We are overriding a specific hook (configure) while Spring controls the full lifecycle of application startup.

What makes this setup interesting in our project is that it is transitional by design. We are not choosing between JAR and WAR. We are supporting both simultaneously because the system is mid-migration. The WAR path keeps compatibility with the central Tomcat architecture, while the JAR path is the target end state once the strangler fig migration completes.

So this entry class is less about Spring Boot syntax and more about architectural transition. It is the bridge between a tightly coupled legacy deployment model and a modern microservice-style independent deployment model.

To conclude, this entry class is the boundary between two architectural eras of the system. One rooted in shared infrastructure and legacy service communication, and another moving toward independent, REST-based services. The fact that both can coexist in a single class is what makes gradual migration possible instead of requiring a complete rewrite.


메타데이터
post_id
e463501b02d4
slug
how-to-create-a-spring-boot-entry-file-that-supports-both-jar-and-war-deployment-e463501b02d4
url
https://medium.com/@maxus_red/how-to-create-a-spring-boot-entry-file-that-supports-both-jar-and-war-deployment-e463501b02d4
canonical_url
https://medium.com/@maxus_red/how-to-create-a-spring-boot-entry-file-that-supports-both-jar-and-war-deployment-e463501b02d4
author_url
https://medium.com/@maxus_red
status
ok
fetched_at
2026-06-16 19:09:56