Spring Boot - Serving JSP pages

[Updated: Jan 16, 2017, Created: Jan 6, 2017]

Placing dynamic contents like JSP pages into static locations does not work.

In a war project, JSP pages are served from src/main/webapp/WEB-INF/.

In a Jar project, JSP pages cannot simply be served from webapp location or from src/main/resources/. That's because of the limitation stated in boot ref docs.

The location src/main/webapp/WEB-INF may work in exploded form but should be avoided. Form boot ref docs:

Do not use the src/main/webapp directory if your application will be packaged as a jar. Although this directory 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.

Fortunately we have another option for a Jar project: Servlet 3.0 specification allows to have dynamic pages in src/main/resources/META-INF/resources/ (Please check out an example here).


Let's explore both options with examples.


Serving JSPs with Jar Packaging


Creating Controller

@Controller
public class MyController {

   @RequestMapping("/")
   public String handler (Model model) {
       model.addAttribute("msg",
                          "a jar packaging example");
       return "myView";
   }
}

The JSP page

Save the following page at location: src/main/resources/META-INF/resources/WEB-INF/views/myView.jsp

<h2>From JSP page </h2>
<%@ page language="java"
    contentType="text/html; charset=ISO-8859-1"
    pageEncoding="ISO-8859-1"%>
<html>
<body>
    Message : ${msg}
</body>
</html>

The view resolver

Boot auto-configuration implicitly adds many view resolvers including InternalResourceViewResolver. This resolver is used to locate and render JSP pages. A boot application client just need to add spring.mvc.view.prefix and spring.mvc.view.suffix properties:

src/main/resources/application.properties:

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

Specifying Jar packaging in pom.xml

<project ....>
 <modelVersion>4.0.0</modelVersion>

 <groupId>com.logicbig.example</groupId>
 <artifactId>boot-jar-serving-jsp</artifactId>
 <version>1.0-SNAPSHOT</version>
 <packaging>jar</packaging>
.......

</project>

We can also skip the packaging element because maven default packaging type is 'jar'.


JSP related maven dependencies

  <dependency>
   <groupId>org.apache.tomcat.embed</groupId>
   <artifactId>tomcat-embed-jasper</artifactId>
  </dependency>
  <dependency>
   <groupId>javax.servlet</groupId>
   <artifactId>jstl</artifactId>
  </dependency>

Running application

The easiest way to run application successfully add Spring Boot Maven plugin and run it from IDE or command line:

 mvn spring-boot:run

Output




Example Project


Dependencies and Technologies Used :
  • spring-boot-starter-web 1.4.2.RELEASE: Starter for building web, including RESTful, applications using Spring MVC. Uses Tomcat as the default embedded container.
    Corresponding Spring version: 4.3.4.RELEASE
  • tomcat-embed-jasper 8.5.6: Core Tomcat implementation.
  • jstl 1.2 javax.servlet:jstl
  • JDK 1.8
  • Maven 3.3.9

Serving Jsp With Jar Packaging Select All Download
  • boot-jar-serving-jsp
    • src
      • main
        • java
          • com
            • logicbig
              • example
        • resources
          • META-INF
            • resources
              • WEB-INF
                • views



Serving JSPs with War Packaging

We are going to modify above example to package it as war, which can be deployed to external web container as well.


Specifying packaging as war

<project ....>
 <modelVersion>4.0.0</modelVersion>

 <groupId>com.logicbig.example</groupId>
 <artifactId>boot-war-serving-jsp</artifactId>
 <version>1.0-SNAPSHOT</version>
 <packaging>war</packaging>
.......
</project>


Location for JSPs

Here we have two options:

We can leave the jsp pages at the same location i.e. src/main/resources/META-INF/resources/WEB-INF/views/
Or
We can place them in the traditional webapp location i.e. src/main/webapp/WEB-INF/views/

Both options will work.

In this example we are going to place it under webapp directory.



The main class

@SpringBootApplication
public class WebMvcConfigExample extends SpringBootServletInitializer {

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

   public static void main (String[] args) {

       SpringApplication app =
                 new SpringApplication(WebMvcConfigExample.class);
       app.run(args);
   }
}

Why SpringBootServletInitializer should be subclassed?

For producing a deployable war file, we have to provide a SpringBootServletInitializer subclass and override its configure method.

In above code, the main class is extending SpringBootServletInitializer which in turn extends WebApplicationInitializer. WebApplicationInitializer is based on servlet 3.0 ServletContainerInitializer concept. The purpose of this extension is: WebApplicationInitializer sets up servlet context and additionally it asks subclass to set up the source (the class annotated with @SpringBootApplication) of the SpringApplication. so that it can call SpringApplication#run() with a valid source and can do it's auto configuration and application level bean wiring etc. This arrangement is only needed when application is deployed in a servlet container as a war file. In a web container of course 'main method' can't get executed like it does in a stand-alone exploded application or executable jar or war.

If interested check out onStartup(..) method of SpringBootServletInitializer.java.



Exclude embedded tomcat server from deployable war file

spring-boot-starter-web has transitive dependency of spring-boot-starter-tomcat. We have to change it's default scope to 'provided' so that it won't get included in the resultant war, otherwise the embedded servlet container will interfere with the servlet container.

<dependency>
   <groupId>org.springframework.boot</groupId>
   <artifactId>spring-boot-starter-tomcat</artifactId>
   <scope>provided</scope>
</dependency>


There are no major changes with the controller and application.properties.



Running the war application

After packaging the war, we can deploy it to a servlet container or we can still execute it in exploded form:

 mvn spring-boot:run

Output




Example Project


Dependencies and Technologies Used :
  • spring-boot-starter-web 1.4.2.RELEASE: Starter for building web, including RESTful, applications using Spring MVC. Uses Tomcat as the default embedded container.
    Corresponding Spring version: 4.3.4.RELEASE
  • spring-boot-starter-tomcat 1.4.2.RELEASE: Starter for using Tomcat as the embedded servlet container. Default servlet container starter used by spring-boot-starter-web.
    Corresponding Spring version: 4.3.4.RELEASE
  • tomcat-embed-jasper 8.5.6: Core Tomcat implementation.
  • jstl 1.2 javax.servlet:jstl
  • JDK 1.8
  • Maven 3.3.9

Serving Jsp With War Packaging Select All Download
  • boot-war-serving-jsp
    • src
      • main
        • java
          • com
            • logicbig
              • example
        • resources
        • webapp
          • WEB-INF
            • views