上一篇对SpringMVC的处理器映射器和适配器根据实例做了一个整体的介绍,也略微涉及到前端控制器的用法,这一篇将对前端控制器的流程以及使用做一个较为详细的介绍。
第一种:*.action,访问以.action结尾 由DispatcherServlet进行解析
第二种:/,所以访问的地址都由DispatcherServlet进行解析,对于静态文件的解析需要配置不让DispatcherServlet进行解析,使用此种方式可以实现RESTful风格的url
在平常开发中使用第一种配置方法较多。
DispatcherServlet解析请求的过程图
①:DispatcherServlet是springmvc中的前端控制器,负责接收request并将request转发给对应的处理组件。
②:HanlerMapping是springmvc中完成url到controller映射的组件。DispatcherServlet接收request,然后从HandlerMapping查找处理request的controller。
③:Cntroller处理request,并返回ModelAndView对象,Controller是springmvc中负责处理request的组件(类似于struts2中的Action),ModelAndView是封装结果视图的组件。
④ ⑤ ⑥:视图解析器解析ModelAndView对象并返回对应的视图给客户端
总的来说,前端控制器用来维护url和controller的映射,具体做法是对标记@Controller类中标识有@RequestMapping的方法进行映射。
在@RequestMapping里边定义映射的url。
DispatcherServlet 是web服务器的入口,它继承自抽象类FrameworkServlet,也就是间接继承了HttpServlet。我们大家都知道,Servlet的生命周期是:初始化阶段——响应客户请求阶段——销毁。
DispatcherServlet也是一种Servlet。所以,它的生命周期也分为这三个阶段,借助DispatcherServlet的生命周期的源码,我们可以对它有一个更好的理解。
初始化阶段
DispatcherServlet继承FrameworkServlet类,使用initStrategies()方法初始化 。
protected void initStrategies(ApplicationContext context) { initMultipartResolver(context); //文件上传解析 initLocaleResolver(context); //本地化解析 initThemeResolver(context); //主题解析 initHandlerMappings(context); //通过HandlerMapping,将请求映射到处理器 initHandlerAdapters(context); //通过HandlerAdapter支持多种类型的处理器 initHandlerExceptionResolvers(context); //如果执行过程中遇到异常将交给HandlerExceptionResolver来解析 initRequestToViewNameTranslator(context); //直接解析请求到视图名 initViewResolvers(context); //通过ViewResolver解析逻辑视图名到具体视图实现 initFlashMapManager(context); //flash映射管理器 }
响应客户请求阶段
/** * Exposes the DispatcherServlet-specific request attributes and delegates to {@link #doDispatch} * for the actual dispatching. */@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); } } }
其中, doDispatch(request, response); 实现请求的分发,流程是:
1. 把请求分发到handler(按照配置顺序获取servlet的映射关系获取handler);
2. 根据servlet已安装的 HandlerAdapters 去查询第一个能处理的handler;
3. handler激发处理请求
doDispatch() 方法中的几个重要方法
getHandler(processedRequest)
获取类HandlerExecutionChain ,存放这个 url 请求的各种信息的类(bean , method ,参数 , 拦截器 ,beanFactory 等等)
getHandlerAdapter(mappedHandler.getHandler())
获取请求处理类 handlerAdapter ( 通过handlerAdapter类的 supports() 方法判断)
ha.handle(processedRequest, response, mappedHandler.getHandler())
返回 ModelAndView 视图(以及response)
processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException)
结果处理方法以及异常处理
销毁阶段
public void destroy() { getServletContext().log("Destroying Spring FrameworkServlet '" + getServletName() + "'"); // Only call close() on WebApplicationContext if locally managed... if (this.webApplicationContext instanceof ConfigurableApplicationContext && !this.webApplicationContextInjected) { ((ConfigurableApplicationContext) this.webApplicationContext).close(); } }
最后,展示基于注解的完整的springMVC配置文 件
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:context="http://www.springframework.org/schema/context" xmlns:mvc="http://www.springframework.org/schema/mvc" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.0.xsd http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-4.0.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd"> <context:component-scan base-package="com.amuxia.controller"> </context:component-scan> <mvc:annotation-driven></mvc:annotation-driven> <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver"> <!-- 配置jsp路径的前缀 --> <property name="prefix" value="/WEB-INF/jsp/"/> <!-- 配置jsp路径的后缀 --> <property name="suffix" value=".jsp"/> </bean></beans>
前端控制器的基本工作流程是:一个http请求到达服务器,被DispatcherServlet接收,DispatcherServlet将请求委派给合适的处理器Controller。
此时处理控制权到达Controller对象。Controller内部完成请求的数据模型的创建和业务逻辑的处理,然后再将填充了数据后的模型(model)和控制权一并交还给DispatcherServlet,委派DispatcherServlet来渲染响应。
DispatcherServlet再将这些数据和适当的数据模版视图结合,向Response输出响应。
Java知音公众号后续将会系统的整理发布一系列关于ssm框架的知识点,由浅入深。结合小例子来感受ssm框架给我们开发带来的便利,如果您有好的Demo或者文章,欢迎投稿!