SpringMvc源码探秘(五)从源码层出发有哪些可以拓展的地方

486 阅读6分钟

一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第16天,点击查看活动详情

目录

SpringMvc源码探秘(一)Tomcat启动项目时Spring做了什么
SpringMvc源码探秘(二)DispatchServlet初始化
SpringMvc源码探秘(三)请求是怎么到达Controller的(一)
SpringMvc源码探秘(三)请求是怎么到达Controller的(二)
SpringMvc源码探秘(四)请求的参数是怎么转换成对象的

前言

在上一章节,讲解了Spring是如何把请求转换成我们需要的参数传入我们入参对象,不知道看完有没有一些收获,我在上一章的最后留了一个思考题。

正式开始

参数解析

我们能不能自定义一个ArgumentResolver呢,来解析一些特定的类,比方说Token,实现不用在业务代码里token转换实体类呢。

上一章节中有提到这个问题,这个问题当然是可以做到的。我们先来描述一下,上面的这个场景。
在我们正常的接口交互中,都会要求前端在调用接口时,在请求头里添加一个token字段,在接下来接口使用的时候,可以通过token获取用户信息,在普通的开发时,我们获取用户信息做法通常是这样的。 image.png 那么每次有使用到token获取User的时候,都需要调用这么一行代码,是否觉得多余?但是又没有什么好办法优化。

记住这个时候,我们已经是阅读过Spring源码的人了。简历上都得写上一句精通Spring,并阅读过源码。这个时候只需要参照我们上一章节讲到的ArgumentResolver即可实现,具体怎么做呢。

我们参考RequestResponseBodyMethodProcessor,先要创建一个类,接着这个类要实现HandlerMethodArgumentResolver接口就是这样。 image.png 实现这个接口要继承两个接口方法,supoortsParameter、ResolveArgument。
这两个方法是干嘛的呢,我们需要看一下SpringMVC处理参数那里的方法。 image.png 可以看到SpringMVC调用resolvers的supportsParameter方法时,是遍历每一个MethodParameter对象来调用该方法,得知是否支持该参数,如果支持的话,会嗲用resolveArgument方法来获得该参数的具体值,那么我们现在知道了,可以具体的来实现这两个方法了。

supportsParameter

image.png 这里比较简单,我实现的意思就是获取该参数的类型,判断该类型是不是和我们要获取的User类是一样的,是一样的就返回True了。

resolveArgument

image.png 这里我实现的也比较简单,因为正常的业务开发中,TokenUtil都是被封装过的,所以只要把request传斤工具类就可以了。

image.png 我这里的TokenUtil只是模拟一下获取用户的逻辑,所以不用太在意。

这里我们的ArgumentResolver类已经创建好了,接下来要把他注册进HandlerAdapter中。 image.png 经过我们前面的源码阅读,知道他是会注册进Spring容器的,所以我这里采用的是比较简单的方式BeanPostProcess的方式,为了保证他的afterPropertiesSet初始化方法执行完成后,再执行我们这个添加ArgumentResovler,所以才用了这个方式,After方法中,去修改他的属性。

后面的代码,只需要在Controller的参数列表中写上我们当前这个属性就可以了。 image.png 这个方法很简单,我就不说了,后面我们直接看代码结果。

image.png 这里实验的结果已经出来了。这一惊说明我们的解析器成功了。如果解析器没有运行的话,此处的user应该是null的,网页上应该显示的是另外的字样了。

这个方法不仅可以用做解析token字段,还可以用于参数加密什么的,其他玩法大家可以在评论区讨论一下。

请求拦截器

在正常的业务代码中,和token绑定的用户信息是放在缓存中的,既然是缓存,那么是会过期的,所以会有一个校验是否过期,或者token是否被篡改的一个需求,这个功能我们就可以借助请求拦截器完成。 在阅读DispatchServlet的doDispatch时,我们看到下面的这段代码。

image.png 在正式调用Controller方法之前,会去调用applyPreHandle方法,这个方法可以决定,最后是否执行Controller的代码。

image.png 通过看applyPreHandle方法得知,该类内部有个interceptorList集合。 image.png 追过去得知,需要的类型是HandlerInterceptor接口类型。此时我们也创建一个类,然后实现这个接口。

image.png 在上方的applyPreHandle方法,看到会在for中区调用preHandle方法,所以我们在此类也实现这个方法即可。

image.png 到这里就算是完成了,如果存在的话,返回True,也就是会继续执行,如果不存在就会返回False,也就是在applyPreHandle时就return出去了。

注册这个拦截器的方式,我也是选择了最简单的方式,通过PostProcessor image.png

请求日志记录

在日常开发中,我们会遇到需求,统计网站访问量、PV、UV这些。

在DispatchServlet的父类FrameworkServlet类中的ProcessRequest方法中,这个方法最终会去调用DispatchServlet的doService方法,嗲用doService方法是被Try包裹的,在这个Try中的finally代码块中的最后有这样的一行代码。 image.png image.png 看到这里就知道,请求的所有方法结束后,他会向Spring发送一个事件。事件是ServletRequestHandled事件。来告诉是监听器们,这里有个请求处理完了。

这个监听器里面记录的东西还蛮全的,像请求地址、请求IP、请求方式、处理时间、返回的状态。。这些都有记录。

那么我们完全可以写个监听器来监听这个事件,来做一些日志记录什么的工作,代码实现如下。 image.png 我这里就简单写了一下,正常这些个日志是要落库的。

image.png 通过查看控制台,可以看到,这个事件监听器已经应用成功了,并且监听到了事件的触发。

告一段落

到这里了,这一篇文章就结束了,SpringMVC系列到这一篇也结束了,后面会开启SpringBoot的源码揭秘部分。

都看到这了,点个赞再走呗,宝~

结束语

写文章的目的是为了帮助自己巩固知识,写的不好或者错误的地方可以在评论区指出。如果您看了文章觉得对您有所帮助可以点个赞,如果发现有些问题产生了疑惑,或者不明白的可以评论、加我微信,一定知无不言。当然也希望和大家交个朋友,相互学习。