最新公司的电商平台上了个内测版本, 测试用户报告了一个严重错误, 在某些情况下可能导致产品无法搜索, 产品详情页面也打不开.
先看日志:
org.springframework.web.util.NestedServletException: Request processing failed; nested exception is java.lang.ClassCastException: java.util.SubList cannot be cast to java.util.LinkedList at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:965) at org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:844) at javax.servlet.http.HttpServlet.service(HttpServlet.java:735) at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:829) at javax.servlet.http.HttpServlet.service(HttpServlet.java:848) at org.eclipse.jetty.servlet.ServletHolder.handle(ServletHolder.java:643) at org.eclipse.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1374) at com.synnex.ecx.web.common.session.EcContextFilter.doFilter(EcContextFilter.java:92) at org.springframework.web.filter.DelegatingFilterProxy.invokeDelegate(DelegatingFilterProxy.java:343) at org.springframework.web.filter.DelegatingFilterProxy.doFilter(DelegatingFilterProxy.java:260) at org.eclipse.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1345) at com.synnex.ecx.web.common.security.XssFilter.doFilter(XssFilter.java:26) at org.eclipse.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1345) at com.synnex.cis.web.ext.filter.UrlSpyControlFilter.doFilter(UrlSpyControlFilter.java:138) at org.eclipse.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1345) at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:88) at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:106) at org.eclipse.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1345) at com.synnex.jee.web.Log4JFilter.doFilter(Log4JFilter.java:54) at org.eclipse.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1345) at com.synnex.cis.web.ext.filter.UrlSpyControlFilter.doFilterInner(UrlSpyControlFilter.java:563) at com.synnex.cis.web.ext.filter.GlobalUrlSpyControlFilter.doFilter(GlobalUrlSpyControlFilter.java:22) at org.eclipse.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1345) at com.synnex.jee.web.PerformanceMeasurementFilter.doFilter(PerformanceMeasurementFilter.java:163) at org.eclipse.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1345) at org.eclipse.jetty.servlet.ServletHandler.doHandle(ServletHandler.java:491) at org.eclipse.jetty.server.handler.ScopedHandler.handle(ScopedHandler.java:131) at org.eclipse.jetty.security.SecurityHandler.handle(SecurityHandler.java:486) at org.eclipse.jetty.server.session.SessionHandler.doHandle(SessionHandler.java:255) at org.eclipse.jetty.server.handler.ContextHandler.doHandle(ContextHandler.java:1067) at org.eclipse.jetty.servlet.ServletHandler.doScope(ServletHandler.java:417) at org.eclipse.jetty.server.session.SessionHandler.doScope(SessionHandler.java:216) at org.eclipse.jetty.server.handler.ContextHandler.doScope(ContextHandler.java:1001) at org.eclipse.jetty.server.handler.ScopedHandler.handle(ScopedHandler.java:129) at org.eclipse.jetty.server.handler.ContextHandlerCollection.handle(ContextHandlerCollection.java:250) at org.eclipse.jetty.server.handler.HandlerCollection.handle(HandlerCollection.java:149) at org.eclipse.jetty.server.handler.HandlerWrapper.handle(HandlerWrapper.java:111) at org.eclipse.jetty.server.Server.handle(Server.java:360) at org.eclipse.jetty.server.AbstractHttpConnection.handleRequest(AbstractHttpConnection.java:454) at org.eclipse.jetty.server.AbstractHttpConnection.headerComplete(AbstractHttpConnection.java:890) at org.eclipse.jetty.server.AbstractHttpConnection$RequestHandler.headerComplete(AbstractHttpConnection.java:944) at org.eclipse.jetty.http.HttpParser.parseNext(HttpParser.java:630) at org.eclipse.jetty.http.HttpParser.parseAvailable(HttpParser.java:230) at org.eclipse.jetty.server.AsyncHttpConnection.handle(AsyncHttpConnection.java:77) at org.eclipse.jetty.io.nio.SelectChanne
经过代码跟踪, 发现一段这样的可疑代码:
LinkedList<String> recentKeywords = (LinkedList<String>) map .get(PersonalizationMap.PREF_RECENTLY_SEARCHED_KEYWORDS);
此处, 从map中取出一个List, 强制转型为LinkedList. 但是, Map中取出的List对象预先做了截断处理.
if (list.size() > VALUE_COUNT_LIMIT) { list = list.subList(0, VALUE_COUNT_LIMIT); }
上面代码中的subList, 返回的对象其实是java.util.SubList, 所以转型失败.
究其原因, 是没有做到"基于接口编程", 其实没有必要强制转型为LinkedList, 转型为List就行了.
将代码稍作修改后, 问题解决. 如下:
List<String> recentKeywords = (List<String>) ecContext.getPref().get( PersonalizationMap.PREF_RECENTLY_SEARCHED_KEYWORDS);