Spring MVC Themes Tutorial
June 27, 2019
On this page we will walk through the Spring MVC Themes tutorial with examples using JavaConfig. Spring MVC provides custom theme resolution. We can give the different theme options to our users in web application that can be handled in Spring MVC in easy way. This is same as Spring MVC internationalization and localization handling. The main API which handles themes are ThemeSource, ThemeResolver and ThemeChangeInterceptor.
In our theme example we have also accommodated the code of spring MVC internationalization and localization and for the explanation, find the link here. To understand the spring MVC theme we will go step by step.
Contents
- Project Structure in Eclipse
- Gradle File
- Configuration Class for Spring MVC Themes
- ThemeSource
- ThemeResolver
- ThemeChangeInterceptor
- Resource Handling
- Create CSS
- Create Properties File for Themes
- Create Properties File for i18n
- Create JSP File
- Create Controller
- Output
- Change Locale and Theme Together
Project Structure in Eclipse
Find the demo project structure in eclipse.Gradle File
Find the Gradle file used in our example.build.gradle
apply plugin: 'java' apply plugin: 'eclipse' apply plugin: 'war' archivesBaseName = 'concretepage' version = '1' repositories { mavenCentral() } dependencies { compile 'org.springframework.boot:spring-boot-starter-web:1.2.2.RELEASE' compile 'jstl:jstl:1.2' providedCompile 'org.springframework.boot:spring-boot-starter-tomcat:1.2.2.RELEASE' }
Configuration Class for Spring MVC Themes
In java configuration create bean forThemeSource
, ThemeResolver
and register ThemeChangeInterceptor
to achieve spring MVC themes.
AppConfiguration.java
package com.concretepage.config; import java.util.Locale; import org.springframework.context.MessageSource; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Configuration; import org.springframework.context.support.ReloadableResourceBundleMessageSource; import org.springframework.ui.context.ThemeSource; import org.springframework.ui.context.support.ResourceBundleThemeSource; import org.springframework.web.servlet.LocaleResolver; 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.WebMvcConfigurerAdapter; import org.springframework.web.servlet.i18n.CookieLocaleResolver; import org.springframework.web.servlet.i18n.LocaleChangeInterceptor; import org.springframework.web.servlet.theme.CookieThemeResolver; import org.springframework.web.servlet.theme.ThemeChangeInterceptor; import org.springframework.web.servlet.view.InternalResourceViewResolver; @Configuration @ComponentScan("com.concretepage") @EnableWebMvc public class AppConfiguration extends WebMvcConfigurerAdapter { @Bean public InternalResourceViewResolver viewResolver() { InternalResourceViewResolver resolver = new InternalResourceViewResolver(); resolver.setPrefix("/WEB-INF/views/"); resolver.setSuffix(".jsp"); return resolver; } /* Theme specific start */ @Bean public ThemeSource themeSource() { ResourceBundleThemeSource source = new ResourceBundleThemeSource(); source.setBasenamePrefix("hello-"); return source; } @Bean public ThemeResolver themeResolver(){ CookieThemeResolver resolver = new CookieThemeResolver(); resolver.setCookieMaxAge(2400); resolver.setCookieName("mythemecookie"); resolver.setDefaultThemeName("theme1"); return resolver; } /* End */ /*Internationalization and Localization specific: start*/ @Bean public MessageSource messageSource() { ReloadableResourceBundleMessageSource messageSource = new ReloadableResourceBundleMessageSource(); messageSource.setBasename("/i18/usermsg"); messageSource.setDefaultEncoding("UTF-8"); return messageSource; } @Bean public LocaleResolver localeResolver(){ CookieLocaleResolver resolver = new CookieLocaleResolver(); resolver.setDefaultLocale(new Locale("en")); resolver.setCookieName("myLocaleCookie"); resolver.setCookieMaxAge(4800); return resolver; } /* End */ //Register interceptors @Override public void addInterceptors(InterceptorRegistry registry) { //Internationalization and Localization specific LocaleChangeInterceptor interceptor = new LocaleChangeInterceptor(); interceptor.setParamName("mylocale"); registry.addInterceptor(interceptor); //Theme specific ThemeChangeInterceptor themeInterceptor = new ThemeChangeInterceptor(); themeInterceptor.setParamName("mytheme"); registry.addInterceptor(themeInterceptor); } //Used to access CSS resource @Override public void addResourceHandlers(final ResourceHandlerRegistry registry) { registry.addResourceHandler("/resources/**").addResourceLocations("/resources/"); } }
Initializer.java
package com.concretepage.config; import javax.servlet.ServletContext; import javax.servlet.ServletException; import javax.servlet.ServletRegistration.Dynamic; import org.springframework.web.WebApplicationInitializer; import org.springframework.web.context.support.AnnotationConfigWebApplicationContext; import org.springframework.web.servlet.DispatcherServlet; public class Initializer implements WebApplicationInitializer { public void onStartup(ServletContext servletContext) throws ServletException { AnnotationConfigWebApplicationContext ctx = new AnnotationConfigWebApplicationContext(); ctx.register(AppConfiguration.class); ctx.setServletContext(servletContext); Dynamic dynamic = servletContext.addServlet("dispatcher", new DispatcherServlet(ctx)); dynamic.addMapping("/"); dynamic.setLoadOnStartup(1); } }
ThemeSource
org.springframework.ui.context.ThemeSource is an interface that resolves themes. It enables parameterization and internalization of themes. ResourceBundleThemeSource
is the implementation of ThemeSource
. It finds a java ResourceBundle
per theme. We need to tell it file base name using which ResourceBundleThemeSource
resolves the ResourceBundle
. We need to create bean with spring reserved name as themeSource.
@Bean public ThemeSource themeSource() { ResourceBundleThemeSource source = new ResourceBundleThemeSource(); source.setBasenamePrefix("hello-"); return source; }
ThemeResolver
org.springframework.web.servlet.ThemeResolver is an interface that is used for theme resolution strategies. We need to create a bean with the spring reserved name themeResolver. The implementations of this interface can handle theme resolution via request and changes in theme via request and response. The implementation classes are based on session and cookie etc that are given as follows.FixedThemeResolver
: This is the default implementation of ThemeResolver
which uses the fixed configured default theme. It can be set by defaultThemeName()
method. The default name is theme. It does not support setThemeName
because fixed cannot be changed.
CookieThemeResolver
: This is the cookie based implementation of ThemeResolver
. We need to create bean for CookieThemeResolver
and set cookie name, cookie max age, default theme name etc. For custom theme setting, a cookie is set on browser and till the next change and expiry time, cookie value remains in browser and selected theme is displayed onwards. Cookie based approach is useful in stateless websites. Find the bean creation for using CookieThemeResolver
.
@Bean public ThemeResolver themeResolver(){ CookieThemeResolver resolver = new CookieThemeResolver(); resolver.setCookieMaxAge(2400); resolver.setCookieName("mythemecookie"); resolver.setDefaultThemeName("theme1"); return resolver; }
SessionThemeResolver
: This is the session based implementation of ThemeResolver
. This approach is useful where the application has user session. In case of theme custom setting, a theme attribute is set in user session. The theme attribute name can be overridden by using setThemeName(). We can create the bean as below.
@Bean public ThemeResolver themeResolver(){ SessionThemeResolver resolver = new SessionThemeResolver(); resolver.setDefaultThemeName("theme1"); return resolver; }
ThemeChangeInterceptor
org.springframework.web.servlet.theme.ThemeChangeInterceptor is an interceptor that allows to change theme per request. This is achieved by a configurable request parameter. The default request parameter is theme. Find the code how to register ThemeChangeInterceptor
.
LocaleChangeInterceptor interceptor = new LocaleChangeInterceptor(); ThemeChangeInterceptor themeInterceptor = new ThemeChangeInterceptor(); themeInterceptor.setParamName("mytheme"); registry.addInterceptor(themeInterceptor);
? mytheme= theme1: Applies the theme hello-theme1.properties
? mytheme= theme2: Applies the theme hello-theme2.properties
Resource Handling
For the demo, we need some CSS file, and to include these resources, we need to tell spring about the URL pattern of resources. As our CSS file has been kept in resources\css, the URL pattern can be defined as below in the code.@Override public void addResourceHandlers(final ResourceHandlerRegistry registry) { registry.addResourceHandler("/resources/**").addResourceLocations("/resources/"); }
WebMvcConfigurerAdapter
.
Create CSS
For the demo we are using following CSS files.style1.css
H3 { COLOR: green; FONT-FAMILY: sans-serif; FONT-SIZE: 20pt; }
style2.css
H3 { COLOR: red; FONT-FAMILY: arial; FONT-SIZE: 20pt; }
Create Properties File for Themes
Find the properties file used in our demo.hello-theme1.properties
styleSheet=resources/css/style1.css background= red
hello-theme2.properties
styleSheet=resources/css/style2.css background= Green
Create Properties File for i18n
Find the properties file for i18n.usermsg_en.properties
user.title= Fill User Form user.name= Enter Name user.age= Enter Age user.submit= Submit user.success= You have successfully submitted the form
usermsg_de.properties
user.title= Füllen User Form user.name= Name eingeben user.age= Geben Alter user.submit= einreichen user.success= Sie haben die Form eingereicht
Create JSP File
Find the JSP file being used in our demo. We have created two links that will change the theme.userform.jsp
<%@taglib uri="http://www.springframework.org/tags" prefix="spring"%> <%@taglib uri="http://www.springframework.org/tags/form" prefix="form"%> <html> <head> <title><spring:message code="user.title"/></title> <link rel="stylesheet" href="<spring:theme code='styleSheet'/>" type="text/css"/> </head> <body bgcolor="<spring:theme code='background'/>"> Change language: <a href="user?mylocale=en">English </a> | <a href="user?mylocale=de">German </a><br/><br/> Change theme: <a href="user?mytheme=theme1">Theme 1 </a> | <a href="user?mytheme=theme2"> Theme 2 </a> <h3> <spring:message code="user.title"/></h3> <table> <form:form action="save" method="post" commandName="user"> <tr><td><spring:message code="user.name"/>:</td> <td><form:input path="name"/> </td> </tr> <tr> <td> <spring:message code="user.age"/> :</td> <td><form:input path="age"/> </td> </tr> <tr> <td colspan=2> <spring:message code="user.submit" var="valSubmit"></spring:message> <input type="submit" value="${valSubmit}"> </td></tr> </form:form> </table> </body> </html>
success.jsp
<%@taglib uri="http://www.springframework.org/tags" prefix="spring"%> <html> <head> <title><spring:message code="user.title"/></title> <link rel="stylesheet" href="<spring:theme code='styleSheet'/>" type="text/css"/> </head> <body bgcolor="<spring:theme code='background'/>"> Change language: <a href="user?mylocale=en">English </a> | <a href="user?mylocale=de">German </a><br/><br/> Change theme: <a href="user?mytheme=theme1">Theme 1 </a> | <a href="user?mytheme=theme2"> Theme 2 </a> <h3> <spring:message code="user.success"/></h3> </body> </html>
Create Controller
Find the controller used in our example.UserController.java
package com.concretepage.controller; import java.util.Locale; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.ModelAttribute; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.servlet.ModelAndView; import com.concretepage.bean.User; @Controller public class UserController { @RequestMapping(value="/user", method = RequestMethod.GET) public ModelAndView user(Locale locale){ return new ModelAndView("userform","user",new User()); } @RequestMapping(value = "/save", method = RequestMethod.POST) public String save(@ModelAttribute("user") User user) { return "success"; } }
User.java
package com.concretepage.bean; public class User { private String name; private Integer age; public String getName() { return name; } public void setName(String name) { this.name = name; } public Integer getAge() { return age; } public void setAge(Integer age) { this.age = age; } }
Output
Deploy the code in tomcat 8 and access the URL localhost:8080/concretepage-1/userBy default the hello-theme1.properties file will be loaded. We will get the output as given below.
We will get the output as below.
CookieThemeResolver
in our demo, so our defined cookie mythemecookie will be set in browser.
Change Locale and Theme Together
To change locale and theme together we can use locale and theme parameter together in query string that will look like.http://localhost:8080/concretepage-1/user?mytheme=theme2&mylocale=en
http://localhost:8080/concretepage-1/user?mytheme=theme2&mylocale=de
http://localhost:8080/concretepage-1/user?mytheme=theme1&mylocale=de
http://localhost:8080/concretepage-1/user?mytheme=theme1&mylocale=en
Now I am done. Happy Learning!