以下内容基于书:《看透SpringMVC-源代码分析与实践》基本照搬。。。用于自己查阅备忘。
先看一眼DispatcherServlet继承树
我们知道servlet处理方法都是通过HttpServlet的service方法开始,FrameworkServlet重写了父类HttpServlet的service方法。代码如下:
FrameworkServlet service
protected void service(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
/*
因为HttpServlet中是没有定义doPatch方法的,所以规定
if(是doPatch类型)
就交由本类(FrameworkServlet)内的doPatch方法运行。
else
调用父类(HttpServlet)的service方法。
*/
if (HttpMethod.PATCH.matches(request.getMethod())) {
processRequest(request, response);
}
else {
super.service(request, response);
}
}
HttpServlet的service是根据类型调用不同的do方法。如doGet,doPost。这些方法在FrameworkServlet内被重写了,所以最终调用的还是FrameworkServlet内的相应方法。
代码如下(以doGet为例):
FrameworkServlet(doxxx)
protected final void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
processRequest(request, response);
}
我们总结一下这个流程,以doGet为例
get request->FrameworkServlet(service)->判断是不是patch请求:不是->HttpServlet(service)->判断是什么类型:get->HttpServlet(doGet)->doGet被子类重写->FrameworkServlet(doGet)->FrameworkServlet(processRequest)
FrameworkServlet内的其他doxxx请求也是类似的,最终都是processRequest(request, response);来处理,也就是说在HttpServlet的service方法内doxx先按类型分开,然后在FrameworkServlet的doxx合并到一个方法处理。绕了一个大弯。
- 之所以重新合并,原因还没看到,应该是不同类型的请求在spring内部还需要进行统计处理。
- 之所以不直接利用service进行处理,原因书上有讲,主要是出于灵活性的考虑吧,写过代码的同学应该都能理解。
下面我们就看下processRequest方法,代码如下:
FrameworkServlet(processRequest)
protected final void processRequest(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
long startTime = System.currentTimeMillis();
Throwable failureCause = null;
//先获取LocaleContext与RequestAttributes备份起来
//用于在finally中进行恢复
LocaleContext previousLocaleContext = LocaleContextHolder.getLocaleContext();
LocaleContext localeContext = buildLocaleContext(request);
RequestAttributes previousAttributes = RequestContextHolder.getRequestAttributes();
ServletRequestAttributes requestAttributes = buildRequestAttributes(request, response, previousAttributes);
WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
asyncManager.registerCallableInterceptor(FrameworkServlet.class.getName(), new RequestBindingInterceptor());
//将当前的localeContext,requestAttributes覆盖进去
initContextHolders(request, localeContext, requestAttributes);
try {
//实际处理请求的地方
doService(request, response);
}
catch(...){//catch代码没理解上的意义,被我删掉了
}
finally {
//恢复相关信息,因为在service可能还有Filter等操作
resetContextHolders(request, previousLocaleContext, previousAttributes);
if (requestAttributes != null) {
requestAttributes.requestCompleted();
}
//log...
//发布Event,类型是ServletRequestHandledEvent,这个下面有解释
publishRequestHandledEvent(request, response, startTime, failureCause);
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
从细节看,
LocaleContextHolder是一个abstract class,没有实现的class,内部的方法都是静态的,用法就是不实例化直接调用,通过它可以获得LocaleContext,也就是语言等本地化信息,如”zh-cn”。 一般获取Locale的方法是request.getLocale(),LocaleContextHolder内的方法因为是静态的,可以随时随地调用,更方便一些。
RequestContextHolder也是一样的道理。通过RequestContextHolder可以get到request与session。
关于发布与监听事件
publishEvents设置为true时,请求处理结束后就会发出这个消息,publishEvents在web.xml文件配置SpringMVC的servlet时配置,默认为true。
那怎么做到监听这个事件呢,很简单,demo如下:
//做两件事 1、@Component注解。 2、实现ApplicationListener
@Component
public class ServletRequestHandledEventListener implements ApplicationListener<ServletRequestHandledEvent>{
final static Logger logger = LoggerFactory.getLogger("RequestProcessLog");
@override
public void onApplicationEvent(ServletRequestHandledEvent event){
logger.info(event.getDescription());
}
}
doService(request, response);
这行语句是实际处理请求的核心语句,它是个抽象方法,交给子类,也就是DispatcherServlet实现。
所以我们下篇博客就观察DispatcherServlet的代码。