上篇博客说到,当我执行程序时,springmvc的控制下,它始终跳转到首页,而不正常跳转。当时通过换一个服务器解决了问题,以为是缓存的事儿。但后来又发生了同样的事儿,顿时感觉出事儿了。就立马降低了日志输出等级,一看,果然有问题。虽然目前还没有解决,只是有一些思路,不过,也写下了记录一下!
一、程序的简单概述
页面打开,是加载到一个列表页,然后通过点击列表页的链接按钮,跳转到详情页。在详情页中,有倒计时,倒计时结束后,会出现一个开始抢购商品的按钮,点击按钮,就可以进行抢购提交订单。
这之中涉及到的后台方法有:list(加载被抢购商品的基本信息列表)、detail(商品详情)、exposer(暴露抢购地址)、execute(执行抢购)、time(获取当前时间)
整个模块的简单流程是:(每次都忍不住自己吐槽画图水平,我对word情有独钟,已经很久不画流程图,手都生疏了,各位游客就将就着看吧)
二、出现的问题
之前是在列表页跳转到商品详情页的时候,总是反复的打开主页面,反复的执行controller的同一个方法。现在的情况是,在执行倒计时的过程中,显示一个执行抢购的按钮,然后给这个按钮绑定单击执行抢购事件。但是在这个过程中,需要向后台请求time,还有暴露抢购地址的方法。问题是:当执行time请求的时候,没有问题,但是请求地址暴露方法时,又给跳转到list方法上了,根本不执行exposer方法。这样就直接导致,web页面无法显示开始抢购的按钮,整个后续的流程,也就无法执行完!
三、日志分析(截取关键)
首先看执行相关方法的日志输出:
[org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping]-[DEBUG] Looking up
handler method for path /1001/exposer
2017-03-05 12:34:13,072 [http-nio-8080-exec-2]
[org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping]-[DEBUG] Returning
handler method [public java.lang.String Web.SeckillController.list(org.springframework.ui.Model)]
从日志可以看出,当在寻找/1001/exposer的时候,本来应该返回的是SecKillController.exposer()方法,但结果,返回的是list方法。然后我就以为是这个方法选择器出问题了,还有就是这个路径匹配出了问题,就加载源码,设置断点,重点调试RequestMappingHandlerMapping类和HandlerMethodSelector类,就是想看看这个东西是不是真的在代码执行的时候就出问题了,事实证明并不是的。然后再比对日志,更是清晰明了的说明了问题:
017-03-05 12:29:58,214 [http-nio-8080-exec-2]
[org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping]-[DEBUG] Looking for
request mappings in application context: WebApplicationContext for namespace 'SecKill-servlet': startup
date [Sun Mar 05 12:29:55 CST 2017]; parent: Root WebApplicationContext
2017-03-05 12:32:30,112 [http-nio-8080-exec-2]
[org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping]-[INFO] Mapped
"{[/time/now],methods=[GET]}" onto public Dto.SeckillResult<java.lang.Long> Web.SeckillController.time()
2017-03-05 12:32:33,108 [http-nio-8080-exec-2]
[org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping]-[INFO] Mapped
"{[],methods=[GET]}" onto public java.lang.String Web.SeckillController.list(org.springframework.ui.Model)
2017-03-05 12:32:35,216 [http-nio-8080-exec-2]
[org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping]-[INFO] Mapped
"{[/{seckillId}/{md5}/execution],methods=[POST],produces=[application/json;charset=UTF-8]}" onto public
Dto.SeckillResult<Dto.SeckillExecution>
Web.SeckillController.execute(java.lang.Long,java.lang.String,java.lang.Long)
2017-03-05 12:33:37,191 [http-nio-8080-exec-2]
[org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping]-[INFO] Mapped
"{[/{seckillId}/detail],methods=[GET]}" onto public java.lang.String
Web.SeckillController.detail(java.lang.Long,org.springframework.ui.Model)
2017-03-05 12:33:48,565 [http-nio-8080-exec-2]
[org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping]-[INFO] Mapped
"{[/{seckillId}/exposer],methods=[POST],produces=[application/json;charset=UTF-8]}" onto public
Dto.SeckillResult<Dto.Exposer> Web.SeckillController.exposer(java.lang.Long)
在日志信息中,很明显可以看出,每个方法都是匹配无误的。那么到底是哪儿出了问题呢?
注意看每次都会跳转至的list方法的匹配特点,是[ ],也即是说,如果路径没有匹配地址,那么就会执行到list方法。什么时候,明明URL上有路径,但是却又被servlet拦截不到呢,反而去执行了list方法呢?
三、问题分析
程序如果没有访问路径,那么本来应该报出404的错误。也就是上一篇博客中的疑惑一:消失的404页面。看了日志之后,并不是404页面消失了,而是当在controller中没有找到匹配路径时,而list方法的匹配路径为[ ],这时候,自然而然的就会执行list,并没有所谓的错误存在,也就并不会有预想中的404页面。
首先,上次的跳转详情页,出现的反复跳转到list首页,根据我今天犯的一个错,突然醒悟,我把那个detail给打错了,导致无法匹配路径,直接执行list方法。
其次,在请求暴露抢购路径时,为了避免用户通过浏览器加上猜测,而获得抢购路径,而在controllser中标记了Post请求,然而,在Js中请求这个方法的时候,却用的是Get,自然而然不会跳到对应的方法,而找不到路径的时候,而list的匹配路径为[ ],很自然的又执行了list方法。
以上,就是问题出现的根本原因,和缓存没有关系。我上次换了一个tomcat服务器,那个并不是解决问题的方法,我现在想想,可能是我刚好换了服务器,我刚好又把那个detail的词给些对了,才造成了我以为是换了服务器,清理了缓存解决问题的错觉!
四、总结
虽然问题解决了,但是问题又来了,为什么list的分发路径变为了[ ],而自从发现这个情况之后,我反复的测试了好几次,刚开始的时候,不管我怎么换路径,始终都是[ ],后来再试了几次,包括改请求方式,还有方法名等,还是[ ],可是过了一会儿,我再重启的时候,路径就变成我所配置的路径。再然后反复撤销代码,才发现了端倪:
@RequestMapping(name = "/list", method = RequestMethod.GET)
呃,很想打死我自己!虽然导致问题出现的原因很奇葩,也是由于自己的粗心大意了。但是,解决问题的过程很惊险和刺激,尤其是调试springMVC的源码,还有就是读日志找原因的过程,真的感觉很好。说实在的,我很少去一行一行的分析日志,然后调试框架的源码,呃,如果不敢工期的话,我倒希望我多手贱几回!哈哈哈哈。。。。。。
从明天起,我要拿出我写诗的心情去写代码,立志把代码写成诗一般的感觉。。。。。。。。。