spring mvc DispatcherServlet详解之一---处理请求深入解析
要深入理解spring mvc的工作流程,就需要先了解spring mvc的架構(gòu):
從上圖可以看到 前端控制器DispatcherServlet在其中起著主導(dǎo)作用,理解了DispatcherServlet 就完全可以說弄清楚了spring mvc。
為了加深對spring mvc的整個工作流程的理解,本文從分析DispatcherServlet的工作過程來一窺spring mvc的整個面貌。
1. 初始化
protected void initStrategies(ApplicationContext context) {
initMultipartResolver(context); //文件上傳解析,如果請求類型是multipart將通過MultipartResolver進(jìn)行文件上傳解析;
initLocaleResolver(context); //本地化解析
initThemeResolver(context); //主題解析
initHandlerMappings(context); //通過HandlerMapping,將請求映射到處理器
initHandlerAdapters(context); //通過HandlerAdapter支持多種類型的處理器
initHandlerExceptionResolvers(context); //如果執(zhí)行過程中遇到異常將交給HandlerExceptionResolver來解析
initRequestToViewNameTranslator(context); //直接解析請求到視圖名
initViewResolvers(context); //通過ViewResolver解析邏輯視圖名到具體視圖實(shí)現(xiàn)
initFlashMapManager(context); //flash映射管理器
}
initMultipartResolver,initLocaleResolver,initThemeResolver,initRequestToViewNameTranslator,initFlashMapManager 這五個初始化方法流程相同,都是使用
context.getBean(String name, Class<FlashMapManager> requiredType)的方式獲取到相應(yīng)的Resolver。以initMultipartResolver為例,見如下: /*** Initialize the MultipartResolver used by this class.* <p>If no bean is defined with the given name in the BeanFactory for this namespace,* no multipart handling is provided.*/private void initMultipartResolver(ApplicationContext context) {try {this.multipartResolver = context.getBean(MULTIPART_RESOLVER_BEAN_NAME, MultipartResolver.class);if (logger.isDebugEnabled()) {logger.debug("Using MultipartResolver [" + this.multipartResolver + "]");}}catch (NoSuchBeanDefinitionException ex) {// Default is no multipart resolver.this.multipartResolver = null;if (logger.isDebugEnabled()) {logger.debug("Unable to locate MultipartResolver with name '" + MULTIPART_RESOLVER_BEAN_NAME +"': no multipart request handling provided");}}}
多個resolver
initHandlerMappings,initHandlerAdapters,initHandlerExceptionResolvers,initViewResolvers 獲取方式相同,使用:BeanFactoryUtils.beansOfTypeIncludingAncestors(ListableBeanFactory lbf, Class<HandlerMapping> type, boolean includeNonSingletons, boolean allowEagerInit) 的方式獲取到相應(yīng)的Resolver。以initHandlerMappings為例,見如下: /*** Initialize the HandlerMappings used by this class.* <p>If no HandlerMapping beans are defined in the BeanFactory for this namespace,* we default to BeanNameUrlHandlerMapping.*/private void initHandlerMappings(ApplicationContext context) {this.handlerMappings = null;if (this.detectAllHandlerMappings) {// Find all HandlerMappings in the ApplicationContext, including ancestor contexts.Map<String, HandlerMapping> matchingBeans =BeanFactoryUtils.beansOfTypeIncludingAncestors(context, HandlerMapping.class, true, false);if (!matchingBeans.isEmpty()) {this.handlerMappings = new ArrayList<HandlerMapping>(matchingBeans.values());// We keep HandlerMappings in sorted order.OrderComparator.sort(this.handlerMappings);}}else {try {HandlerMapping hm = context.getBean(HANDLER_MAPPING_BEAN_NAME, HandlerMapping.class);this.handlerMappings = Collections.singletonList(hm);}catch (NoSuchBeanDefinitionException ex) {// Ignore, we'll add a default HandlerMapping later. }}// Ensure we have at least one HandlerMapping, by registering// a default HandlerMapping if no other mappings are found.if (this.handlerMappings == null) {this.handlerMappings = getDefaultStrategies(context, HandlerMapping.class);if (logger.isDebugEnabled()) {logger.debug("No HandlerMappings found in servlet '" + getServletName() + "': using default");}}}
那么深入看一下BeanFactoryUtils.beansOfTypeIncludingAncestors 究竟做了什么?返回指定類型和子類型的所有bean,若該bean factory 是一個繼承類型的beanFactory,這個方法也會獲取祖宗factory中定義的指定類型的bean。
/*** Return all beans of the given type or subtypes, also picking up beans defined in* ancestor bean factories if the current bean factory is a HierarchicalBeanFactory.* The returned Map will only contain beans of this type.* <p>Does consider objects created by FactoryBeans if the "allowEagerInit" flag is set,* which means that FactoryBeans will get initialized. If the object created by the* FactoryBean doesn't match, the raw FactoryBean itself will be matched against the* type. If "allowEagerInit" is not set, only raw FactoryBeans will be checked* (which doesn't require initialization of each FactoryBean).* <p><b>Note: Beans of the same name will take precedence at the 'lowest' factory level,* i.e. such beans will be returned from the lowest factory that they are being found in,* hiding corresponding beans in ancestor factories.</b> This feature allows for* 'replacing' beans by explicitly choosing the same bean name in a child factory;* the bean in the ancestor factory won't be visible then, not even for by-type lookups.* @param lbf the bean factory* @param type type of bean to match* @param includeNonSingletons whether to include prototype or scoped beans too* or just singletons (also applies to FactoryBeans)* @param allowEagerInit whether to initialize <i>lazy-init singletons</i> and* <i>objects created by FactoryBeans</i> (or by factory methods with a* "factory-bean" reference) for the type check. Note that FactoryBeans need to be* eagerly initialized to determine their type: So be aware that passing in "true"* for this flag will initialize FactoryBeans and "factory-bean" references.* @return the Map of matching bean instances, or an empty Map if none* @throws BeansException if a bean could not be created*/public static <T> Map<String, T> beansOfTypeIncludingAncestors(ListableBeanFactory lbf, Class<T> type, boolean includeNonSingletons, boolean allowEagerInit)throws BeansException {Assert.notNull(lbf, "ListableBeanFactory must not be null");Map<String, T> result = new LinkedHashMap<String, T>(4);result.putAll(lbf.getBeansOfType(type, includeNonSingletons, allowEagerInit));if (lbf instanceof HierarchicalBeanFactory) {HierarchicalBeanFactory hbf = (HierarchicalBeanFactory) lbf;if (hbf.getParentBeanFactory() instanceof ListableBeanFactory) {Map<String, T> parentResult = beansOfTypeIncludingAncestors((ListableBeanFactory) hbf.getParentBeanFactory(), type, includeNonSingletons, allowEagerInit);for (Map.Entry<String, T> entry : parentResult.entrySet()) {String beanName = entry.getKey();if (!result.containsKey(beanName) && !hbf.containsLocalBean(beanName)) {result.put(beanName, entry.getValue());}}}}return result;}?2. 提供服務(wù)
? ? ?我們來看看這個servlet是如何提供服務(wù)的?
@Overrideprotected 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.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);try {doDispatch(request, response);}finally {if (WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted()) {return;}// Restore the original attribute snapshot, in case of an include.if (attributesSnapshot != null) {restoreAttributesAfterInclude(request, attributesSnapshot);}}}? ?從上面我們可以看到,提供服務(wù)只要分4步:
? 1. 保存現(xiàn)場。保存request 熟悉的快照,以便能在必要時恢復(fù)。
? 2. 將框架需要的對象放入request中,以便view和handler使用。
? 3. 請求分發(fā)服務(wù).
? 4. 恢復(fù)現(xiàn)場。
其中最重要的是請求分發(fā)服務(wù):
/*** 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.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;}try {// Actually invoke the handler.mv = ha.handle(processedRequest, response, mappedHandler.getHandler());}finally {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 mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);return;}// Clean up any resources used by a multipart request.if (multipartRequestParsed) {cleanupMultipart(processedRequest);}}}分發(fā)過程如下:
1. 判斷是否設(shè)置了multipart resolver,設(shè)置的話轉(zhuǎn)換為multipart request,沒有的話則繼續(xù)下面的步驟。
2. 根據(jù)當(dāng)前request,獲取hangdler。
3. 根據(jù)當(dāng)前request,獲取HandlerAdapter。
4. 如果支持http請求頭,處理?last-modified header請求頭。
5. 應(yīng)用已注冊interceptor的preHandle方法
6.?HandlerAdapter處理請求。
7. 設(shè)置默認(rèn)視圖。
8.?應(yīng)用已注冊interceptor的postHandle方法。
9. 處理異常或者視圖渲染。
小結(jié):
? ?DispatherServlet整個過程的細(xì)節(jié)一章之內(nèi)很難描述的面面俱到,只能分析部分流程,想了解更具體的實(shí)現(xiàn)需要從源代碼中去尋找。
?
轉(zhuǎn)載于:https://www.cnblogs.com/davidwang456/p/4096530.html
總結(jié)
以上是生活随笔為你收集整理的spring mvc DispatcherServlet详解之一---处理请求深入解析的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Java对存储过程的调用方法 --转载
- 下一篇: mysql中数据库database、实例