spring mobile 是spring新推出的一个用于支持移动浏览的小框架,用起来很简单,和spring mvc结合也很方便。它可以测试出访问的设备(DEVICE)但是默认的只能检测出三种(Mobile,
Tablet,Normal
),本文就是对它的扩展(spring mobile 的官网 实例 http://projects.spring.io/spring-mobile)。
首先自定义设备接口继承spring的Device,添加几种设备。
package com.jy.device;
import org.springframework.mobile.device.Device;
public interface CustomDevice extends Device{
public boolean isIphone();
public boolean isAndroid();
public boolean isIpad();
public boolean isWinPhone();
}
设备类型的枚举
package com.jy.device;
public enum CustomDeviceType {
/**
* Represents a normal device. i.e. a browser on a desktop or laptop computer
*/
NORMAL,
/**
* Represents a mobile device, such as an iPhone
*/
MOBILE,
/**
* Represents a tablet device, such as an iPad
*/
TABLET,
IPHONE,//这两个是新加的
ANDROID
}
设备的实现
package com.jy.device.impl;
import com.jy.device.CustomDevice;
import com.jy.device.CustomDeviceType;
class CustomLiteDevic implements CustomDevice{
public static final CustomLiteDevic NORMAL_INSTANCE = new CustomLiteDevic(CustomDeviceType.NORMAL);
public static final CustomLiteDevic MOBILE_INSTANCE = new CustomLiteDevic(CustomDeviceType.MOBILE);
public static final CustomLiteDevic TABLET_INSTANCE = new CustomLiteDevic(CustomDeviceType.TABLET);
public static final CustomLiteDevic IPHONE_INSTANCE = new CustomLiteDevic(CustomDeviceType.IPHONE);
public static final CustomLiteDevic ANDROID_INSTANCE = new CustomLiteDevic(CustomDeviceType.ANDROID);
public boolean isNormal() {
return this.deviceType == CustomDeviceType.NORMAL;
}
public boolean isMobile() {
return this.deviceType == CustomDeviceType.MOBILE;
}
public boolean isTablet() {
return this.deviceType == CustomDeviceType.TABLET;
}
public CustomDeviceType getDeviceType() {
return this.deviceType;
}
public String toString() {
StringBuilder builder = new StringBuilder();
builder.append("[LiteDevice ");
builder.append("type").append("=").append(this.deviceType);
builder.append("]");
return builder.toString();
}
private final CustomDeviceType deviceType;
/**
* Creates a LiteDevice.
*/
private CustomLiteDevic(CustomDeviceType customDeviceType) {
this.deviceType = customDeviceType;
}
@Override
public boolean isIphone() {
return this.deviceType == CustomDeviceType.IPHONE;
}
@Override
public boolean isAndroid() {
return this.deviceType == CustomDeviceType.ANDROID;
}
@Override
public boolean isIpad() {
return false;
}
@Override
public boolean isWinPhone() {
// TODO Auto-generated method stub
return false;
}
}
设备的检测,判断
package com.jy.device.impl;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Enumeration;
import java.util.List;
import javax.servlet.http.HttpServletRequest;
import org.springframework.mobile.device.Device;
import org.springframework.mobile.device.DeviceResolver;
import org.springframework.stereotype.Component;
@Component
public class CustomLiteDeviceResolver implements DeviceResolver{
private final List<String> mobileUserAgentPrefixes = new ArrayList<String>();
private final List<String> mobileUserAgentKeywords = new ArrayList<String>();
private final List<String> tabletUserAgentKeywords = new ArrayList<String>();
private final List<String> normalUserAgentKeywords = new ArrayList<String>();
public CustomLiteDeviceResolver() {
init();
}
public CustomLiteDeviceResolver(List<String> normalUserAgentKeywords) {
init();
this.normalUserAgentKeywords.addAll(normalUserAgentKeywords);
}
public Device resolveDevice(HttpServletRequest request) {
String userAgent = request.getHeader("User-Agent");
// UserAgent keyword detection of Normal devices
if (userAgent != null) {
userAgent = userAgent.toLowerCase();
for (String keyword : normalUserAgentKeywords) {
if (userAgent.contains(keyword)) {
return resolveFallback(request);
}
}
}
// UserAgent keyword detection of Tablet devices
if (userAgent != null) {
userAgent = userAgent.toLowerCase();
// Android special case
if (userAgent.contains("android")) {
return CustomLiteDevic.ANDROID_INSTANCE;
}
if (userAgent.contains("safari")&& userAgent.contains("iphone")) {
return CustomLiteDevic.IPHONE_INSTANCE;
}
// Kindle Fire special case
if (userAgent.contains("silk") && !userAgent.contains("mobile")) {
return CustomLiteDevic.TABLET_INSTANCE;
}
for (String keyword : tabletUserAgentKeywords) {
if (userAgent.contains(keyword)) {
return CustomLiteDevic.TABLET_INSTANCE;
}
}
}
// UAProf detection
if (request.getHeader("x-wap-profile") != null
|| request.getHeader("Profile") != null) {
return CustomLiteDevic.MOBILE_INSTANCE;
}
// User-Agent prefix detection
if (userAgent != null && userAgent.length() >= 4) {
String prefix = userAgent.substring(0, 4).toLowerCase();
if (mobileUserAgentPrefixes.contains(prefix)) {
return CustomLiteDevic.MOBILE_INSTANCE;
}
}
// Accept-header based detection
String accept = request.getHeader("Accept");
if (accept != null && accept.contains("wap")) {
return CustomLiteDevic.MOBILE_INSTANCE;
}
// UserAgent keyword detection for Mobile devices
if (userAgent != null) {
for (String keyword : mobileUserAgentKeywords) {
if (userAgent.contains(keyword)) {
return CustomLiteDevic.MOBILE_INSTANCE;
}
}
}
// OperaMini special case
@SuppressWarnings("rawtypes")
Enumeration headers = request.getHeaderNames();
while (headers.hasMoreElements()) {
String header = (String) headers.nextElement();
if (header.contains("OperaMini")) {
return CustomLiteDevic.MOBILE_INSTANCE;
}
}
return resolveFallback(request);
}
// subclassing hooks
/**
* List of user agent prefixes that identify mobile devices. Used primarily to match
* by operator or handset manufacturer.
*/
protected List<String> getMobileUserAgentPrefixes() {
return mobileUserAgentPrefixes;
}
/**
* List of user agent keywords that identify mobile devices. Used primarily to match
* by mobile platform or operating system.
*/
protected List<String> getMobileUserAgentKeywords() {
return mobileUserAgentKeywords;
}
/**
* List of user agent keywords that identify tablet devices. Used primarily to match
* by tablet platform or operating system.
*/
protected List<String> getTabletUserAgentKeywords() {
return tabletUserAgentKeywords;
}
/**
* List of user agent keywords that identify normal devices. Any items in this list
* take precedence over the mobile and tablet user agent keywords, effectively
* overriding those.
*/
protected List<String> getNormalUserAgentKeywords() {
return normalUserAgentKeywords;
}
/**
* Initialize this device resolver implementation. Registers the known set of device
* signature strings. Subclasses may override to register additional strings.
*/
protected void init() {
getMobileUserAgentPrefixes().addAll(
Arrays.asList(KNOWN_MOBILE_USER_AGENT_PREFIXES));
getMobileUserAgentKeywords().addAll(
Arrays.asList(KNOWN_MOBILE_USER_AGENT_KEYWORDS));
getTabletUserAgentKeywords().addAll(
Arrays.asList(KNOWN_TABLET_USER_AGENT_KEYWORDS));
}
/**
* Fallback called if no mobile device is matched by this resolver. The default
* implementation of this method returns a "normal" {@link Device} that is neither
* mobile or a tablet. Subclasses may override to try additional mobile or tablet
* device matching before falling back to a "normal" device.
*/
protected Device resolveFallback(HttpServletRequest request) {
return CustomLiteDevic.NORMAL_INSTANCE;
}
// internal helpers
private static final String[] KNOWN_MOBILE_USER_AGENT_PREFIXES = new String[] {
"w3c ", "w3c-", "acs-", "alav", "alca", "amoi", "audi", "avan", "benq",
"bird", "blac", "blaz", "brew", "cell", "cldc", "cmd-", "dang", "doco",
"eric", "hipt", "htc_", "inno", "ipaq", "ipod", "jigs", "kddi", "keji",
"leno", "lg-c", "lg-d", "lg-g", "lge-", "lg/u", "maui", "maxo", "midp",
"mits", "mmef", "mobi", "mot-", "moto", "mwbp", "nec-", "newt", "noki",
"palm", "pana", "pant", "phil", "play", "port", "prox", "qwap", "sage",
"sams", "sany", "sch-", "sec-", "send", "seri", "sgh-", "shar", "sie-",
"siem", "smal", "smar", "sony", "sph-", "symb", "t-mo", "teli", "tim-",
"tosh", "tsm-", "upg1", "upsi", "vk-v", "voda", "wap-", "wapa", "wapi",
"wapp", "wapr", "webc", "winw", "winw", "xda ", "xda-" };
private static final String[] KNOWN_MOBILE_USER_AGENT_KEYWORDS = new String[] {
"blackberry", "webos", "ipod", "lge vx", "midp", "maemo", "mmp", "mobile",
"netfront", "hiptop", "nintendo DS", "novarra", "openweb", "opera mobi",
"opera mini", "palm", "psp", "phone", "smartphone", "symbian", "up.browser",
"up.link", "wap", "windows ce" };
private static final String[] KNOWN_TABLET_USER_AGENT_KEYWORDS = new String[] {
"ipad", "playbook", "hp-tablet", "kindle" };
}
最后在spring_mvc的配置文件中把自己定义的CustomLiteDeviceResolver注入到拦截器中
<mvc:interceptors>
<bean class="org.springframework.mobile.device.DeviceResolverHandlerInterceptor" >
<constructor-arg>
<bean class="com.jy.device.impl.CustomLiteDeviceResolver" />
</constructor-arg>
</bean>
</mvc:interceptors>
配置参数的转化(方便spring mvc 中接收处理Device 参数)
<mvc:annotation-driven>
<mvc:argument-resolvers>
<bean class="org.springframework.mobile.device.DeviceWebArgumentResolver" />
</mvc:argument-resolvers>
</mvc:annotation-driven>
测试方法
启动web服务器
写一个如下的请求测试
/**
* 用户退出
*
* @param fengexUser
* @return
*/
@RequestMapping(value = "/loginout")
@ResponseBody
public Object loginOut(CustomDevice device) {// TODO 退出操作(c)
// logger.debug("member(id) loginout:" + member.getId());
logger.debug("is phone? " + device.isMobile());
logger.debug("is tablet? " + device.isTablet());
logger.debug("is normal? " + device.isNormal());
return null;
}