上一篇对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或者文章,欢迎投稿!