Serving JSP with Spring Boot 3

Learn to create and configure JSP view resolver in Spring Boot 3 application which uses JSP template files to render the view layer. Also, learn to build and deploy the application and serve static content in the JSP files.

1. Project Structure

For reference, the files in this application are placed as the given structure in the image.

2. Maven

To sever an application using JSP, we need the following dependencies. Note that we are using the Jakarta Servlet API which uses the Jakarta namespace required by Spring Boot 3.

  • spring-boot-starter-web
  • tomcat-embed-jasper
  • jakarta.servlet.jsp.jstl-api
  • jakarta.servlet.jsp.jstl
<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
  <groupId>org.apache.tomcat.embed</groupId>
  <artifactId>tomcat-embed-jasper</artifactId>
  <scope>provided</scope>
</dependency>
<dependency>
  <groupId>jakarta.servlet.jsp.jstl</groupId>
  <artifactId>jakarta.servlet.jsp.jstl-api</artifactId>
  <version>3.0.0</version>
</dependency>
<dependency>
  <groupId>org.glassfish.web</groupId>
  <artifactId>jakarta.servlet.jsp.jstl</artifactId>
  <version>3.0.1</version>
</dependency>

Additionally, we must use the war packaging as jar packaging is not supported as discussed in the official documentation.

<packaging>war</packaging>

3. Application Setup

3.1. Extend SpringBootServletInitializer in the Application Class

The first step in producing a deployable war file is to provide a SpringBootServletInitializer subclass and override its configure() method. This makes use of Spring Framework’s Servlet 3.0 support and allows you to configure the application when it is launched by the Servlet container.

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

@SpringBootApplication
public class App extends SpringBootServletInitializer {

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

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

3.2. Web Controller

The controller class is a regular @Controller class with request mapping annotations. Please note that we should not use the @RestController annotation, by mistake.

@Controller
public class ItemController {

  @Autowired
  ItemService itemService;

  @RequestMapping("/view-items")
  public ModelAndView viewBooks(ModelAndView model) {

    model.setViewName("view-items");
    model.addObject("items", itemService.getAll());
    return model;
  }
}

3.3. Model and Service

For reference, we are using a simple POJO Item as model and ItemService for returning a list of items. The service returns two items. In a real application, it will connect to the database and fetch the details from the database or any other source.

@Service
public class ItemService {

  public List<Item> getAll() {
    return List.of(new Item(1L, "Item - 1"), new Item(2L, "Item - 2"));
  }
}
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Item {

  private Long id;
  private String name;
}

4. Setting up JSP View Resolver

To resolve JSP files location and automatically configure the JSP view resolver, we can have two approaches:

4.1. Using application.properties

The following properties automatically configure the Spring MVC to serve the content from the configured location and register the JstlView class. It is a specialization of InternalResourceView for JSTL pages, i.e. JSP pages.

Once registered, every view name returned from a handler will be translated to a JSP resource (for example: “home” → “/WEB-INF/jsp/home.jsp“), using this view class to enable explicit JSTL support.

spring.mvc.view.prefix=/WEB-INF/view/
spring.mvc.view.suffix=.jsp

4.2. Using Java Configuration

We can configure the JSP view resolution in the Java configuration also, as follows. Do not forget to use the @EnableWebMvc which configures the other necessary beans to support a MVC application.

@Configuration
@EnableWebMvc
public class WebMvcConfig implements WebMvcConfigurer {

  @Bean
  public ViewResolver viewResolver() {
    final InternalResourceViewResolver bean = new InternalResourceViewResolver();
    bean.setViewClass(JstlView.class);
    bean.setPrefix("/WEB-INF/jsp/");
    bean.setSuffix(".jsp");
    return bean;
  }

  @Override
  public void configureViewResolvers(ViewResolverRegistry registry) {
    registry.viewResolver(viewResolver());
  }
}

4.3. Serving Static Content

By default, Spring MVC serves the static files (JS, CSS etc) from the following directories:

  • /src/main/resources
  • /src/main/resources/static

We can configure the custom URI and directory location as follows. The following configuration will serve a file http://localhost:8080/static/css/style.css from the location /src/main/resources/static/css/style.css.

@Configuration
@EnableWebMvc
public class WebMvcConfig implements WebMvcConfigurer {

  //...

  @Override
  public void addResourceHandlers(ResourceHandlerRegistry registry) {
    registry
        .addResourceHandler("/static/**")
        .addResourceLocations("classpath:/static/")
        .setCachePeriod(3600)
        .resourceChain(true)
        .addResolver(new PathResourceResolver());
  }
}

5. JSP Files

The JSP files can be placed in /webapp/WEB-INF/jsp directory as /WEB-INF/jsp path has been configured with the view resolver. Add the webapp directory as the resources root so its content is copied to the root directory when building the project.

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
<html>
    <head>
        <title>View Items</title>
        <link href="/static/css/style.css" rel="stylesheet">
    </head>
    <body>
        <table>
            <thead>
                <tr>
                    <th>Id</th>
                    <th>Name</th>
                </tr>
            </thead>
            <tbody>
                <c:forEach items="${items}" var="item">
                    <tr>
                        <td>${item.id}</td>
                        <td>${item.name}</td>
                    </tr>
                </c:forEach>
            </tbody>
        </table>
    </body>
</html>

6. Package and Run the Application

After the code has been created, we can package the application from the terminal:

mvc clean install

It will create a war file in the target directory. We can run the war file in two ways:

  • Using the java -jar command from the terminal.
  • Deploying in the Tomcat application server.

To run the application from the terminal, run the following command:

java -jar .\target\jsp-demo-1.0-SNAPSHOT.war

It will start the embedded Tomcat server and deploy the application with it.

7. Demo

After the application has been deployed, we can access a valid application URL in the browser and the JSP page will be rendered in the browser.

8. FAQs

8.1. Views are not resolved with 404 Error

In most cases, this error indicates that you are trying to run the application as embedded jar deployment or you are not using the java-jar command to run the application.

To fix this issue, you must run the application with java-jar command or deploy it inside a Tomcat server.

java -jar .\target\jsp-demo-1.0-SNAPSHOT.war

8.2. ClassNotFoundException: jakarta.servlet.jsp.jstl.core.Config

This error indicates that you are still using the javax.servlet:jstl dependency. With the latest Spring Boot release, we must use the Jakarta namespaces so add the following dependency.

<!-- Remove javax packages -->
<dependency>
  <groupId>javax.servlet</groupId>
  <artifactId>jstl</artifactId>
  <version>1.2</version>
</dependency>

<!-- Add jakarta packages -->
<dependency>
  <groupId>jakarta.servlet.jsp.jstl</groupId>
  <artifactId>jakarta.servlet.jsp.jstl-api</artifactId>
  <version>3.0.0</version>
</dependency>
<dependency>
  <groupId>org.glassfish.web</groupId>
  <artifactId>jakarta.servlet.jsp.jstl</artifactId>
  <version>3.0.1</version>
</dependency>

9. Conclusion

In this Spring Boot JSP example, we learned to serve the JSP pages in a Spring boot 3 application, configure the view resolution, and serve static content with a demo. We also learned to fix some common mistakes that developers make when serving the JSP page.

Happy Learning !!

Source Code on Github

Leave a Comment

  1. Hi Lokesh

    Is there any way we can configure JSP Tiles with Spring boot 3.2.0
    Seems like it is not possible , would you please share some example ??

    Reply
        • Spring recommends thymleaf and it integrates as well. Even migration may not be that difficult if you have Spring MVC style form/data-bindings. BUT if you have stateless REST APIs then using a dedicated and mature UI framework (such as Angular or React) makes more sense to me. These frameworks have way more large community support and ready-made components to use.

          Reply
  2. HI,

    Using src/main/webapp/WEB-INF/jsp/welcome.jsp is discouraged if you want to deploy via a jar package.

    “Do not use the src/main/webapp folder if your application will be packaged as a jar. Although this folder is a common standard, it will only work with war packaging and it will be silently ignored by most build tools if you generate a jar.”
    Reference: https://docs.spring.io/spring-boot/docs/1.1.4.RELEASE/reference/htmlsingle/#using-boot-structuring-your-code

    Reply
  3. Whitelabel Error Page
    This application has no explicit mapping for /error, so you are seeing this as a fallback.

    Sun Jan 26 12:22:18 IST 2020
    There was an unexpected error (type=Internal Server Error, status=500).
    The absolute uri: http://www.springframework.org/tags cannot be resolved in either web.xml or the jar files deployed with this application

    Reply
  4. Worth mentioning, this project as a standalone would not work as Versions of dependency items are missing in pom.xml. After a little research I found it is depending upon a ‘spring-boot-starter-parent’ project as baseline. My guess is, version of dependencies are being controlled from the parent project. As Ankit has mentioned above, we need a tool to download the dependencies from parent project i.e. mvn eclipse:eclipse -DwtpVersion=2.0 command.

    org.springframework.boot
    spring-boot-starter-parent
    2.1.5.RELEASE

    Reply
  5. Thank you it’s working on my machine
    for all those who don’t able to import this in your STS .plz try the below steps.
    1) unzip the file and copy the unzipped file to your work-space(i.e the drive) u have chosen for STS.
    2)open the unzipped folder (normally in windows) and got to POM file. Now click on the address bar .and copy it.
    ur address bar should look something like this.

    D:\SPRING BOOT 4\WORKSPACE SPRING BOOT\spring-boot-demo-jsp-example\spring-boot-demo

    note: the address at last must be showing \spring-boot-demo

    3)now open cmd and browse to the specific drive (if it is other than C drive) abd paste the copied address and press enter

    4) NOw the important part … Type the command Exactly like I have written.
    mvn eclipse:eclipse -DwtpVersion=2.0

    and press enter if everything is right it will start downloading the required files and Build Success will be shown.

    Now just open ur STS and try to import the file u will be able to import it. :)

    Reply
  6. As a fix ,in pom.xml, for spring-boot-starter-tomcat remove tag. It is needed only if its not a spring boot application. It is failing because there is no explicit tomcat server.

    After fixing this issue also example is not working properly.
    Suppose I am hiiting localhost:8080/next
    then response is

    2019-02-14 16:29:20.601 DEBUG 2436 — [nio-8080-exec-4] s.w.s.m.m.a.RequestMappingHandlerMapping : Looking up handler method for path /WEB-INF/view/next.jsp
    2019-02-14 16:29:20.602 DEBUG 2436 — [nio-8080-exec-4] s.w.s.m.m.a.RequestMappingHandlerMapping : Did not find handler method for [/WEB-INF/view/next.jsp]

    basically is not treating it a request to get static resource.

    If you create a method in controller with @Requestparam(/WEB-INF/view/next.jsp) then it gets detected.

    Lokesh could you help us on this.

    Reply
  7. I cannot get this to work. I downloaded your source and imported it as a project into Intellij Idea. When I try to run it, I get the following exception:

    java.lang.IllegalStateException: ApplicationEventMulticaster not initialized – call ‘refresh’ before multicasting events via the context: org.springframework.context.annotation.AnnotationConfigApplicationContext@1e4a7dd4: startup date [Tue Jul 10 21:12:36 PDT 2018]; root of context hierarchy

    Reply
  8. When I run this code using “mvn spring-boot:run”, then application is running fine. But when I converted the same application into executable jar file, (changed war to jar in pom.xml) and executed the jar file then this error is coming,

    Whitelabel Error Page
    This application has no explicit mapping for /error, so you are seeing this as a fallback.
    
    Tue Dec 19 16:01:29 IST 2017
    There was an unexpected error (type=Not Found, status=404).
    /WEB-INF/view/index.jsp
    

    Could you please resolve this issue. I need this very urgent, in my project as well I’m encountering the same issue. Here is email id, “tshravan2012@gmail.com”. Thanks a lot for your time.

    Reply
  9. Your example doesn’t work.
    I encountered next error: “.. Caused by: java.lang.ClassNotFoundException: javax.servlet.Filter”
    which shows me that it caused by explicit dependency for tomcat with provided: https://stackoverflow.com/questions/30374316/spring-boot-java-lang-noclassdeffounderror-javax-servlet-filter
    I wonder why should i have that dependency at all if spring uses tomcat embedded by default. So i removed it, but now i obtained: “Whitelabel Error Page”

    Reply
  10. I have followed you tutorial to add view layer to my existing spring boot rest application. Following is the method i am trying to use a home page view(home.jsp). I am not getting the jsp page render, instead I am getting a string ‘home’ when i ran the project. Please help me in fixing issue, I am new to spring boot.

    @RequestMapping(value = “/dashboard”)
    @ResponseBody
    public String getMyMessage() {

    return “home”;
    }

    my MvcConfiguration class is:

    import org.springframework.context.annotation.Configuration;
    import org.springframework.web.servlet.config.annotation.EnableWebMvc;
    import org.springframework.web.servlet.config.annotation.ViewResolverRegistry;
    import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
    import org.springframework.web.servlet.view.InternalResourceViewResolver;
    import org.springframework.web.servlet.view.JstlView;

    @Configuration
    @EnableWebMvc
    public class MvcConfiguration extends WebMvcConfigurerAdapter{
    @Override
    public void configureViewResolvers(ViewResolverRegistry registry) {
    InternalResourceViewResolver resolver = new InternalResourceViewResolver();
    resolver.setPrefix(“/WEB-INF/views/”);
    resolver.setSuffix(“.jsp”);
    resolver.setViewClass(JstlView.class);
    registry.viewResolver(resolver);
    }
    }

    Reply
    • @Neelam Panwar, You need to remove @ResponseBody annotation from your getMyMessage() method. That is for render a JSON object or a string. If you want to return a JSP view. you just need to remove that.

      Reply

Leave a Comment

About Us

HowToDoInJava provides tutorials and how-to guides on Java and related technologies.

It also shares the best practices, algorithms & solutions and frequently asked interview questions.

Our Blogs

REST API Tutorial

Dark Mode

Dark Mode