Spring Mobile is an extension to Spring MVC that aims to simplify the development of mobile web applications. It includes a module for detecting on the server the type of device, mobile, tablet, or desktop making a request.
Getting Started
Include the project in your dependencies, for example, in a Maven pom:
Then set the version1 in your gradle.properties file:
mobileVersion=1.1.5.RELEASE
Next, add either the DeviceResolverHandlerInterceptor or DeviceResolverRequestFilter to your web application. The first is more tightly coupled to the Spring framework, while the second is an implementation of a servlet filter, so less coupled to Spring.
DeviceResolverHandlerInterceptor
Spring Mobile ships with a HandlerInterceptor that, on preHandle, delegates to a DeviceResolver. The resolved Device is set as a request attribute named “currentDevice”, making it available to handlers throughout the request processing.
To enable, add the DeviceResolverHandlerInterceptor to the list of interceptors defined in your DispatcherServlet configuration XML:
Alternatively, you can add the DeviceResolverHandlerInterceptor using Spring's Java-based configuration:
@Configuration
@EnableWebMvc
@ComponentScan
public class WebConfig implements WebMvcConfigurer {
//...
@Bean
public DeviceResolverHandlerInterceptor drhInterceptor() {
return new DeviceResolverHandlerInterceptor();
}
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(drhInterceptor());
}
}
DeviceResolverRequestFilter
As an alternative to the DeviceResolverHandlerInterceptor, Spring Mobile also ships with a servlet filter that delegates to a DeviceResolver. As with the HandlerInterceptor, the resolved Device is set under a request attribute named “currentDevice”.
To enable, add the DeviceResolverRequestFilter to your web.xml as follows:
To look up the current Device in your code, you can do so in several ways. If you already have a reference to a ServletRequest or Spring WebRequest, simply use DeviceUtils:
This would get the current device or null if no device has been resolved for the request. There’s also a getRequiredCurrentDevice(HttpServletRequest request) method that throws a runtime exception if the current device has not been resolved.
The Device interface has the following methods available:
Return Type
Method
DevicePlatform
getDevicePlatform() – Returns an enum which can be IOS, ANDROID, or UNKNOWN.
Boolean
isMobile() – True if this device is a mobile device such as an Apple iPhone or a Nexus One Android.
Boolean
isNormal() – True if this device is not a mobile or tablet device.
Boolean
isTablet() – True if this device is a tablet device such as an Apple iPad or a Motorola Xoom.
DeviceWebArgumentResolver
If you'd like to pass the current Device automatically as an argument to one or more of your controller methods, configure a DeviceWebArgumentResolverusing XML:
Spring allows for different implementations of DeviceResolver, but by default provides an implementation which only detects the presence of a mobile or tablet device named LiteDeviceResolver.
You can also customize the LiteDeviceResolver by adding additional keywords that, if contained in a request’s User-Agent, will resolve as a “normal” device, for example, using Java configuration:
@Bean
public LiteDeviceResolver liteDeviceResolver() {
List<String> keywords = new ArrayList<String>();
keywords.add("vivaldi");
keywords.add("yandex");
return new LiteDeviceResolver(keywords);
}
@Bean
public DeviceResolverHandlerInterceptor deviceResolverHandlerInt() {
return new DeviceResolverHandlerInterceptor(liteDeviceResolver());
}
@Override
public void addInterceptors(InterceptorRegistry registry) {
Spring Mobile provides a single SitePreferenceHandler implementation named StandardSitePreferenceHandler, which should be suitable for most needs. It supports a query parameter–based site preference indication (site_preference) and pluggable SitePreference storage and may be enabled in a Spring MVC application using the SitePreferenceHandlerInterceptor. In addition, if a SitePreference has not been explicitly indicated by the user, a default will be derived based on the user’s Device detected.
So along with the previous interceptors, add the following:
@Bean
public SitePreferenceHandlerInterceptor
sitePreferenceHandlerInterceptor() {
return new SitePreferenceHandlerInterceptor();
}
Then update the addInterceptors method to the following:
@Override
public void addInterceptors(InterceptorRegistry registry) {
Similarly to the Device resolution, you can gain access to the current SitePreference using either the SitePreferenceUtils or SitePreferenceHandlerMethodArgumentResolver. However, it might make more sense to redirect mobile users to a different site. In this case, you may use the SiteSwitcherHandlerInterceptor to redirect mobile users to a dedicated mobile site.
The mDot, dotMobi, urlPath, and standard factory methods of SitePreferenceHandlerInterceptor configure the cookie-based SitePreference storage. The cookie value will be shared across the mobile and normal site domains. Internally, the interceptor delegates to a SitePreferenceHandler, so there is no need to register a SitePreferenceHandlerInterceptor when using the switcher. For example, the following Interceptor would redirect mobile users to mobile.app.com, tablets to tablet.app.com, and otherwise just app.com:
@Bean
public SiteSwitcherHandlerInterceptor siteSwitcherHandlerInterceptor() {
This Interceptor would redirect mobile users to <your app>/mobile/ paths. For example, if the normal URL is “myapp.com/courses”, then the mobile site will be “myapp.com/mobile/courses”.
Spring Mobile Example
This example will build on the Spring Web MVC and Spring Data content from the previous chapters, as well as making use of Spring Boot. For more information on either topic, please see the related chapter. This example project is available online.2
To get started, create a new directory named “spring-mobile” and create a Gradle “build.gradle” file like the following:
plugins {
id 'org.springframework.boot' version '2.3.1.RELEASE'
id 'io.spring.dependency-management' version '1.0.9.RELEASE'
This uses the Spring Boot Gradle plugin and dependency management to simplify setting up the project. Note that we include the “spring-data-jpa” project from Chapter 6 as a dependency. This makes the repositories available for potential inclusion as Spring beans at runtime (depending on the configuration).
Next, create a main class, like the following:
@SpringBootApplication
@Import({WebConfig.class, ServiceConfig.class})
public class SpringMobileWebApp {
public static void main(String[] args) throws IOException {
Next, we specify the WebConfig class, which defines Interceptors as described previously in this chapter, as well as GroovyMarkupConfigurer and GroovyMarkupViewResolver:
Note that we’ve defined a SiteSwitcherHandlerInterceptor using the “/mobile” path. The Groovy-related configuration tells Spring to look under /templates/ in the classpath for files ending in “.groovy”.
Finally, we need to define the controllers for our MVC application. To enable a completely different logic for mobile sites, we can define a separate controller for mobile vs. normal requests. Alternatively, we could inject SitePreference as a method parameter to each controller method and use that instead since we set up a SitePreferenceHandlerMethodArgumentResolver.
In this case, we create a CourseController and MobileCourseController, which should look like the following:
@Controller
@RequestMapping
public class CourseController {
@GetMapping("/")
public String home() {
return "home";
}
// additional methods...
Listing 8-4
CourseController.java
@Controller
@RequestMapping("/mobile")
public class MobileCourseController {
@GetMapping("/")
public String home() {
return "mobile/home";
}
// additional methods...
Listing 8-5
MobileCourseController.java
Note that since the MobileCourseController is annotated with @RequestMapping("/mobile"), it will match all paths starting with “/mobile”, hence all users with the SitePreference of Mobile. Similarly, we could do the same for Tablet.
The Groovy markup templates should be placed under the src/main/resources/templates directory. The “home.groovy” template should look something like the following:
Using the URL ?site_preference=mobile (or clicking the “Mobile” link on the web page which has the same URL path) triggers the SiteSwitcherHandlerInterceptor to change the SitePreference. In this case, the user would be redirected to the “mobile/home” view which is rendered by the file src/main/resources/templates/mobile/home.groovy.
Figure 8-1
Mobile/normal home page
Exercise: Add Tablets
Starting with the code from this chapter (which is available online3), add support for tablets.