最近在开发FX Trading的Dashboard的时候,发现用到的一些Bootstrap的样式在手机上的显示效果很不好,而且每笔trade包含的信息也比较多,用手机看实在是不方便,于是就萌生了做一版针对mobile的想法。
Spring Mobile还在1.0的beta版的时候就有了解过,但当时的版本还比较简陋,有些功能都不够完整,其实现在也都比较简单,总共也没多少代码,不过也足以解决mobile相关的一些问题了。Spring Mobile是基于Spring MVC的,如果你的项目本身使用的就是Spring MVC,那基本没多少额外的工作量。我们的项目就是基于Spring MVC的,现在也只是想增加针对mobile的展示,所以controller都可以重用,只需做一些针对mobile的页面就可以了。
Spring Mobile实现的功能主要包括以下四块,也都是配置一下就可以使用了,所以你只需要专注于页面的展示。
Device Resolution
访问设备的识别,默认采用LiteDeviceResolver作为实现,通过分析HTTP Header来进行识别,如User-Agent,Accept等。
若是一些特殊的User-Agent被识别成了mobile,可以将这些特殊的User-Agent的关键字定义为一个List
每个request被处理之前,将会先识别设备类型Device,然后保存在request的currentDevice参数中。
定义DeviceResolverHandlerInterceptor在Spring MVC的配置中,即可实现设备的自动识别。
<interceptors>
<!-- resolve the device that originated the web request -->
<bean class="org.springframework.mobile.device.DeviceResolverHandlerInterceptor" />
</interceptors>
也可以通过在web.xml中定义DeviceResolverRequestFilter,来实现设备的自动识别。
<filter>
<filter-name>deviceResolverRequestFilter</filter-name>
<filter-class>org.springframework.mobile.device.DeviceResolverRequestFilter</filter-class>
</filter>
若要在代码中获取当前设备类型,可以通过以下三种方式:
1、直接从HttpRequest中获取currentDevice参数。
2、使用DeviceUtils,但需要一个ServletRequest或Spring WebRequest作为参数。
Device currentDevice = DeviceUtils.getCurrentDevice(servletRequest);
3、将Device作为Controller中方法的参数,需要添加以下配置。
<annotation-driven>
<argument-resolvers>
<bean class="org.springframework.mobile.device.DeviceWebArgumentResolver" />
</argument-resolvers>
</annotation-driven>
Site Preference
用于管理web应用采用哪种模式展现给用户。
默认采用StandardSitePreferenceHandler作为实现,首先从request中读取site_preference参数,并将读取到的设备类型保存在cookie中;若没读取到,则尝试从cookie中获取CookieSitePreferenceRepository.SITE_PREFERENCE,若仍然没有获取到,则采用DeviceResolver得到的设备类型;最终采用的设备类型将会保存在request的currentSitePreference参数中。
site_preference支持:normal,mobile和tablet。不区分大小写。 如:
<a href="${currentUrl}?site_preference=mobile">Mobile</a>
定义SitePreferenceHandlerInterceptor在Spring MVC的配置中,即可实现Site Preference管理。
<interceptors>
<!-- manage the user's site preference (declare after DeviceResolverHandlerInterceptor) -->
<bean class="org.springframework.mobile.device.site.SitePreferenceHandlerInterceptor" />
</interceptors>
若要在代码中获取当前的Site Preference,可以通过一下三种方式:
1、直接从HttpRequest中获取currentSitePreference参数。
2、使用SitePreferenceUtils,但需要一个ServletRequest或Spring WebRequest作为参数。
SitePreference sitePreference = SitePreferenceUtils.getCurrentSitePreference(servletRequest);
3、将SitePreference作为Controller中方法的参数,需要添加以下配置。
<annotation-driven>
<argument-resolvers>
<bean class="org.springframework.mobile.device.site.SitePreferenceWebArgumentResolver" />
</argument-resolvers>
</annotation-driven>
Site Switch
Site Switch用于移动版和桌面版采用不同domain的应用,如:m.google.com 和 google.com。
Site Switch默认采用StandardSitePreferenceHandler作为Site Preference的实现,因此不需要再配置SitePreferenceHandlerInterceptor。
mDot
用于重定向mobile用户到m.${serverName}。
定义SiteSwitcherHandlerInterceptor在Spring MVC的配置中:
<interceptors>
<!-- resolve the device that originated the web request -->
<bean class="org.springframework.mobile.device.DeviceResolverHandlerInterceptor" />
<!-- redirects mobile users to "m.myapp.com" (declare after DeviceResolverHandlerInterceptor) -->
<bean class=“org.springframework.mobile.device.switcher.SiteSwitcherHandlerInterceptor" factory-method="mDot">
<constructor-arg index="0" type="java.lang.String" value="myapp.com"/>
<constructor-arg index="1" type="java.lang.Boolean" value="true"/>
</bean>
</interceptors>
第一个参数用于指定serverName,必须的参数;第二个为可选参数,用于指定是否将tablet作为mobile来处理。
dotMobi
用于重定向mobile用户到${serverName - lastDomain}.mobi。
定义SiteSwitcherHandlerInterceptor在Spring MVC的配置中:
<interceptors>
<!-- resolve the device that originated the web request -->
<bean class="org.springframework.mobile.device.DeviceResolverHandlerInterceptor" />
<!-- redirects mobile users to "myapp.mobi" (declare after DeviceResolverHandlerInterceptor) -->
<bean class=“org.springframework.mobile.device.switcher.SiteSwitcherHandlerInterceptor" factory-method="dotMobi">
<constructor-arg index="0" type="java.lang.String" value="myapp.com"/>
<constructor-arg index="1" type="java.lang.Boolean" value="true"/>
</bean>
</interceptors>
第一个参数用于指定serverName,必须的参数;第二个为可选参数,用于指定是否将tablet作为mobile来处理。
Standard
用于重定向mobile和tablet用户到指定的domain。
定义SiteSwitcherHandlerInterceptor在Spring MVC的配置中:
<interceptors>
<!-- resolve the device that originated the web request -->
<bean class="org.springframework.mobile.device.DeviceResolverHandlerInterceptor" />
<!-- redirects mobile users to "mobile.app.com" (declare after DeviceResolverHandlerInterceptor) -->
<bean class=“org.springframework.mobile.device.switcher.SiteSwitcherHandlerInterceptor" factory-method="standard">
<constructor-arg value="app.com"/>
<constructor-arg value="mobile.app.com"/>
<constructor-arg value="tablet.app.com"/>
<constructor-arg value=".app.com"/>
</bean>
</interceptors>
第一个参数用于指定serverName;第二个参数用于指定mobile的domain,第三个参数用于指定tablet的domain,第四个参数用于指定cookie的domain。
urlPath
用于重定向mobile用户到相同domain下的不同的路径 ${serverName}/${mobilePath}。
定义SiteSwitcherHandlerInterceptor在Spring MVC的配置中:
<interceptors>
<!-- resolve the device that originated the web request -->
<bean class="org.springframework.mobile.device.DeviceResolverHandlerInterceptor" />
<!-- redirects mobile users to "myapp.com/m" (declare after DeviceResolverHandlerInterceptor) -->
<bean class=“org.springframework.mobile.device.switcher.SiteSwitcherHandlerInterceptor" factory-method="urlPath">
<constructor-arg index="0" type="java.lang.String" value="/m" />
</bean>
</interceptors>
参数用于指定mobile的路径。
若应用不在domain的根路径下,可指定root path。
<interceptors>
<!-- resolve the device that originated the web request -->
<bean class="org.springframework.mobile.device.DeviceResolverHandlerInterceptor" />
<!-- redirects mobile users to "myapp.com/showcase/m" (declare after DeviceResolverHandlerInterceptor) -->
<bean class=“org.springframework.mobile.device.switcher.SiteSwitcherHandlerInterceptor" factory-method="urlPath">
<constructor-arg index="0" type="java.lang.String" value="/m" />
<constructor-arg index="1" type="java.lang.String" value="/showcase" />
</bean>
</interceptors>
第一个参数用于指定mobile的路径,第二个参数用于指定应用的root path。
以上两种配置都会将tablet作为PC来处理,如需指定tablet的路径,可通过如下的配置。
<interceptors>
<!-- resolve the device that originated the web request -->
<bean class="org.springframework.mobile.device.DeviceResolverHandlerInterceptor" />
<!-- redirects mobile users to "myapp.com/showcase/m" (declare after DeviceResolverHandlerInterceptor) -->
<bean class=“org.springframework.mobile.device.switcher.SiteSwitcherHandlerInterceptor" factory-method="urlPath">
<constructor-arg index="0" type="java.lang.String" value="/m" />
<constructor-arg index="1" type="java.lang.String" value="/t" />
<constructor-arg index="2" type="java.lang.String" value="/showcase" />
</bean>
</interceptors>
为了让mobile和tablet的路径能够正常工作,还需要在web.xml中配置相应的url给DispatcherServlet。
<servlet-mapping>
<servlet-name>dispatcherServlet</servlet-name>
<url-pattern>/</url-pattern>
<url-pattern>/m/*</url-pattern>
<url-pattern>/t/*</url-pattern>
</servlet-mapping>
Device Aware View Management
根据设备类型来决定使用的view,避免了在Controller中判断设备类型来返回特定view的繁琐。LiteDeviceDelegatingViewResolver可以在不同的路径下查找相同名字的view,也可以在相同路径下查找不同后缀的view。
在Spring MVC的配置中添加如下定义:
<bean class="org.springframework.mobile.device.view.LiteDeviceDelegatingViewResolver">
<constructor-arg>
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/WEB-INF/views/" />
<property name="suffix" value=".jsp" />
</bean>
</constructor-arg>
<property name="mobilePrefix" value="mobile/" />
<property name="tabletPrefix" value="tablet/" />
<property name="enableFallback" value="true" />
</bean>
enableFallback用于若找不到调整路径后的view,则使用原始的view。这是一个很好的特性,但是在Spring MVC并没有真正实现它,所以这个参数是不起作用的。
可参见spring的issue list: