Spring Mobile是如何判断访问设备的类型的

Spring最近换域名了,去转转,发现了一个有意思的项目:spring mobile。

http://projects.spring.io/spring-mobile/

这个项目有很多实用的功能,如识别访问我们网站的设备是什么类型的(手机,平板,PC),据域名或者url来切换不同访问内容,据不同的访问设备转到不同的view中。


识别访问设备类型的核心代码在org.springframework.mobile.device.LiteDeviceResolver 类中,这个类实际上根据http请求头部的User-Agent的内容来判断设备到底是哪种类型的。

可以看到,这个类实际上是很简单的。不过字符串的匹配算法可能可以优化下。

[java] view plaincopy
  1. public class LiteDeviceResolver implements DeviceResolver {  
  2.   
  3.     private final List<String> mobileUserAgentPrefixes = new ArrayList<String>();  
  4.   
  5.     private final List<String> mobileUserAgentKeywords = new ArrayList<String>();  
  6.   
  7.     private final List<String> tabletUserAgentKeywords = new ArrayList<String>();  
  8.   
  9.     private final List<String> normalUserAgentKeywords = new ArrayList<String>();  
  10.   
  11.     public LiteDeviceResolver() {  
  12.         init();  
  13.     }  
  14.   
  15.     public LiteDeviceResolver(List<String> normalUserAgentKeywords) {  
  16.         init();  
  17.         this.normalUserAgentKeywords.addAll(normalUserAgentKeywords);  
  18.     }  
  19.   
  20.     public Device resolveDevice(HttpServletRequest request) {  
  21.         String userAgent = request.getHeader("User-Agent");  
  22.         // UserAgent keyword detection of Normal devices  
  23.         if (userAgent != null) {  
  24.             userAgent = userAgent.toLowerCase();  
  25.             for (String keyword : normalUserAgentKeywords) {  
  26.                 if (userAgent.contains(keyword)) {  
  27.                     return resolveFallback(request);  
  28.                 }  
  29.             }  
  30.         }  
  31.         // UAProf detection  
  32.         if (request.getHeader("x-wap-profile") != null || request.getHeader("Profile") != null) {  
  33.             return LiteDevice.MOBILE_INSTANCE;  
  34.         }  
  35.         // User-Agent prefix detection  
  36.         if (userAgent != null && userAgent.length() >= 4) {  
  37.             String prefix = userAgent.substring(04).toLowerCase();  
  38.             if (mobileUserAgentPrefixes.contains(prefix)) {  
  39.                 return LiteDevice.MOBILE_INSTANCE;  
  40.             }  
  41.         }  
  42.         // Accept-header based detection  
  43.         String accept = request.getHeader("Accept");  
  44.         if (accept != null && accept.contains("wap")) {  
  45.             return LiteDevice.MOBILE_INSTANCE;  
  46.         }  
  47.         // UserAgent keyword detection for Mobile and Tablet devices  
  48.         if (userAgent != null) {  
  49.             userAgent = userAgent.toLowerCase();  
  50.             // Android special case   
  51.             if (userAgent.contains("android") && !userAgent.contains("mobile")) {  
  52.                 return LiteDevice.TABLET_INSTANCE;  
  53.             }  
  54.             // Kindle Fire special case   
  55.             if (userAgent.contains("silk") && !userAgent.contains("mobile")) {  
  56.                 return LiteDevice.TABLET_INSTANCE;  
  57.             }  
  58.             for (String keyword : tabletUserAgentKeywords) {  
  59.                 if (userAgent.contains(keyword)) {  
  60.                     return LiteDevice.TABLET_INSTANCE;  
  61.                 }  
  62.             }  
  63.             for (String keyword : mobileUserAgentKeywords) {  
  64.                 if (userAgent.contains(keyword)) {  
  65.                     return LiteDevice.MOBILE_INSTANCE;  
  66.                 }  
  67.             }  
  68.         }  
  69.         // OperaMini special case  
  70.         @SuppressWarnings("rawtypes")  
  71.         Enumeration headers = request.getHeaderNames();  
  72.         while (headers.hasMoreElements()) {  
  73.             String header = (String) headers.nextElement();  
  74.             if (header.contains("OperaMini")) {  
  75.                 return LiteDevice.MOBILE_INSTANCE;  
  76.             }  
  77.         }  
  78.         return resolveFallback(request);  
  79.     }  
  80.   
  81.     // subclassing hooks  
  82.   
  83.     /** 
  84.      * List of user agent prefixes that identify mobile devices. 
  85.      * Used primarily to match by operator or handset manufacturer. 
  86.      */  
  87.     protected List<String> getMobileUserAgentPrefixes() {  
  88.         return mobileUserAgentPrefixes;  
  89.     }  
  90.   
  91.     /** 
  92.      * List of user agent keywords that identify mobile devices. 
  93.      * Used primarily to match by mobile platform or operating system. 
  94.      */  
  95.     protected List<String> getMobileUserAgentKeywords() {  
  96.         return mobileUserAgentKeywords;  
  97.     }  
  98.   
  99.     /** 
  100.      * List of user agent keywords that identify tablet devices. 
  101.      * Used primarily to match by tablet platform or operating system. 
  102.      */  
  103.     protected List<String> getTabletUserAgentKeywords() {  
  104.         return tabletUserAgentKeywords;  
  105.     }  
  106.   
  107.     /** 
  108.      * List of user agent keywords that identify normal devices. 
  109.      * Any items in this list take precedence over the mobile and 
  110.      * tablet user agent keywords, effectively overriding those.  
  111.      */  
  112.     protected List<String> getNormalUserAgentKeywords() {  
  113.         return normalUserAgentKeywords;  
  114.     }  
  115.   
  116.     /** 
  117.      * Initialize this device resolver implementation. 
  118.      * Registers the known set of device signature strings. 
  119.      * Subclasses may override to register additional strings. 
  120.      */  
  121.     protected void init() {  
  122.         getMobileUserAgentPrefixes().addAll(Arrays.asList(KNOWN_MOBILE_USER_AGENT_PREFIXES));  
  123.         getMobileUserAgentKeywords().addAll(Arrays.asList(KNOWN_MOBILE_USER_AGENT_KEYWORDS));  
  124.         getTabletUserAgentKeywords().addAll(Arrays.asList(KNOWN_TABLET_USER_AGENT_KEYWORDS));  
  125.     }  
  126.   
  127.     /** 
  128.      * Fallback called if no mobile device is matched by this resolver. 
  129.      * The default implementation of this method returns a "normal" {@link Device} that is neither mobile or a tablet. 
  130.      * Subclasses may override to try additional mobile or tablet device matching before falling back to a "normal" device. 
  131.      */  
  132.     protected Device resolveFallback(HttpServletRequest request) {  
  133.         return LiteDevice.NORMAL_INSTANCE;  
  134.     }  
  135.   
  136.     // internal helpers  
  137.   
  138.     private static final String[] KNOWN_MOBILE_USER_AGENT_PREFIXES = new String[] { "w3c ""w3c-""acs-""alav",  
  139.             "alca""amoi""audi""avan""benq""bird""blac""blaz""brew""cell""cldc""cmd-""dang",  
  140.             "doco""eric""hipt""htc_""inno""ipaq""ipod""jigs""kddi""keji""leno""lg-c""lg-d",  
  141.             "lg-g""lge-""lg/u""maui""maxo""midp""mits""mmef""mobi""mot-""moto""mwbp""nec-",  
  142.             "newt""noki""palm""pana""pant""phil""play""port""prox""qwap""sage""sams""sany",  
  143.             "sch-""sec-""send""seri""sgh-""shar""sie-""siem""smal""smar""sony""sph-""symb",  
  144.             "t-mo""teli""tim-""tosh""tsm-""upg1""upsi""vk-v""voda""wap-""wapa""wapi""wapp",  
  145.             "wapr""webc""winw""winw""xda ""xda-" };  
  146.   
  147.     private static final String[] KNOWN_MOBILE_USER_AGENT_KEYWORDS = new String[] { "blackberry""webos""ipod",  
  148.             "lge vx""midp""maemo""mmp""mobile""netfront""hiptop""nintendo DS""novarra""openweb",  
  149.             "opera mobi""opera mini""palm""psp""phone""smartphone""symbian""up.browser""up.link",  
  150.             "wap""windows ce" };  
  151.   
  152.     private static final String[] KNOWN_TABLET_USER_AGENT_KEYWORDS = new String[] { "ipad""playbook""hp-tablet",  
  153.             "kindle" };  
  154.   
  155. }  


原文地址:https://www.cnblogs.com/duyinqiang/p/5696476.html