主要参与建立工做,并无涉及到请求的处理。web
servlet的处理过程:首先是从servlet接口的service方法开始,而后在HttpServlet的service方法中根据请求的类型不一样将请求路由到doget,doHead,doPost,doPut,doDelete,doOptions和doTrace七个方法。而且作了doHead,doOptions,doTrace的默认实现,其中doHead调用doGet,而后返回只有header没有body的response.spring
在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 {session
String method = request.getMethod();mvc
if (method.equalsIgnoreCase(RequestMethod.PATCH.name())) {app
processRequest(request, response);异步
}async
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(),这时就会出现问题了。
processRequest是FrameworkServlet类在处理请求中最核心的方法
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方法将现有属性设置到LocaleContextHolder和RequestContextHolder中(处理完成后再恢复到原来的值),
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是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展示)
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的流程图:
三个Servlet处理中的大体功能:
HttpServletBean:没有参与实践请求的处理;
FrameworkServlet:将不一样类型的请求合并到了processRequest方法统一处理
processRequest作了三件事:
调用了doService模板方法具体处理请求;
将当前请求的LocaleContext和ServletRequestAttributes在处理请求前设置到LocaleContextHolder和RequestContextHolder,并在请求处理完成后恢复;
请求处理完成后发布ServletRequestHandledEvent消息。
DispatcherServlet:doSerivce方法给request设置了一些属性,并将请求交给了doDispatch方法具体处理。
DispatcherServlet中的doDispatch方法完成了Spring mvc中请求处理过程的顶层设计,使用了DIspatcherSerlvet中的九大组件完成了具体的请求处理。