一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第16天,点击查看活动详情。
目录
SpringMvc源码探秘(一)Tomcat启动项目时Spring做了什么
SpringMvc源码探秘(二)DispatchServlet初始化
SpringMvc源码探秘(三)请求是怎么到达Controller的(一)
SpringMvc源码探秘(三)请求是怎么到达Controller的(二)
SpringMvc源码探秘(四)请求的参数是怎么转换成对象的
前言
在上一章节,讲解了Spring是如何把请求转换成我们需要的参数传入我们入参对象,不知道看完有没有一些收获,我在上一章的最后留了一个思考题。
正式开始
参数解析
我们能不能自定义一个ArgumentResolver呢,来解析一些特定的类,比方说Token,实现不用在业务代码里token转换实体类呢。
上一章节中有提到这个问题,这个问题当然是可以做到的。我们先来描述一下,上面的这个场景。
在我们正常的接口交互中,都会要求前端在调用接口时,在请求头里添加一个token字段,在接下来接口使用的时候,可以通过token获取用户信息,在普通的开发时,我们获取用户信息做法通常是这样的。
那么每次有使用到token获取User的时候,都需要调用这么一行代码,是否觉得多余?但是又没有什么好办法优化。
记住这个时候,我们已经是阅读过Spring源码的人了。简历上都得写上一句精通Spring,并阅读过源码。这个时候只需要参照我们上一章节讲到的ArgumentResolver即可实现,具体怎么做呢。
我们参考RequestResponseBodyMethodProcessor,先要创建一个类,接着这个类要实现HandlerMethodArgumentResolver接口就是这样。
实现这个接口要继承两个接口方法,supoortsParameter、ResolveArgument。
这两个方法是干嘛的呢,我们需要看一下SpringMVC处理参数那里的方法。
可以看到SpringMVC调用resolvers的supportsParameter方法时,是遍历每一个MethodParameter对象来调用该方法,得知是否支持该参数,如果支持的话,会嗲用resolveArgument方法来获得该参数的具体值,那么我们现在知道了,可以具体的来实现这两个方法了。
supportsParameter
这里比较简单,我实现的意思就是获取该参数的类型,判断该类型是不是和我们要获取的User类是一样的,是一样的就返回True了。
resolveArgument
这里我实现的也比较简单,因为正常的业务开发中,TokenUtil都是被封装过的,所以只要把request传斤工具类就可以了。
我这里的TokenUtil只是模拟一下获取用户的逻辑,所以不用太在意。
这里我们的ArgumentResolver类已经创建好了,接下来要把他注册进HandlerAdapter中。
经过我们前面的源码阅读,知道他是会注册进Spring容器的,所以我这里采用的是比较简单的方式BeanPostProcess的方式,为了保证他的afterPropertiesSet初始化方法执行完成后,再执行我们这个添加ArgumentResovler,所以才用了这个方式,After方法中,去修改他的属性。
后面的代码,只需要在Controller的参数列表中写上我们当前这个属性就可以了。
这个方法很简单,我就不说了,后面我们直接看代码结果。
这里实验的结果已经出来了。这一惊说明我们的解析器成功了。如果解析器没有运行的话,此处的user应该是null的,网页上应该显示的是另外的字样了。
这个方法不仅可以用做解析token字段,还可以用于参数加密什么的,其他玩法大家可以在评论区讨论一下。
请求拦截器
在正常的业务代码中,和token绑定的用户信息是放在缓存中的,既然是缓存,那么是会过期的,所以会有一个校验是否过期,或者token是否被篡改的一个需求,这个功能我们就可以借助请求拦截器完成。 在阅读DispatchServlet的doDispatch时,我们看到下面的这段代码。
在正式调用Controller方法之前,会去调用applyPreHandle方法,这个方法可以决定,最后是否执行Controller的代码。
通过看applyPreHandle方法得知,该类内部有个interceptorList集合。
追过去得知,需要的类型是HandlerInterceptor接口类型。此时我们也创建一个类,然后实现这个接口。
在上方的applyPreHandle方法,看到会在for中区调用preHandle方法,所以我们在此类也实现这个方法即可。
到这里就算是完成了,如果存在的话,返回True,也就是会继续执行,如果不存在就会返回False,也就是在applyPreHandle时就return出去了。
注册这个拦截器的方式,我也是选择了最简单的方式,通过PostProcessor
请求日志记录
在日常开发中,我们会遇到需求,统计网站访问量、PV、UV这些。
在DispatchServlet的父类FrameworkServlet类中的ProcessRequest方法中,这个方法最终会去调用DispatchServlet的doService方法,嗲用doService方法是被Try包裹的,在这个Try中的finally代码块中的最后有这样的一行代码。
看到这里就知道,请求的所有方法结束后,他会向Spring发送一个事件。事件是ServletRequestHandled事件。来告诉是监听器们,这里有个请求处理完了。
这个监听器里面记录的东西还蛮全的,像请求地址、请求IP、请求方式、处理时间、返回的状态。。这些都有记录。
那么我们完全可以写个监听器来监听这个事件,来做一些日志记录什么的工作,代码实现如下。
我这里就简单写了一下,正常这些个日志是要落库的。
通过查看控制台,可以看到,这个事件监听器已经应用成功了,并且监听到了事件的触发。
告一段落
到这里了,这一篇文章就结束了,SpringMVC系列到这一篇也结束了,后面会开启SpringBoot的源码揭秘部分。
都看到这了,点个赞再走呗,宝~
结束语
写文章的目的是为了帮助自己巩固知识,写的不好或者错误的地方可以在评论区指出。如果您看了文章觉得对您有所帮助可以点个赞,如果发现有些问题产生了疑惑,或者不明白的可以评论、加我微信,一定知无不言。当然也希望和大家交个朋友,相互学习。