Spring MVC 5 - Theme Resolver example

Posted on February 18, 2018


In this post, we will learn how to use the Spring framework themes to change the look-and-feel of your application.

Tools and technologies used for this application are -

  • Spring MVC 5.0.3.RELEASE
  • Servlet API 4.0.0
  • Java SE 9
  • Bootswatch v4.0.0  
  • Maven 3.5.2
  • Jetty Maven plugin 9.4.8
  • Eclipse Oxygen.2 Release (4.7.2)
     

Project structure

Final project structure of our application will look like as follows.

spring-mvc-theme-example.png

Related - How to create a web project using maven build tool in eclipse IDE.

Jar dependencies

Open pom.xml file of your maven project and add the following jars dependencies in your in it.

pom.xml

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
   xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
   <modelVersion>4.0.0</modelVersion>
   <groupId>com.boraji.tutorial.spring</groupId>
   <artifactId>spring-mvc-theme-resolver-example</artifactId>
   <version>0.0.1-SNAPSHOT</version>
   <packaging>war</packaging>
   <name>Spring MVC - Static Resources Mapping example</name>
   <properties>
      <failOnMissingWebXml>false</failOnMissingWebXml>
      <maven.compiler.source>9</maven.compiler.source>
      <maven.compiler.target>9</maven.compiler.target>
   </properties>
   <dependencies>
      <dependency>
         <groupId>org.springframework</groupId>
         <artifactId>spring-webmvc</artifactId>
         <version>5.0.3.RELEASE</version>
      </dependency>
      <dependency>
         <groupId>javax.servlet</groupId>
         <artifactId>javax.servlet-api</artifactId>
         <version>4.0.0</version>
         <scope>provided</scope>
      </dependency>
      <dependency>
         <groupId>javax.servlet.jsp</groupId>
         <artifactId>javax.servlet.jsp-api</artifactId>
         <version>2.3.2-b02</version>
         <scope>provided</scope>
      </dependency>
   </dependencies>
   <build>
      <plugins>
         <!-- Embedded Jetty HTTP Server for testing war application -->
         <plugin>
            <groupId>org.eclipse.jetty</groupId>
            <artifactId>jetty-maven-plugin</artifactId>
            <version>9.4.8.v20171121</version>
         </plugin>
      </plugins>
   </build>
</project>

Themes Configuration

In this example, we are using the Bootswatch’s themes. You can create your own theme files (i.e. stylesheet files) for your application.

To use the Bootswatch’s themes in your application, download the Cerulean and Pulse themes from the below links-

Save theme files under src/main/webapp/statics/css folder as bootstrap-cerulean.css and bootstrap-pulse.css.

Now, create two properties files under src/main/resources/theme as follows-

cerulean.properties

stylesheet=bootstrap-cerulean.css

bootstrap-pulse.css

stylesheet=bootstrap-pulse.css

To load these theme properties files, you need to register the ThemeSource interface or its implementation (such as ResourceBundleThemeSource) in Spring application context.

The keys of the properties are known as themed element. In your JSP views, you can use <spring:theme> tag to retrieve the key’s values.

 

Spring Web Configuration and Registering Theme Resolver

To configure the theme resolver in  your web @Configuration class  -

  • First, register the ThemeSource by declaring a @Bean method with themeSource name.
  • Next, you need to register the ThemeResolver to decide which theme to use. 
  • Finally, register the ThemeChangeInterceptor in overridden method addInterceptors() of WebMvcConfigurer to set the request parameter for changing the current theme.

MvcWebConfig.java

package com.boraji.tutorial.spring.config;

import java.util.concurrent.TimeUnit;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.CacheControl;
import org.springframework.ui.context.ThemeSource;
import org.springframework.ui.context.support.ResourceBundleThemeSource;
import org.springframework.web.servlet.ThemeResolver;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.ViewResolverRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import org.springframework.web.servlet.theme.CookieThemeResolver;
import org.springframework.web.servlet.theme.ThemeChangeInterceptor;

@Configuration
@EnableWebMvc
@ComponentScan("com.boraji.tutorial.spring.controller")
public class MvcWebConfig implements WebMvcConfigurer {

   @Override
   public void configureViewResolvers(ViewResolverRegistry registry) {
      registry.jsp("/WEB-INF/views/", ".jsp");
   }

   // Resources mapping
   @Override
   public void addResourceHandlers(ResourceHandlerRegistry registry) {
      registry.addResourceHandler("/resources/**").addResourceLocations("/statics/")
            .setCacheControl(CacheControl.maxAge(2, TimeUnit.HOURS).cachePublic());
   }

   @Bean
   public ThemeSource themeSource() {
      ResourceBundleThemeSource themeSource = new ResourceBundleThemeSource();
      themeSource.setBasenamePrefix("theme/");
      return themeSource;
   }

   @Bean
   public ThemeResolver themeResolver() {
      CookieThemeResolver resolver = new CookieThemeResolver();
      resolver.setDefaultThemeName("pulse");
      return resolver;
   }

   @Override
   public void addInterceptors(InterceptorRegistry registry) {
      ThemeChangeInterceptor themeChangeInterceptor = new ThemeChangeInterceptor();
      themeChangeInterceptor.setParamName("theme");
      registry.addInterceptor(themeChangeInterceptor);
   }
}

 

Servlet container Initialization and configuration 

In Spring MVC, The DispatcherServlet needs to be declared and mapped for processing all requests either using java or web.xmlconfiguration.

In a Servlet 3.0+ environment, you can use AbstractAnnotationConfigDispatcherServletInitializer class to register and initialize the DispatcherServlet programmatically as follows.

MvcWebApplicationInitializer.java

package com.boraji.tutorial.spring.config;

import org.springframework.web.servlet.support.AbstractAnnotationConfigDispatcherServletInitializer;

public class MvcWebApplicationInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {

   @Override
   protected Class<?>[] getRootConfigClasses() {
      return null;
   }

   @Override
   protected Class<?>[] getServletConfigClasses() {
      return new Class[] { MvcWebConfig.class };
   }

   @Override
   protected String[] getServletMappings() {
      return new String[] { "/" };
   }

}

Controller class

Create a simple @Controller class under com.boraji.tutorial.spring.controller package as follows. 

MyContoller.java

package com.boraji.tutorial.spring.controller;

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

@Controller
public class MyController {

   @GetMapping("/")
   public String index(Model model) {
      return "index";
   }
}

JSP views

Create an index.jsp file with Bootstrap components such as Navbar, Jumbotron, Button etc. under src\main\webapp\WEB-INF\views folder as follows.

Use <spring:theme> tag to retrieve the key’s values from theme properties files

<%@ page language="java" contentType="text/html; charset=ISO-8859-1" pageEncoding="ISO-8859-1"%>
<%@ taglib uri="http://www.springframework.org/tags" prefix="spring"%>
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
<title>BORAJI.COM</title>

<spring:theme code="stylesheet" var="themeName" />
<link href='<spring:url value="/resources/css/${themeName}"/>' rel="stylesheet" />

</head>
<body>
   <nav class="navbar navbar-expand-md navbar-dark bg-dark mb-4">
      <a class="navbar-brand" href="#">BORAJI.COM</a>
      <button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarCollapse"
         aria-controls="navbarCollapse" aria-expanded="false" aria-label="Toggle navigation">
         <span class="navbar-toggler-icon"></span>
      </button>
      <div class="collapse navbar-collapse" id="navbarCollapse">
         <ul class="navbar-nav mr-auto">
            <li class="nav-item active"><a class="nav-link" href="#">Home <span class="sr-only"></span></a></li>
         </ul>
      </div>
   </nav>

   <div role="main" class="container">
      <div class="jumbotron">
         <h1>Spring MVC 5</h1>
         <p class="lead">This is an example of using theme resolver in Spring MVC.</p>


         <div class="dropdown">
            <button class="btn btn-danger dropdown-toggle" type="button" id="dropdownMenuButton"
               data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">Change Theme</button>
            <div class="dropdown-menu" aria-labelledby="dropdownMenuButton">
               <a class="dropdown-item" href="?theme=cerulean">Cerulean</a> <a class="dropdown-item" href="?theme=pulse">Pulse</a>
            </div>
         </div>

      </div>
   </div>
   <script src="https://code.jquery.com/jquery-3.2.1.slim.min.js"></script>
   <script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.12.9/umd/popper.min.js"></script>
   <script src="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/js/bootstrap.min.js"></script>
</body>
</html>

 

Run application

Use the following maven command to run your application.

mvn jetty:run (This command deploy the webapp from its sources, instead of build war).

Enter the http://localhost:8080 URL in browser's address bar to test the Theme Resolver configuration.

spring-mvc-theme-example01.png

Change the look-and-feel of your application by selecting theme from dropdown list.

spring-mvc-theme-example02.png