Setting Up JSP Support and WAR Packaging with Spring Boot 3, Spring 6, and JDK 17

Motivation for the Upgrade

Legacy systems often rely on traditional Java Server Pages (JSP) and WAR deployments. Rather than rewriting the frontend, this guide demonstrates how to modernize the backend by migrating to Spring Boot 3.0, Spring Framework 6.0, and JDK 17, while maintaining JSP support and WAR packaging.

Key reasons for upgrading include:

  • JDK 17 offers significant performance improvements and is the current Long-Term Support (LTS) standard.
  • Spring 6 simplifies configuration compared to older XML-heavy Spring 5.x setups.
  • Tooling ecosystems (IDEs, build tools) are increasingly optimizing for JDK 17.

Reasons for retaining WAR and JSP:

  • WAR allows partial updates (replacing specific classes or views) without redeploying the entire application.
  • Externalizing Tomcat configuration is easier with a WAR deployment.
  • JSP remains a cost-effective option for existing view layers, supported by both modern Tomcat and Spring.

Environment Setup

  • OS: Windows 11
  • IDE: Spring Tool Suite (STS) 4.14.1
  • Server: Tomcat 10.0.22
  • JDK: 17
  • Frontend: jQuery 3.6.0

1. Project Initialization

Generate a project using Spring Initializr or the STS wizard. Ensure the following selections:

  • Packaging: WAR
  • Java Version: 17

2. Bootstrap Configuration

Create the main application class. It must extend SpringBootServletInitializer to support traditional WAR deployment on Servlet containers like Tomcat 10.

package com.example.legacyapp;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.web.servlet.support.SpringBootServletInitializer;
import org.springframework.boot.builder.SpringApplicationBuilder;

@SpringBootApplication
public class LegacyAppStarter extends SpringBootServletInitializer {

    @Override
    protected SpringApplicationBuilder configure(SpringApplicationBuilder builder) {
        return builder.sources(LegacyAppStarter.class);
    }

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

3. Maven Dependencies (pom.xml)

The most critical step is configuring the pom.xml to include JSP engine dependencies and the WAR plugin. Note that Spring Boot 3 uses Jakarta EE (jakarta.* packages) instead of Java EE (javax.*).

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" ...>
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>3.0.0</version>
    </parent>
    
    <groupId>com.example</groupId>
    <artifactId>legacy-web-app</artifactId>
    <version>1.0.0</version>
    <packaging>war</packaging>

    <properties>
        <java.version>17</java.version>
    </properties>

    <dependencies>
        <!-- Web Starter (Exclude embedded Tomcat to avoid conflicts with external Tomcat 10) -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
            <exclusions>
                <exclusion>
                    <groupId>org.springframework.boot</groupId>
                    <artifactId>spring-boot-starter-tomcat</artifactId>
                </exclusion>
            </exclusions>
        </dependency>

        <!-- Provided Tomcat APIs for compilation -->
        <dependency>
            <groupId>org.apache.tomcat</groupId>
            <artifactId>tomcat-servlet-api</artifactId>
            <version>10.0.22</version>
            <scope>provided</scope>
        </dependency>

        <!-- JSP Support -->
        <dependency>
            <groupId>org.apache.tomcat.embed</groupId>
            <artifactId>tomcat-embed-jasper</artifactId>
            <scope>compile</scope>
        </dependency>
        
        <!-- JSTL Tag Library -->
        <dependency>
            <groupId>jakarta.servlet.jsp.jstl</groupId>
            <artifactId>jakarta.servlet.jsp.jstl-api</artifactId>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
            <!-- WAR Plugin Configuration -->
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-war-plugin</artifactId>
                <configuration>
                    <failOnMissingWebXml>false</failOnMissingWebXml>
                </configuration>
            </plugin>
        </plugins>
    </build>
</project>

4. Web Configuration (Java Config)

Instead of web.xml, use a configuration class extending WebMvcConfigurationSupport. This handles view resolvers, static resources, and filter registration.

package com.example.legacyapp.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.ViewControllerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport;
import org.springframework.web.servlet.support.AbstractAnnotationConfigDispatcherServletInitializer;

import jakarta.servlet.Filter;
import org.springframework.web.filter.OncePerRequestFilter;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import java.io.IOException;

@Configuration
public class WebAppConfig extends WebMvcConfigurationSupport {

    @Override
    public void addViewControllers(ViewControllerRegistry registry) {
        registry.addViewController("/").setViewName("index");
    }

    @Override
    public void addResourceHandlers(ResourceHandlerRegistry registry) {
        registry.addResourceHandler("/assets/**").addResourceLocations("/assets/");
    }

    @Bean
    public Filter customRequestFilter() {
        return new OncePerRequestFilter() {
            @Override
            protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, 
                                            jakarta.servlet.FilterChain chain) 
                                            throws java.io.IOException, jakarta.servlet.ServletException {
                System.out.println("Request URI: " + request.getRequestURI());
                chain.doFilter(request, response);
            }
        };
    }
}

5. Controller and View

Create a simple controller that returns data for the JSP view.

package com.example.legacyapp.controller;

import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;

@Controller
public class DashboardController {
    
    @GetMapping("/dashboard")
    public String showDashboard(Model model) {
        model.addAttribute("message", "Successfully running on Spring Boot 3 + JDK 17");
        return "dashboard"; // Resolves to /WEB-INF/jsp/dashboard.jsp or similar depending on resolver
    }
}

Example JSP (src/main/webapp/dashboard.jsp):

<%@ page contentType="text/html;charset=UTF-8" %>
<html>
<head>
    <title>Legacy App Modernized</title>
    <script src="/assets/jquery-3.6.0.min.js"></script>
</head>
<body>
    <h1 style="color: green;">${message}</h1>
    <div id="content">Loaded via JSP</div>
</body>
</html>

6. Building the WAR

Run the Maven command to package the application:

mvn clean package -DskipTests

This generates a .war file in the target directory.

7. Deployment to Tomcat 10

  1. Ensure JAVA_HOME points to JDK 17 in Tomcat's bin/setenv.bat (or catalina.sh).
  2. Copy the generated legacy-web-app.war into the webapps folder of Tomcat 10.
  3. Start Tomcat using startup.bat.

Access the application at http://localhost:8080/legacy-web-app/.

Tags: Spring Boot 3 Spring Framework 6 JDK 17 JSP Tomcat 10

Posted on Tue, 04 Aug 2026 16:07:29 +0000 by davissj