spring mvc读书笔记HttpServletBean,FrameworkServlet和DispathcerServlet,doDispatch结构

 

首先,HttpServletBean,FrameworkServlet和DispathcerServlet这三个Servlet的处理过程,最后分析springmvc中最重要的doDispatch方法

 

httpServletBean

主要参与创建工作,并没有涉及到请求的处理。

FrameworkServlet

servlet的处理过程:首先是从servlet接口的service方法开始,然后在HttpServlet的service方法中根据请求的类型不同将请求路由到doget,doHead,doPost,doPut,doDelete,doOptionsdoTrace七个方法。并且做了doHead,doOptions,doTrace的默认实现,其中doHead调用doGet,然后返回只有header没有bodyresponse.

在FrameworkServ;et中重写了service,doGet,doPost,doPut,doDelte,doOptioms,doTrace方法(除了doHead所有处理请求的方法).在service中增加了对patch类型请求的处理,其他类型的请求直接交给了父类处理;doOptions和doTrace方法可以通过设置dispatchOptionsRequest和dispatchTraceRequest参数决定是自己处理还是交给父类处理;doGet,doPost,doPut,doDelete都是自己处理。所有需要自己处理的请求都交给了processRequest方法统一处理。

@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);

                   }

         }

 

         protected final void doGet(HttpServletRequest request, HttpServletResponse response)

                            throws ServletException, IOException {

 

                   processRequest(request, response);

         }

这里所做的事情和HttpServlet中对请求按类型分别进行处理不同,这里正好相反,将所有请求都合并到了processRequest方法,所有请求类型都执行相同的模板方法processRequest.后面将会对不同的请求交给不同的Handler进行处理。这里的service不是直接覆盖HttpServlet中的service方法,而是又将调用super.service(),如果直接用processRequest处理,我们在做一些特殊的需求在Post请求处理前对request做一些处理,这时可能会新建一个继承自DispatcherServlet的类,然后覆盖doPost方法,在里面先对request做处理,然后在电泳supper.doPost(),这时就会出现问题了。

 

   processRequestFrameworkServlet类在处理请求中最核心的方法

 

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);

                   }

                   catch (ServletException ex) {

                            failureCause = ex;

                            throw ex;

                   }

                   catch (IOException ex) {

                            failureCause = ex;

                            throw ex;

                   }

                   catch (Throwable ex) {

                            failureCause = ex;

                            throw new NestedServletException("Request processing failed", ex);

                   }

                   finally {

                            resetContextHolders(request, previousLocaleContext, previousAttributes);

                            if (requestAttributes != null) {

                                     requestAttributes.requestCompleted();

                            }

                            if (logger.isDebugEnabled()) {

                                     if (failureCause != null) {

                                               this.logger.debug("Could not complete request", failureCause);

                                     }

                                     else {

                                               if (asyncManager.isConcurrentHandlingStarted()) {

                                                        logger.debug("Leaving response open for concurrent processing");

                                               }

                                               else {

                                                        this.logger.debug("Successfully completed request");

                                               }

                                     }

                            }

                            publishRequestHandledEvent(request, response, startTime, failureCause);

                   }

         }

 

         processRequest方法中最核心的语句是doService(request,response),这是一个模板方法,在DispatcherServlet中具体实现。在doService前后还做了一些事情(装饰模式),先开始从LocaleContextHolder中取得LocaleContext并设置到previousLocalContext,从requestContextHolder中获得RequestAttributes并设置到perviousAttributes,然后调用buildLocaleContext和BuildRequestAttributes方法获得当前请求的LocaleContext和RequestAttributes,通过initContextHolders方法将现有属性设置到LocaleContextHolderRequestContextHolder中(处理完成后再恢复到原来的值),

   LocaleContext和RequestAttributes是什么,localeContext里面存放着Locale(本地信息化 zh-cn等),RequestAttributes是spring的一个接口,通过它可以get/set/removeAttribute,根据scope参数判断是request还是session.这里具体使用ServletRequestAttribute类,里面封装了request,response,session,都可以直接获得。

  // org.springframework.web.context.request.ServletRequestAttributes.class

public void setAttribute(String name, Object value, int scope) {

                   if (scope == SCOPE_REQUEST) {

                            if (!isRequestActive()) {

                                     throw new IllegalStateException(

                                                        "Cannot set request attribute - request is not active anymore!");

                            }

                            this.request.setAttribute(name, value);

                   }

                   else {

                            HttpSession session = getSession(true);

                            this.sessionAttributesToUpdate.remove(name);

                            session.setAttribute(name, value);

                   }

         }

 

 通过scope判断是对request还是session进行操作;是直接对request和session进行操作,判断request是否已经被使用过,isRequestActive(),当requestCompleted之后requestActive就会变为false,request未完成之前为true. RequestAttributes用来管理request和session的属性。

 

org.springframework.context.i18n.LocaleContextHolder.class

LocaleContextHolder是一个抽象类,其中所有的属性和方法都是static的,可以直接调用,没有父类和子类,不能实例化。

private static final ThreadLocal<LocaleContext> localeContextHolder =

                            new NamedThreadLocal<LocaleContext>("Locale context");

 

         private static final ThreadLocal<LocaleContext> inheritableLocaleContextHolder =

                            new NamedInheritableThreadLocal<LocaleContext>("Locale context");

上面两个属性都是ThreadLocal<LocaleContext>类型的,LocaleContextHolder中提供了get/set localeContext和Locale方法,比如在程序中要用到Locale的时候,首先是request.getLocale(),这是最直接的方法,如果service层要用到locale时,这时没有request,可以在controller中拿出来再传到service中,这样也可以,但是还有一种方法就是用LocaleContextHolder.getLocale();

 

org.springframework.web.context.request.RequestContextHolder.class也是一个抽象的类,里面的属性和方法都是static的:

private static final ThreadLocal<RequestAttributes> requestAttributesHolder =

                            new NamedThreadLocal<RequestAttributes>("Request attributes");

         private static final ThreadLocal<RequestAttributes> inheritableRequestAttributesHolder =

                            new NamedInheritableThreadLocal<RequestAttributes>("Request context");

这两个属性都是ThreadLocal<RequestAttributes>类型的,其中的方法都是对requestAttribute的set/get/reset/currentRequestAttribute/faces.还包含了一个内部类FacesRequestAttributesFactory,避免硬编码。

  FrameworkServlet中processRequest中最后finally中对request的属性进行了恢复调用resetContextHolder方法将原来的localeContext和requestAttribues恢复。原因是可能在servlet外还有别的操作,为了不影响操作,所以需要进行恢复。

 

  最后是publishRequesthandledEvent(request,response,startTime,failureCanse)发布了消息

org.springframework.web.servlet.FrameworkServlet.class

         private void publishRequestHandledEvent(

                            HttpServletRequest request, HttpServletResponse response, long startTime, Throwable failureCause) {

                   if (this.publishEvents) {

                            // Whether or not we succeeded, publish an event.

                            long processingTime = System.currentTimeMillis() - startTime;

                            int statusCode = (responseGetStatusAvailable ? response.getStatus() : -1);

                            this.webApplicationContext.publishEvent(

                                               new ServletRequestHandledEvent(this,

                                                                 request.getRequestURI(), request.getRemoteAddr(),

                                                                 request.getMethod(), getServletConfig().getServletName(),

                                                                 WebUtils.getSessionId(request), getUsernameForRequest(request),

                                                                 processingTime, failureCause, statusCode));

                   }

         }

 

当publishEvents设置为true时,就会发布这个消息,无论请求处理成功与否都会发布。

publishEvents可以在web.xml中配置spring mvc的servlet的时候进行配置,默认为true,我们可以监听这个事件来做一些事情,如记录日志。

 

public class ServletRequestHandledEventListener implements ApplicationListener<ServletRequestHandledEvent> {

         final static Logger logger = LoggerFactory.getLogger("RequestProcessLog");

         @Override

         public void onApplicationEvent(ServletRequestHandledEvent event) {

                   logger.info(event.getDescription());

 

         }

}

 

上面是一个简单的应用,只要简单的继承ApplicationListener,实现对应的方法,然后注册到spring中就可以使用了。

 

   到现在为止FrameworkServlet就分析完了,回顾一下:首先在service方法中添加了Patch的处理,并将所有需要自己处理的请求都集中到processRequest方法中统一处理,这和HttpServlet里面根据request的类型将请求分配到各个不同的方法里处理过程正好相反。

 

  然后就是processRequest方法,将主要的处理逻辑交给了doService,这是一个模板方法,在子类中具体实现;另外就是对当前request中获得的LocaleContext和RequestAttributes方法进行保存,以及处理完之后恢复,最后发布了ServletRequestHandledEvent事件。

 

 

DispatcherServlet

DispatcherServlet是Spring mvc中最核心的类,整个处理过程的顶层设计都在这里。

DispatcherServlet里面执行处理的入口方法是doService,不过doService没有直接处理,而是交给了doDispatch进行具体处理。在doDispatcher处理前doService做了一些事情,首先判断请求是不是include请求,如果是则对request的Attribute做快照备份,等doDispatch处理完成后进行还原,做完快照后对request设置一些属性。

org.springframework.web.servlet.DispatcherServlet.class

 

protected void doService(HttpServletRequest request, HttpServletResponse response) throws Exception {

                   if (logger.isDebugEnabled()) {

                            String resumed = WebAsyncUtils.getAsyncManager(request).hasConcurrentResult() ? " resumed" : "";

                            logger.debug("DispatcherServlet with name '" + getServletName() + "'" + resumed +

                                               " processing " + request.getMethod() + " request for [" + getRequestUri(request) + "]");

                   }

 

                  // Keep a snapshot of the request attributes in case of an include,

                   // to be able to restore the original attributes after the include.

                   //快照include之后还原

                   Map<String, Object> attributesSnapshot = null;

                   if (WebUtils.isIncludeRequest(request)) {

                            attributesSnapshot = new HashMap<String, Object>();

                            Enumeration<?> attrNames = request.getAttributeNames();

                            while (attrNames.hasMoreElements()) {

                                     String attrName = (String) attrNames.nextElement();

                                     if (this.cleanupAfterInclude || attrName.startsWith("org.springframework.web.servlet")) {

                                               attributesSnapshot.put(attrName, request.getAttribute(attrName));

                                     }

                            }

                   }

 

                   // Make framework objects available to handlers and view objects.

                   request.setAttribute(WEB_APPLICATION_CONTEXT_ATTRIBUTE, getWebApplicationContext());

                   request.setAttribute(LOCALE_RESOLVER_ATTRIBUTE, this.localeResolver);

                   request.setAttribute(THEME_RESOLVER_ATTRIBUTE, this.themeResolver);

                   request.setAttribute(THEME_SOURCE_ATTRIBUTE, getThemeSource());

 

                   FlashMap inputFlashMap = this.flashMapManager.retrieveAndUpdate(request, response);

                   if (inputFlashMap != null) {

                            request.setAttribute(INPUT_FLASH_MAP_ATTRIBUTE, Collections.unmodifiableMap(inputFlashMap));

                   }

                   request.setAttribute(OUTPUT_FLASH_MAP_ATTRIBUTE, new FlashMap());

                   request.setAttribute(FLASH_MAP_MANAGER_ATTRIBUTE, this.flashMapManager);

                   //交给dodispatch方法

                   try {

                            doDispatch(request, response);

                   }

                   finally {

                            if (!WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted()) {

                                     // Restore the original attribute snapshot, in case of an include.

                                     if (attributesSnapshot != null) {

                                               restoreAttributesAfterInclude(request, attributesSnapshot);

                                     }

                            }

                   }

         }

 

对request设置的属性中,webApplicationContext,lcaoleResolver,ThemeResolver和themeSource后面三个都和flashMap相关,主要用于Redirect转发时参数的传递。比如,为了避免重复提交表单,可以在处理完post请求后redirect到一个get请求,这样即使用户刷新也不会重复提交。这里有一个问题,提交订单时有参数,提交完成后redirect到一个显示订单的页面,显然在显示订单的时候还要知道一些参数。按普通的模式如果想要提交参数,就只能将其写入url中,但是url也有长度限制。另外一些场景中我们不想将参数暴露到url中,这时既可以用flashMap来进行传递参数。只需要将需要传递的参数写入OUTPUT_FLASH_MAP_ATTRIBUTE,例如

 

((FlashMap)((ServletRequestAttributes)(RequestContextHolder.getRequestAttributes())).getRequest().getAttribute(DispatcherServlet.OUTPUT_FLASH_MAP_ATTRIBUTE)).put("name", "张三丰");

 

 这样在redirect之后的handle中的spring 就会自动将其值设置到model里,spring还给我们提供了更加简单的操作方法,只需要在handler方法的参数中定义redirectAttributes类型的变量,然后将需要保存的属性设置到里面就行,之后的事情由spring自动完成.RedirectAttributes有两种设置参数的方法addAttribute(key,value)addFlashAttribute(key,value),用第一个方法设置的参数会拼接到url中,第二个设置的参数会保存在flashMap中.

@RequestMapping(value="/submit",method=RequestMethod.POST)

         public String submit(RedirectAttributes attr) throws IOException{

                   ((FlashMap) ((ServletRequestAttributes) (RequestContextHolder.getRequestAttributes())).getRequest()

                                     .getAttribute(DispatcherServlet.OUTPUT_FLASH_MAP_ATTRIBUTE)).put("name", "张三丰");

                   attr.addAttribute("orderId","one");

                   attr.addFlashAttribute("local","zh-cn");

                   return "redirect:showorders";

                  

         }

         @RequestMapping(value="/showorders",method=RequestMethod.GET)

         public String showOrders(Model model) throws IOException{

                   //dothing....

                   return "orders";

         }

这里分别用三种方法传输redirect参数

1.RequestContextHolder获得request,并从中拿到outputflashMao,将属性放进去;

2.RedirectAttributes中addFlashAttribute方法设置

3,通过RedirectAttributes中的addAttribute方法设置,这样设置参数不会保存到flashMao中,而是拼接到url中;

  还可以用requestContextUtils来操作:RequestContextUtils.getOutoutFlashNap(request),这样可以得到outputFlashMap,其内部还是从Request的属性中获得的。

这就是flashMap的用法,inputFlashMap用于保存上次请求中转发过来的属性,outputFlashMap用来保存本次请求需要转发的属性,FlashMapManager用于管理他们;

 

doService对request设置了一些属性,如果是include属性,会对request当前的属性进行快照备份,并在处理结束后恢复,最后将请求转发给doDispatch方法。

 

doDispatch方法非常简洁,从顶层设计了整个请求处理的过程,doDispatch只有四句核心的代码,他们的任务分别是:1.根据request找到Handler;2根据handler找到对应的HandlerAdaper;3.用HandlerAdaper处理Handler;4.调用processDispatchResult方法处理上面的结果(包含找到view并渲染输出给用户),

 

mappedHandler = getHandler(processedRequest);

HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());

mv = ha.handle(processedRequest, response, mappedHandler.getHandler());

processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);

 

上面有三个需要了解的地方:HandlerMapping,Handler,HandlerAdapter,下面具体说一下:

 

HandlerMapping:查找请求对应的Handler,有许多请求,每个请求都需要一个handler来处理,具体接收到请求后用哪个handler来处理呢,这就是handlerMapping要做的事情。

 

HandlerAdapter:从名字上看它是一个适配器,spring mvc中handler可以是任意类型,只要能处理请求就够了,但是servlet中的处理方法的结构却是固定的,都是以request和response作为参数的(例如doService方法),如何让固定格式的servlet处理方法调用灵活的handler来处理呢?这就是HandlerAdapter要做的事。

 

Handler是用来干活的工具,HandlerMapping是用于根据需要干的活找到对应的工具,HandlerAdapter是用工具干活的人。

 

view 和viewResolver的原理与handler和HandlerMapping 的原理类似。view是用来展现model的数据的,而viewResolver是用来查找view的。

 

   上面四句代码就是handlerMapping找到干活的handler,找到使用handler的HandlerAdapter,让HandlerAdapter使用handler干活,干完活后将结果写个报告(通过view展现)

 

doDospatch结构

org.springframework.web.servlet.DispatcherServlet.class

         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.

                                     //当前请求的处理器

                                     mappedHandler = getHandler(processedRequest);

                                     if (mappedHandler == null || mappedHandler.getHandler() == null) {

                                               noHandlerFound(processedRequest, response);

                                               return;

                                     }

 

                                     // Determine handler adapter for the current request.

//当前请求的处理适配器

                                     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: " + lastModified);

                                               }

                                               if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) {

                                                        return;

                                               }

                                     }

 

                                     if (!mappedHandler.applyPreHandle(processedRequest, response)) {

                                               return;

                                     }

 

                                     // Actually invoke the handler.

                                     //调用处理请求

                                     mv = ha.handle(processedRequest, response, mappedHandler.getHandler());

 

                                     //如果异步,直接返回

if (asyncManager.isConcurrentHandlingStarted()) {

                                               return;

                                     }

 

                                     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);

                                     }

                            }

                   }

         }

 

doDispatch大体分为两部分:处理请求和渲染页面:

 

HttpServletRequest processedRequest:实际处理时所用到的request,如果不是上传请求则直接使用接收到的request,否则封装为上传类型的request;

 

HandlerExceptionChain mappedHandler:处理请求时的处理器链(包含处理器和对应的Interceptor)

 

Boolean multipartRequestParsed:是不是上传请求的标识

 

ModelAndView mv:封装model和view的容器。此变量在整个springmvc中承担着非常重要的角色。

 

Exception dispatchException:处理请求过程中抛出的异常,它不包含渲染过程抛出的异常。

 

doDispatch中首先检查是不是上传请求,如果是上传请求,就将request转换为MultipartHttpServletrequest,并将mulitipartRequestParsed标识设置为true,其中也用到了multipartResolver.

 

  然后通过gethandler方法获得处理器链,其中使用到了handlerMapping,返回值类型为handlerExecutionChain类型,其中包含着与当前request相匹配的Interceptor和Handler,

 

protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {

                   for (HandlerMapping hm : this.handlerMappings) {

                            if (logger.isTraceEnabled()) {

                                     logger.trace(

                                                        "Testing handler map [" + hm + "] in DispatcherServlet with name '" + getServletName() + "'");

                            }

                            HandlerExecutionChain handler = hm.getHandler(request);

                            if (handler != null) {

                                     return handler;

                            }

                   }

                   return null;

         }

 

  接下来是处理get,head请求的last_modified,当浏览器第一次跟服务器请求资源(get,head请求)时,服务器在返回的请求头里会包含一个Last_modified的属性,代表本资源最后是什么时候修改的,在浏览器以后发送请求的时候会同时发送之前接收到的last_modifed,服务器接收到带last_modified的请求后会用其值和自己的实际资源的最后修改时间做对比,如果资源过期了则返回新的资源(同时返回新的last-modified),否则直接返回304状态码表示资源未过期,浏览器也直接使用之前缓存的结果。

 

接下来调用相应Interceptor的preHandle.

 

处理完Interceptor的preHandle后就到了处理请求的关键地方-让handleAdapter使用handler处理请求,Controller就是在这个地方执行的,这里主要使用了handlerAdapter.

 

handler处理完成后,如果需要异步请求,则直接返回,如果不需要,当view为空时,设置为默认view,然后执行相应的Interceptor的postHandle,设置默认view的过程中使用到了ViewNameTranslator..

 

到这里请求的内容完成了,接下来使用processDispatchResult方法处理前面返回的结果,其中包含处理异常,内部异常处理和本身抛出的异常。

 

         private void processDispatchResult(HttpServletRequest request, HttpServletResponse response,

                            HandlerExecutionChain mappedHandler, ModelAndView mv, Exception exception) throws Exception {

 

                   boolean errorView = false;

 

                   if (exception != null) {

                            if (exception instanceof ModelAndViewDefiningException) {

                                     logger.debug("ModelAndViewDefiningException encountered", exception);

                                     mv = ((ModelAndViewDefiningException) exception).getModelAndView();

                            }

                            else {

                                     Object handler = (mappedHandler != null ? mappedHandler.getHandler() : null);

                                     mv = processHandlerException(request, response, handler, exception);

                                     errorView = (mv != null);

                            }

                   }

 

                   // Did the handler return a view to render?

                   if (mv != null && !mv.wasCleared()) {

                            render(mv, request, response);

                            if (errorView) {

                                     WebUtils.clearErrorRequestAttributes(request);

                            }

                   }

                   else {

                            if (logger.isDebugEnabled()) {

                                     logger.debug("Null ModelAndView returned to DispatcherServlet with name '" + getServletName() +

                                                        "': assuming HandlerAdapter completed request handling");

                            }

                   }

 

                   if (WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted()) {

                            // Concurrent handling started during a forward

                            return;

                   }

 

                   if (mappedHandler != null) {

                            mappedHandler.triggerAfterCompletion(request, response, null);

                   }

         }

 

如下是doDispatch的流程图:

205349_Ybjy_1866807.png

 

总结

 三个Servlet处理中的大致功能:

HttpServletBean:没有参与实践请求的处理;

FrameworkServlet:将不同类型的请求合并到了processRequest方法统一处理

 processRequest做了三件事:

  调用了doService模板方法具体处理请求;

 

  将当前请求的LocaleContext和ServletRequestAttributes在处理请求前设置到LocaleContextHolder和RequestContextHolder,并在请求处理完成后恢复;

 请求处理完成后发布ServletRequestHandledEvent消息。

 

DispatcherServlet:doSerivce方法给request设置了一些属性,并将请求交给了doDispatch方法具体处理。

DispatcherServlet中的doDispatch方法完成了Spring mvc中请求处理过程的顶层设计,使用了DIspatcherSerlvet中的九大组件完成了具体的请求处理。

 

转载于:https://my.oschina.net/iioschina/blog/774695

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值