处理器
(1)非注解的处理器映射器和适配器
处理器映射器
第一种非注解的映射器
<bean class="org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping"/>
另一种非注解的映射器
<bean class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">
具体配置如下
<!-- 配置Handler -->
<bean name="/queryItems.action"
class="com.amuxia.controller.ItemsController" />
<!-- 配置另外一个Handler -->
<!--
处理器映射器 将bean的name作为url进行查找,
需要在配置Handler时指定beanname(就是url)
所有的映射器都实现 HandlerMapping接口。
-->
<bean class="org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping" />
也可以多个多个映射器并存,由DispatcherServlet(前端控制器)指定URL被哪个映射器处理,如下:
<!-- 配置Handler -->
<bean id="itemsController" name="/queryItems.action"
class="com.amuxia.ItemsController"/>
<!--
处理器映射器 将bean的name作为url进行查找,
需要在配置Handler时指定URL
-->
<bean class="org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping"/>
<!-- 简单url映射-->
<bean class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">
<property name="mappings">
<props>
<!-- 对 itemsController进行url映射-->
<prop key="/queryItems1.action">items1</prop>
<prop key="/queryItems2.action">items2</prop>
</props>
</property>
</bean>
处理器适配器
第一种非注解的适配器
<bean class="org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter" />
(要求编写的Handler实现Controller接口)
public class ItemsController implements Controller{
public ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response)
throws Exception {
ModelAndView modelAndView = new ModelAndView();
modelAndView.setViewName("view/list");
modelAndView.addObject("name", "张三");
return modelAndView;
}
}
另一种非注解的适配器
<bean class="org.springframework.web.servlet.mvc.HttpRequestHandlerAdapter"/>
(要求编写的Handler实现HttpRequestHandler接口)
public class ItemsController implements HttpRequestHandler{
public void handleRequest(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse)
throws ServletException, IOException {
List<Items> itemsList = new ArrayList<Items>();
Items items = new Items();
items.setName("阿木侠");
itemsList.add(items);
httpServletRequest.setAttribute("list",itemsList);
httpServletRequest.getRequestDispatcher("view/list").forward(httpServletRequest,httpServletResponse);
}
}
这里可以使用response设置响应格式,字符编码等
response.setCharacterEncoding("utf-8");
response.setContentType("application/json;charset=utf-8");
(2)基于注解的处理器映射器和适配器
注解映射器
<bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping"/>
注解适配器
<bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter"/>
注解的映射器和注解的适配器必须配对使用,spring对其有一个更简化的配置,可以使用下面的这段代码代替上面的两段配置
<mvc:annotation-driven></mvc:annotation-driven>
使用注解的映射器和注解的适配器,在具体的Java代码实现上有很大的不同,不再需要实现特定的接口,代码风格更加简化。
@Controllerpublic class ItemsController{
@RequestMapping("/items")
public ModelAndView items() throws Exception{
List<Items> itemsList = new ArrayList<Items>();
Items items = new Items();
items.setName("阿木侠");
itemsList.add(items);
ModelAndView modelAndView = new ModelAndView();
modelAndView.addObject("list",itemsList);
modelAndView.setViewName("view/list"); return modelAndView;
}
}
这里使用到了两个注解,也是最最基本的两个注解
@Controller修饰类,用来标识它是一个控制器。
@RequestMapping("")修饰方法或者类,这里表示实现对items方法和url进行映射,一个方法对应一个url。注意这里@RequestMapping("")中的名称一般和方法同名,但不是必须。
最后,还需要在spring容器中加载Handler,指定扫描controller。
<bean class="com.amuxia.controller.ItemsController"></bean>
这里需要对用到的所有的控制器类都需要在spring容器中加载Handler,但我们在实际开发中,所有的Controller一般都会放在某个包下,所以可以使用扫描组件扫描Controller包文件
<context:component-scan base-package="com.amuxia.controller"></context:component-scan>
扩展
注解的处理器映射器和适配器在spring3.1之前和之后使用略有不同,现在一般使用spring3.1之后的,但对之前的需要有一个大概的认识,避免在旧项目中见到之后一脸懵逼。
在spring3.1之前使用(注解映射器)
org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping
在spring3.1之后使用(注解映射器)
org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping
在spring3.1之前使用(注解适配器)
org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter
在spring3.1之后使用(注解适配器)
org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter
前端控制器
配置
第一种:*.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作用分析
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输出响应。