SpringMVC 流程(1)-- DispatcherServlet

        用户首先向浏览器发送请求,那么该请求经过Tomcat转发给springmvc后,首先会被DispatcherServlet所拦截。从上一篇《SpringMVC流程(0) -- 整体流程》可以看到,DispatcherServlet相当于一个中心,那么SpringMVC为什么要这样设计,这就是前端控制器模式的运用。该DispatcherServlet 的结构如下图:



以下分析每个类的功能,以此探究SpringMVC是如何对Servlet进行封装。

一. GenericServlet
          该类实现了Servlet接口,作用是实现了Servlet接口的初始化方法,没有实现service()核心方法。这里有一个值得我们学习的细节,如下代码:
   
   
public void init(ServletConfig config) throws ServletException {
this.config = config;
this.init();
}
    
    
public void init() throws ServletException {
}
可以看到,该类预留了 init() 接口留给后代实现。这样扩展性在这点上会变得比较好。

二. HttpServlet
         该类实现了 Servlet 接口 的核心方法 service(),并且定义了很多的doGet(),doPost()方法等。在service()方法中,会根据请求的参数来决定该请求分发给哪一个方法进行处理。一般我们自定义Servlet的时候,继承HttpServlet类,重写它的doGet(),doPost()方法即可。


三. HttpServletBean
         该类主要作用是处理 web.xml 中定义的 Servlet 中的 init-param 标签,达到一个初始化的目的。和GenericServlet的功能类似,只是初始化的参数由我们设定。在该类中,有留了一个 initServletBean()方法给后代实现。

四.FrameworkServlet
         该类的主要作用有2个。一是 重写了父类HttpServletBean的initServletBean()方法,二是重写了HttpServlet的service()方法以及对doGet().doPost()等方法都进行了重写 分析下重写的这两个方法。

4.1 对于重写 initServletBean(),可以看下写的注释:
   
   
/**
* Overridden method of {@link HttpServletBean}, invoked after any bean properties
* have been set. Creates this servlet's WebApplicationContext.
*/
@Override
protected final void initServletBean() throws ServletException {
//这就是为什么我们在Tomcat启动的时候总是看见 信息: Initializing Spring FrameworkServlet 'springmvc'
getServletContext().log("Initializing Spring FrameworkServlet '" + getServletName() + "'");
if (this.logger.isInfoEnabled()) {
this.logger.info("FrameworkServlet '" + getServletName() + "': initialization started");
}
long startTime = System.currentTimeMillis();
 
try {
//初始化WebApplicationContext容器
this.webApplicationContext = initWebApplicationContext();
 
//该方法留给后代来进行实现
initFrameworkServlet();
}
... ...
}
          所以我们可以知道, 该重写方法主要是用来初始化WebApplicationContext容器 那么至于什么是WebApplicationContext容器,什么是上下文,可以看下其他文章,这些就不扯远,但是也是很重要的知识,篇幅有限。
          但是有一个点需要我们注意的是,在initWebApplicationContext()方法当中,肯定会执行以下的这个代码:
   
   
if (!this.refreshEventReceived) {
// Either the context is not a ConfigurableApplicationContext with refresh
// support or the context injected at construction time had already been
// refreshed -> trigger initial onRefresh manually here.
onRefresh(wac);
}
但是这个onFresh()方法却什么也没有做,只是留给了后代实现。它在这里的作用是留给子类,也就是SpringMVC的DispatcherServlet来完成SpringMVC初始化。

4.2 对于重写HttpServlet的 service() 方法和重写 doGet(),doPost()方法:
   
   
/**
* Override the parent class implementation in order to intercept PATCH
* requests.
*/
@Override
protected void service(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
 
String method = request.getMethod();
if (method.equalsIgnoreCase(RequestMethod.PATCH.name())) {
processRequest(request, response);
}
else {
super.service(request, response);
}
}
可以看到,重写 service() 的目的是为了拦截 PATCH 请求,因为HttpServlet中并没有对这种请求有处理。那么对于doGet(),doPost()等方法:
   
   
/**
* Delegate GET requests to processRequest/doService.
* <p>Will also be invoked by HttpServlet's default implementation of {@code doHead},
* with a {@code NoBodyResponse} that just captures the content length.
* @see #doService
* @see #doHead
*/
@Override
protected final void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
 
processRequest(request, response);
}
 
/**
* Delegate POST requests to {@link #processRequest}.
* @see #doService
*/
@Override
protected final void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
 
processRequest(request, response);
}
可以看到,重写doGet()和doPost()的主要目的是转到processRequest();方法当中,那么继续看:
   
   
/**
* Process this request, publishing an event regardless of the outcome.
* <p>The actual event handling is performed by the abstract
* {@link #doService} template method.
*/
protected final void processRequest(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
 
long startTime = System.currentTimeMillis();
Throwable failureCause = null;
 
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());
 
initContextHolders(request, localeContext, requestAttributes);
 
try {
doService(request, response);
}
... ...
}
在这里,前面的一些代码暂时也不知道是什么意思,因为正常来说最主要的方法还是 try 语句中的 doService(request,response) 方法。所以暂时不管前面的东西。在doService(request,response)方法中:
   
   
protected abstract void doService(HttpServletRequest request, HttpServletResponse response)
throws Exception;
可以看到是一个抽象方法,留给子类来进行实现。

总结:该类重写service()方法不是最大的作用, 最大的作用还是 
1. 初始化WebApplicationContext。
2. 把对请求的具体处理交给doService()方法让子类来实现。也就是SpringMVC的核心DispatchServlet来实现。

除此之外,要注意到,该类总共留了2个关键的方法给子类实现,一个是doService()方法,另外一个是onFresh()方法。

五. DispatcherServlet
         根据上面的介绍,在该类中重点介绍两个方法,一个是doService()方法,另外一个是onFresh()方法。
5.1 针对 onFresh()方法:
   
   
/**
* This implementation calls {@link #initStrategies}.
*/
@Override
protected void onRefresh(ApplicationContext context) {
initStrategies(context);
}
    
    
/**
* Initialize the strategy objects that this servlet uses.
* <p>May be overridden in subclasses in order to initialize further strategy objects.
*/
protected void initStrategies(ApplicationContext context) {
initMultipartResolver(context);
initLocaleResolver(context);
initThemeResolver(context);
initHandlerMappings(context);
initHandlerAdapters(context);
initHandlerExceptionResolvers(context);
initRequestToViewNameTranslator(context);
initViewResolvers(context);
initFlashMapManager(context);
}
可以看到,这些都是SpringMVC的默认实现,同时使用了一个策略模式。这样就非常容易的让我们更换策略,也就是说,它的一些视图解析器,适配器什么的我们都可以换。

5.2 针对 doService()方法:
   
   
/**
* Exposes the DispatcherServlet-specific request attributes and delegates to {@link #doDispatch}
* for the actual dispatching.
*/
@Override
protected void doService(HttpServletRequest request, HttpServletResponse response) throws Exception {
... ...
try {
doDispatch(request, response);
}
... ...
}
可以看到其注释,暴露request的一些属性,以及分派到 doDispatch()方法中:
   
   
/**
* Process the actual dispatching to the handler.
* <p>The handler will be obtained by applying the servlet's HandlerMappings in order.
* The HandlerAdapter will be obtained by querying the servlet's installed HandlerAdapters
* to find the first that supports the handler class.
* <p>All HTTP methods are handled by this method. It's up to HandlerAdapters or handlers
* themselves to decide which methods are acceptable.
* @param request current HTTP request
* @param response current HTTP response
* @throws Exception in case of any kind of processing failure
*/
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
HttpServletRequest processedRequest = request;
HandlerExecutionChain mappedHandler = null;
boolean multipartRequestParsed = false;
 
WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
 
try {
ModelAndView mv = null;
Exception dispatchException = null;
 
try {
processedRequest = checkMultipart(request);
multipartRequestParsed = (processedRequest != request);
 
// Determine handler for the current request.
// 根据请求得到合适的Handler
mappedHandler = getHandler(processedRequest);
if (mappedHandler == null || mappedHandler.getHandler() == null) {
noHandlerFound(processedRequest, response);
return;
}
 
// Determine handler adapter for the current request.
//根据Handler找到其适配器
HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
 
// Process last-modified header, if supported by the handler.
String method = request.getMethod();
boolean isGet = "GET".equals(method);
if (isGet || "HEAD".equals(method)) {
long lastModified = ha.getLastModified(request, mappedHandler.getHandler());
if (logger.isDebugEnabled()) {
logger.debug("Last-Modified value for [" + getRequestUri(request) + "] is: " + lastMod ified);
}
if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) {
return;
}
}
 
if (!mappedHandler.applyPreHandle(processedRequest, response)) {
return;
}
 
// Actually invoke the handler.
//根据适配器找到其合适的处理方法,并且返回一个ModelAndView对象
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
 
if (asyncManager.isConcurrentHandlingStarted()) {
return;
}
 
//解析ModelAndView对象的视图和数据
applyDefaultViewName(request, mv);
mappedHandler.applyPostHandle(processedRequest, response, mv);
}
catch (Exception ex) {
dispatchException = ex;
}
//最后返回数据
processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
}
catch (Exception ex) {
triggerAfterCompletion(processedRequest, response, mappedHandler, ex);
}
catch (Error err) {
triggerAfterCompletionWithError(processedRequest, response, mappedHandler, err);
}
finally {
if (asyncManager.isConcurrentHandlingStarted()) {
// Instead of postHandle and afterCompletion
if (mappedHandler != null) {
mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);
}
}
else {
// Clean up any resources used by a multipart request.
if (multipartRequestParsed) {
cleanupMultipart(processedRequest);
}
}
}
}

在该方法中,再参照 上一篇《SpringMVC流程(0) -- 整体流程》,就可以发现, 《SpringMVC流程(0) -- 整体流程》这个文字说明其实就是这些代码的解释。再把代码单独拿出来,方便查看:
   
   
// 根据请求得到合适的Handler
mappedHandler = getHandler(processedRequest);
... ...
//根据Handler找到其适配器
HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
... ...
//根据适配器找到其合适的处理方法,并且返回一个ModelAndView对象
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
//解析ModelAndView对象的视图和数据
applyDefaultViewName(request, mv);
mappedHandler.applyPostHandle(processedRequest, response, mv);
... ...
//最后返回数据
processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
... ...

所以我们可以看到,整个整个大流程,都是由该DispatcherServlet来进行控制。


总结:
1. DispatcherServlet整个层次结构为
    
          其中,GenericServlet主要作用为对Servlet进行初始化;HttpServlet主要作用为重写Servlet的service()方法,并且分发到doGet(),doPost()方法当中,相当于具体的对请求的处理;HttpServletBean主要作用为对web.xml文件的初始化参数的处理;FrameworkServlet主要作用为 初始化WebApplicationContext,同时留下reFresh()方法让SpringMVC完成初始化,也留下doService()方法让SpringMVC来实现具体的对请求的处理逻辑。

2. DispatcherServlet 主要作用有2个,一个是对于SpringMVC的默认初始化,另外一个是对于整个流程的调度控制。





Ps:
1. 这里还有很多知识篇幅有限略去了,比如说ApplicationContext,策略模式,适配器模式,开闭原则等,有空大家可以去自己查找下。
2. 另外介绍一篇写得比较深入也比较好的文章: SpringMVC源码剖析(一)- 从抽象和接口说起 http://my.oschina.net/lichhao/blog/99039?p=3#comments
3. 有理解的不对的地方欢迎提出,3Q。




  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值