一、Spring MVC 功能
Spring MVC 本质上还是在使用 Servlet 处理,并在其基础上进行了封装简化了开发流程。 提高易用性、并使用程序逻辑结构变得更清晰。
基于注解的URL映射。 表单参数映射。 缓存处理。 全局统一异常处理。 拦截器的实现。 下载处理。
1. Servlet 处理流程
请求Servlet。 处理业务逻辑。 设置业务Model。 Forward Jsp Servlet。 Jsp Servlet 解析封装 html,并返回。
2. MVC 处理流程
URL映射。 表单参数映射。 调用目标Controller。 数据模型映射。 视图解析。 异常处理。
二、Spring MVC 开始
1. POM
< dependency>
< groupId> javax.servlet</ groupId>
< artifactId> javax.servlet-api</ artifactId>
< version> 3.1.0</ version>
</ dependency>
< dependency>
< groupId> org.springframework</ groupId>
< artifactId> spring-webmvc</ artifactId>
< version> 4.3.8.RELEASE</ version>
</ dependency>
2. web.xml
< servlet>
< servlet-name> dispatcherServlet</ servlet-name>
< servlet-class> org.springframework.web.servlet.DispatcherServlet</ servlet-class>
< init-param>
< param-name> contextConfigLocation</ param-name>
< param-value>
classpath:/spring-mvc.xml
</ param-value>
</ init-param>
</ servlet>
< servlet-mapping>
< servlet-name> dispatcherServlet</ servlet-name>
< url-pattern> /</ url-pattern>
</ servlet-mapping>
3. Controller
public class SimpleController implements Controller {
@Override
public ModelAndView handleRequest ( HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) throws Exception {
ModelAndView modelAndView = new ModelAndView ( "/WEB-INF/page/user_view.jsp" ) ;
modelAndView. addObject ( "name" , "骑士" ) ;
return modelAndView;
}
}
4. user_view.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8" %>
<! DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd" >
< html>
< head>
< meta http-equiv = " Content-Type" content = " text/html; charset=UTF-8" >
< title> 用户页面</ title>
</ head>
< body>
登录人:${name}
</ body>
</ html>
5. spring-mvc.xml
< bean name = " /hello.do" class = " com.qs.springmvcfast.controller.SimpleController" />
三、Spring MVC 组件
MVC 组件
1. HandlerMapping—处理映射
其为 MVC 中 URI路径 与 Controller对象 的映射。 DispatcherServlet 就是基于 此组件 来寻找对应的 Controller。 如果找不到就会报 No mapping found for HTTP request with URI
的异常。
HandlerMapping 作用是通过 URI 找到对应的 Handler。 但其 HandlerMapping.getHandler() 方法并不会直接返回 Handler对像,而是返回 HandlerExecutionChain对象 ,再通过 HandlerExecutionChain.getHandler() 返回最终的 Handler。
HandlerMapping 接口结构
SimpleUrlHandlerMapping:基于 手动配置URI 与 Controller 映射。 BeanNameUrlHandlerMapping:基于 IOC beanName 中 "/"开头的Bean 进行注册至映射。 RequestMappingHandlerMapping:基于 @RequestMapping注解 配置对应映射。
1.1 SimpleUrlHandlerMapping—简单
< bean name = " simpleController" class = " com.qs.springmvcfast.controller.SimpleController" />
< bean name = " simpleController2" class = " com.qs.springmvcfast.controller.SimpleController" />
< bean class = " org.springframework.web.servlet.handler.SimpleUrlHandlerMapping" >
< property name = " urlMap" >
< props>
< prop key = " /hello1.do" >
simpleController
</ prop>
< prop key = " /hello2.do" >
simpleController2
</ prop>
</ props>
</ property>
</ bean>
SimpleUrlHandlerMapping 接口结构
SimpleUrlHandlerMapping 初始化流程。
org.springframework.web.servlet.handler.SimpleUrlHandlerMapping#setUrlMap org.springframework.web.servlet.handler.SimpleUrlHandlerMapping#initApplicationContext org.springframework.web.servlet.handler.SimpleUrlHandlerMapping#registerHandlers org.springframework.web.servlet.handler.AbstractUrlHandlerMapping#registerHandler()
/
:表示根Handler。/*
:表示默认Handler。
org.springframework.web.servlet.DispatcherServlet#doService org.springframework.web.servlet.DispatcherServlet#doDispatch org.springframework.web.servlet.DispatcherServlet#getHandler org.springframework.web.servlet.handler.AbstractHandlerMapping#getHandler org.springframework.web.servlet.handler.AbstractUrlHandlerMapping#getHandlerInternal org.springframework.web.util.UrlPathHelper#getPathWithinApplication 获取 URI 路径。 org.springframework.web.servlet.handler.AbstractUrlHandlerMapping#lookupHandler 查找 Handler。 org.springframework.web.servlet.handler.AbstractHandlerMapping#getHandlerExecutionChain 封装执行链。
1.2 BeanNameUrlHandlerMapping—名称
BeanNameUrlHandlerMapping 实现上与 SimpleUrlHandlerMapping 一至,唯一区别在于 继承自 AbstractDetectingUrlHandlerMapping,通过对应 detectHandlers 可以在无配置的情况下发现 URI 与Handler 映射。
BeanNameUrlHandlerMapping 接口结构
< bean name = " /hello.do" class = " com.qs.springmvcfast.controller.SimpleController" />
< bean name = " /hello2.do" class = " com.qs.springmvcfast.controller.SimpleRequestHandler" />
< bean id = " /hello3.do" class = " com.qs.springmvcfast.controller.SimpleServlet" />
< bean class = " org.springframework.web.servlet.handler.SimpleServletHandlerAdapter" />
< context: component-scan base-package = " com.qs.springmvcfast.controller" />
< mvc: annotation-driven/>
1.3 RequestMappingHandlerMapping—注解
2. Handler—类型
在 AbstractUrlHandlerMapping类 可以看到存储 Handler的Map 值类型是 Object。 意味着所有的类都可以做来 Handler 来使用。
Controller 接口。 HttpRequestHandler 接口。 HttpServlet 接口。 @RequestMapping 方法注解。 可以看出 Handler 没有统一的接口,当 DispatchServlet 获取当对应的 Handler 之后如何调用呢?调用其哪个方法?这里有两种解决办法。
一是用 instanceof 判断 Handler 类型然后调用相关方法。 二是通过 引入适配器 实现,每个 适配器 实现对指定 Handler 的调用。Spring 采用后者 。
3. HandlerAdapter—处理适配器
Spring MVC 采用 适配器模式 来适配调用指定 Handler。 根据 Handler 的不同种类,采用不同的 Adapter。
HandlerAdapter 接口方法。
HandlerAdapter 接口结构
3.1 Handler 与 HandlerAdapter 对应关系
Handler类别 对应适配器 说明 Controller 接口 SimpleControllerHandlerAdapter 标准控制器
,返回 ModelAndViewHttpServlet 接口 SimpleServletHandlerAdapter 基于标准的 Servlet 处理 HttpRequestHandler HttpRequestHandlerAdapter 业务自行处理请求,不需要通过 ModelAndView 转到视图 HandlerMethod RequestMappingHandlerAdapter 基于 @RequestMapping
对应方法处理
3.2 SimpleServletHandlerAdapter 标准控制器
< bean id = " /hello3.do" class = " com.qs.springmvcfast.controller.SimpleServlet" />
< bean class = " org.springframework.web.servlet.handler.SimpleServletHandlerAdapter" />
public class SimpleServlet extends HttpServlet {
@Override
protected void doGet ( HttpServletRequest req, HttpServletResponse resp) throws ServletException , IOException {
super . doGet ( req, resp) ;
resp. getWriter ( ) . println ( "Hello SimpleServlet!" ) ;
}
}
当 IOC 实例化这些类之后,DispatcherServlet 就会通过 org.springframework.web.servlet.DispatcherServlet#getHandlerAdapter() 方法查找对应 Handler适配器,如果找不到就会报异常 No adapter for handler
。
4. ViewResolver—视图解析器
找到应的 Adapter 之后,就会基于 适配器 调用业务处理。 处理完业务方法之后,会返回一个 ModelAndView ,再去查找对应的视图进行处理。 其在 org.springframework.web.servlet.DispatcherServlet#resolveViewName() 中遍历 viewResolvers 列表查找,如果找不到就会报一个异常 Could not resolve view with name
。
4.1 BeanNameViewResolver
< bean name = " simpleView" class = " com.qs.springmvcfast.view.SimpleView" />
< bean class = " org.springframework.web.servlet.view.BeanNameViewResolver" />
public class SimpleView implements View {
@Override
public String getContentType ( ) {
return null ;
}
@Override
public void render ( Map < String , ? > map, HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) throws Exception {
httpServletResponse. getWriter ( ) . print ( "Hello View!" ) ;
}
}
5. View—视图
基于 ViewResolver.resolveViewName() 获取对应 View 来解析生成 html 并返回。
View 接口结构
< bean class = " org.springframework.web.servlet.view.InternalResourceViewResolver" >
< property name = " prefix" value = " /WEB-INF/page/" />
< property name = " suffix" value = " .jsp" />
< property name = " viewClass" value = " org.springframework.web.servlet.view.InternalResourceView" />
</ bean>
< bean class = " org.springframework.web.servlet.view.InternalResourceViewResolver" >
< property name = " prefix" value = " /WEB-INF/page/" />
< property name = " suffix" value = " .jsp" />
< property name = " viewClass" value = " org.springframework.web.servlet.view.JstlView" />
</ bean>
public class SimpleController implements Controller {
@Override
public ModelAndView handleRequest ( HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) throws Exception {
ModelAndView modelAndView = new ModelAndView ( "myView" ) ;
modelAndView. addObject ( "name" , "骑士" ) ;
return modelAndView;
}
}
6. HandlerExceptionResolver—异常处理器
DispatcherServlet 会调用 org.springframework.web.servlet.DispatcherServlet#processHandlerException() 方法,遍历 handlerExceptionResolvers 处理异常,处理完成之后返回 errorView 跳转到异常视图。
HandlerExceptionResolver 接口结构
DefaultHandlerExceptionResolver:【默认】Spring mvc 默认异常处理。 ResponseStatusExceptionResolver:【默认】用于解析带 @ResponseStatus 的自定义异常。 SimpleMappingExceptionResolver:异常映射,将 指定异常 与 错误页面 相对应。
6.1 DefaultHandlerExceptionResolver—默认
< bean class = " com.qs.springmvcfast.exception.SimpleHandlerExceptionResolver" />
< bean class = " org.springframework.web.servlet.mvc.annotation.ResponseStatusExceptionResolver" />
public class SimpleHandlerExceptionResolver implements HandlerExceptionResolver {
@Override
public ModelAndView resolveException ( HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, Exception e) {
return new ModelAndView ( "error" ) ;
}
}
6.2 SimpleMappingExceptionResolver—简单
< bean class = " org.springframework.web.servlet.handler.SimpleMappingExceptionResolver" >
< property name = " defaultErrorView" value = " error" />
< property name = " defaultStatusCode" value = " 500" />
< property name = " exceptionMappings" >
< map>
< entry key = " java.lang.RuntimeException" value = " error" />
< entry key = " java.lang.IllegalArgumentException" value = " argumentError" />
</ map>
</ property>
</ bean>
7. HandlerInterceptor—拦截器
HandlerInterceptor 用于对请求拦截,与 原生Filter 区别在于 Filter 只能在业务执行前拦截,而 HandlerInterceptor 可以在业务 处理 前、中、后 进行处理。 实现机制是基于 HandlerExecutionChain 分别在 doDispatch 方法中执行以下方法。
preHandle:业务处理前执行。 postHandle:业务处理后(异常则不执行)。 afterCompletion:视图处理后。
逻辑源码 org.springframework.web.servlet.DispatcherServlet#doDispatch 方法。
< bean name = " simpleHandlerInterceptor" class = " com.qs.springmvcfast.interceptor.SimpleHandlerInterceptor" />
public class SimpleHandlerInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle ( HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o) throws Exception {
System . out. printf ( "`%s`.preHandle" , this . getClass ( ) . getName ( ) ) . println ( ) ;
return true ;
}
@Override
public void postHandle ( HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, ModelAndView modelAndView) throws Exception {
System . out. printf ( "`%s`.postHandle" , this . getClass ( ) . getName ( ) ) . println ( ) ;
}
@Override
public void afterCompletion ( HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, Exception e) throws Exception {
System . out. printf ( "`%s`.afterCompletion" , this . getClass ( ) . getName ( ) ) . println ( ) ;
}
}
四、Spring MVC 执行流程
五、DispatchServlet 初始化流程
1. 创建 WebApplicationContext
org.springframework.web.servlet.HttpServletBean#init org.springframework.web.servlet.FrameworkServlet#initServletBean org.springframework.web.servlet.FrameworkServlet#initWebApplicationContext org.springframework.web.servlet.FrameworkServlet#createWebApplicationContext(org.springframework.context.ApplicationContext) 基于当前存在的 Spring 上下文做为 Root,创建 MVC 上下文。 org.springframework.web.servlet.FrameworkServlet#configureAndRefreshWebApplicationContext org.springframework.context.support.AbstractApplicationContext#refresh
2. 基于策略模型加载各组件
六、RequestMapping 注解
< context: component-scan base-package = " com.qs.springmvcfast.controller" />
< mvc: annotation-driven/>
@RequestMapping ( "/hello.do" )
public ModelAndView hello ( String name) {
System . out. printf ( "`%s`.hello" , this . getClass ( ) . getName ( ) ) . println ( ) ;
ModelAndView modelAndView = new ModelAndView ( "user_view" ) ;
modelAndView. addObject ( "name" , "骑士" ) ;
return modelAndView;
}
为什么基于 <mvc:annotation-driven/>
配置就能实现 MVC 的整个配置了,之前所提到的 handlerMapping 与 handlerAdapter 组件都不适用了?
认识 NamespaceHandler 接口。 查看 MvcNamespaceHandler。 查看 AnnotationDrivenBeanDefinitionParser。
结论:在 <mvc:annotation-driven />
对应的解析器,自动向 IOC 里面注册了两个 BeanDefinition。 分别是 RequestMappingHandlerMapping 与 BeanNameUrlHandlerMapping 组件。
1. 实现结构
RequestMappingHandlerMapping:URI 映射器。 RequestMappingHandlerAdapter:执行适配器。 InvocableHandlerMethod:Controller 目标对象,包含了 Controller Bean 及对应的 Method 对象,及调用方法。
HandlerMethodArgumentResolverComposite:参数处理器。 ParameterNameDiscoverer:参数名称处理器。 HandlerMethodReturnValueHandlerComposite:返回结构处理器。
2. 执行流程
org.springframework.web.servlet.DispatcherServlet#getHandler 基于注解查找映射Api。 org.springframework.web.servlet.handler.AbstractHandlerMapping#getHandler org.springframework.web.servlet.handler.AbstractHandlerMethodMapping#lookupHandlerMethod org.springframework.web.servlet.handler.AbstractHandlerMethodMapping.MappingRegistry#getMappingsByUrl
org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter#handle org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter#handleInternal org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter#invokeHandlerMethod org.springframework.web.method.support.InvocableHandlerMethod#invokeForRequest org.springframework.web.method.support.InvocableHandlerMethod#doInvoke