组件和概念
处理器:Handler
1,对应 MVC 三层中的 C,也就是控制器,找不到 handler 抛出 404
2,具体表现形式很多,使用 @RequestMapping 注解是做常用的,还有实现 org.springframework.web.servlet.mvc.Controller 接口;实现 org.springframework.web.HttpRequestHandler 接口等
3,只要能实际处理请求的就可以是 handler,正因为实现 handler 的方式不同,所以需要合适的适配器去执行 handler
处理器执行链:HandlerExecutionChain
1,处理器 + 拦截器列表(有顺序的,顺序执行,包括自定义的)
2,其实不难理解,比如项目中配置了拦截器,执行请求时也会执行拦截器,所以当请求过来找到了处理器并不是直接九执行,还要找到 处理器执行链 再执行
前端控制器:DispatcherServlet
1,整个 springmvc 的核心,请求的响应和处理都是它完成的
2,继承关系:DispatcherServlet -> FrameworkServlet -> HttpServletBean -> HttpServlet
3,实际上就是一个 Servlet,所以 DispatcherServlet 初始化的时候会执行 HttpServletBean 的 init() 方法。这是 DispatcherServlet 初始化的入口
DispatcherServlet 九大组件
处理器映射器:HandlerMapping
1,DispatcherServlet 初始化时完成配置,作用是然后根据 request 找到 HandlerExecutionChain
2,HandlerMapping 是一个接口,很多子接口和实现类。其中获取 handler 的方式是模板方式,由子类完成查找 handler(源码是遍历所有子类去查找)
3,RequestMappingHandlerMapping:存储 @RequestMapping 实现的 handler
4,BeanNameUrlHandlerMapping:存储 Controller 类实现 Controller 或 HttpRequestHandler 接口实现的 handler
处理器适配器:HandlerAdapter
1,通过处理器映射器找到了 HandlerExecutionChain,其实也就找到了 Handler
2,Handler 的实现方式是多种,所以要找到合适当前 handler 的执行工具,这个工具就是 HandlerAdapter
3,RequestMappingHandlerAdapter 执行 @RequestMapping 注解实现的 handler
处理器异常解析器:HandlerExceptionResolver
1,当 handler 执行出现了异常时,DispatcherServlet 会调用 HandlerExceptionResolver 来处理。具体是由 DefaultHandlerExceptionResolver 处理,全局异常见下表格
2,自定义全局异常有两种方式:@ControllerAdvice 结合 @ExceptionHandler;继承 HandlerExceptionResolver 并重写 resolveException 方法
Exception |
HTTP Status Code |
HttpRequestMethodNotSupportedException |
405 (SC_METHOD_NOT_ALLOWED) |
HttpMediaTypeNotSupportedException |
415 (SC_UNSUPPORTED_MEDIA_TYPE) |
HttpMediaTypeNotAcceptableException |
406 (SC_NOT_ACCEPTABLE) |
MissingPathVariableException |
500 (SC_INTERNAL_SERVER_ERROR) |
MissingServletRequestParameterException |
400 (SC_BAD_REQUEST) |
ServletRequestBindingException |
400 (SC_BAD_REQUEST) |
ConversionNotSupportedException |
500 (SC_INTERNAL_SERVER_ERROR) |
TypeMismatchException |
400 (SC_BAD_REQUEST) |
HttpMessageNotReadableException |
400 (SC_BAD_REQUEST) |
HttpMessageNotWritableException |
500 (SC_INTERNAL_SERVER_ERROR) |
MethodArgumentNotValidException |
400 (SC_BAD_REQUEST) |
MissingServletRequestPartException |
400 (SC_BAD_REQUEST) |
BindException |
400 (SC_BAD_REQUEST) |
NoHandlerFoundException |
400 (SC_BAD_REQUEST) |
AsyncRequestTimeoutException |
404 (SC_NOT_FOUND) |
AsyncRequestTimeoutException |
503 (SC_SERVICE_UNAVAILABLE) |
试图解析器:ViewResolver
ViewResolver用来将String类型的视图名和Locale解析为View类型的视图。View是用来渲染页面的,也就是将程序返回的参数填入模板里,生成html(也可能是其它类型)文件。
这里就有两个关键问题:使用哪个模板?用什么技术(规则)填入参数?
这其实是ViewResolver主要要做的工作,ViewResolver需要找到渲染所用的模板和所用的技术(也就是视图的类型)进行渲染,具体的渲染过程则交由不同的视图自己完成
RequestToViewNameTranslator
ViewName是根据ViewName查找View,但有的Handler处理完后并没有设置View也没有设置ViewName,这时就需要从request获取ViewName了,如何从request中获取ViewName就是RequestToViewNameTranslator要做的事情了。
RequestToViewNameTranslator在Spring MVC容器里只可以配置一个,所以所有request到ViewName的转换规则都要在一个Translator里面全部实现
LocaleResolver
解析视图需要两个参数:一是视图名,另一个是Locale。
视图名是处理器返回的,Locale是从哪里来的?
这就是LocaleResolver要做的事情。LocaleResolver用于从request解析出Locale,Locale就是zh-cn之类,表示一个区域,有了这个就可以对不同区域的用户显示不同的结果。
SpringMVC主要有两个地方用到了Locale:一是ViewResolver视图解析的时候;二是用到国际化资源或者主题的时候
ThemeResolver
用于解析主题。SpringMVC中一个主题对应一个properties文件,里面存放着跟当前主题相关的所有资源、如图片、css样式等。
SpringMVC的主题也支持国际化,同一个主题不同区域也可以显示不同的风格。
SpringMVC中跟主题相关的类有 ThemeResolver、ThemeSource和Theme。
主题是通过一系列资源来具体体现的,要得到一个主题的资源,首先要得到资源的名称,这是ThemeResolver的工作。然后通过主题名称找到对应的主题(可以理解为一个配置)文件,这是ThemeSource的工作。最后从主题中获取资源就可以了
MultipartResolver
用于处理上传请求。
处理方法是将普通的request包装成MultipartHttpServletRequest,后者可以直接调用getFile方法获取File
如果上传多个文件,还可以调用getFileMap得到FileName->File结构的Map。此组件中一共有三个方法,作用分别是判断是不是上传请求,将request包装成MultipartHttpServletRequest、处理完后清理上传过程中产生的临时资源
FlashMapManager
用来管理FlashMap的,FlashMap主要用在redirect中传递参数
DispatcherServlet 初始化
第一步:HttpServletBean 初始化
HttpServletBean 作用是扩展 HttpServlet
// DispatcherServlet 构造方法
public DispatcherServlet() {
super();
setDispatchOptionsRequest(true);
}
// FrameworkServlet 构造方法
public FrameworkServlet() {
// 省略了 supper()
}
// HttpServletBean.init()
public final void init() throws ServletException {
// ... 这部分的作用是扩展 HttpServlet,具体不展开主要是下面这个方法,目的是创建 IOC 容器
// 这个方法是个空方法,子类实现
initServletBean();
}
第二步:创建 IOC 容器
/**
* FrameworkServlet 重写了 initServletBean
* 作用就是创建 WebApplicationContext(应用上下文),其实就是 MVC 的 IOC 容器
*/
@Override
protected final void initServletBean() throws ServletException {
try {
// 创建 IOC 容器
this.webApplicationContext = initWebApplicationContext();
// 子类实现
initFrameworkServlet();
}
catch (ServletException | RuntimeException ex) {
logger.error("Context initialization failed", ex);
throw ex;
}
}
/**
* 具体怎么创建 IOC 容器
*
*/
protected WebApplicationContext initWebApplicationContext() {
WebApplicationContext rootContext = WebApplicationContextUtils.getWebApplicationContext(getServletContext());
WebApplicationContext wac = null;
// 当前对象是否有 wac,有就用它
if (this.webApplicationContext != null) {
// A context instance was injected at construction time -> use it
wac = this.webApplicationContext;
if (wac instanceof ConfigurableWebApplicationContext) {
ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext) wac;
if (!cwac.isActive()) {
if (cwac.getParent() == null) {
cwac.setParent(rootContext);
}
configureAndRefreshWebApplicationContext(cwac);
}
}
}
// 如果 wac 不存在就到 ServletContext 查找是否有初始化过的 wac
if (wac == null) {
wac = findWebApplicationContext();
}
// 还是不存在就创建一个
if (wac == null) {
// No context instance is defined for this servlet -> create a local one
wac = createWebApplicationContext(rootContext);
}
// 到这里有了 wac,如果没有初始化就初始化
if (!this.refreshEventReceived) {
synchronized (this.onRefreshMonitor) {
onRefresh(wac);
}
}
if (this.publishContext) {
// 把 wac 设置到 ServletContext 的 attrbiute 里去
String attrName = getServletContextAttributeName();
getServletContext().setAttribute(attrName, wac);
}
return wac;
}
第三步:配置 DispatcherServlet 属性
1,这一步其实就是初始化九大对象,这一步走完就意味着 DispatcherServlet 初始化完成
2,每个组件的初始化不一一展开,分析下处理器映射器的初始化
@Override
protected void onRefresh(ApplicationContext context) {
// 调用了 initStrategies()
initStrategies(context);
}
// 初始化九大组件
protected void initStrategies(ApplicationContext context) {
initMultipartResolver(context);
initLocaleResolver(context);
initThemeResolver(context);
initHandlerMappings(context);
initHandlerAdapters(context);
initHandlerExceptionResolvers(context);
initRequestToViewNameTranslator(context);
initViewResolvers(context);
initFlashMapManager(context);
}
private void initHandlerMappings(ApplicationContext context) {
// 初始化映射器为空
this.handlerMappings = null;
// 据属性 detectAllHandlerMappings 决定是检测所有的 HandlerMapping 对象还是使用指定名称的 HandlerMapping 对象
if (this.detectAllHandlerMappings) {
// 从容器及其祖先容器查找所有类型为 HandlerMapping 的 HandlerMapping 对象,记录到 handlerMappings 并排序
Map<String, HandlerMapping> matchingBeans = BeanFactoryUtils.beansOfTypeIncludingAncestors(context, HandlerMapping.class, true, false);
if (!matchingBeans.isEmpty()) {
this.handlerMappings = new ArrayList<>(matchingBeans.values());
// 进行定义时所使用的 order 属性,顺序属性很关键,因为它涉及到 HandlerMapping 使用时的优先级
// 比如在根据请求查找 handler 的时候 @RequestMapping 实现的 handler 使用 RequestMappingHandlerMapping,而不是 BeanNameUrlHandlerMapping
AnnotationAwareOrderComparator.sort(this.handlerMappings);
}
} else {
// 获取名称为 handlerMapping 的 HandlerMapping bean 并记录到 handlerMappings
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.
}
}
// 如果 handlerMapping 为空就通过 DispatcherServlet.properties 配置文件构建
if (this.handlerMappings == null) {
this.handlerMappings = getDefaultStrategies(context, HandlerMapping.class);
}
for (HandlerMapping mapping : this.handlerMappings) {
if (mapping.usesPathPatterns()) {
this.parseRequestPath = true;
break;
}
}
}
DispatcherServlet 请求处理流程
进入 DispatcherServlet.doDispatch 方法
- DispatcherServlet 接收到请求,先是调用
doService
来处理,doService 调用 doDispatch
来处理
获取 HandlerExecutionChain(处理器执行链)
/**
* doDispatch 会调用 getHandler 方法来获取处理器执行链,子类实现
*/
@Nullable
protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
if (this.handlerMappings != null) {
// DispatcherServlet 初始化时后会准备好一批处理器映射器
// 遍历映射器,找到 handler
for (HandlerMapping mapping : this.handlerMappings) {
// 由抽象子类 AbstractHandlerMapping 实现
HandlerExecutionChain handler = mapping.getHandler(request);
if (handler != null) {
return handler;
}
}
}
return null;
}
/**
* AbstractHandlerMapping.getHandler
*/
@Override
@Nullable
public final HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
// 先根据 request 查找 handler
// 断点可以看到是根据 uri @RequestMapping() 值去找的方法:HandlerMethod.getHandlerInternal
Object handler = getHandlerInternal(request);
// 省略...
// 把 handler 和所有的拦截器封装成 HandlerExecutionChain(也可以自定义拦截器)
HandlerExecutionChain executionChain = getHandlerExecutionChain(handler, request);
if (hasCorsConfigurationSource(handler) || CorsUtils.isPreFlightRequest(request)) {
CorsConfiguration config = getCorsConfiguration(handler, request);
if (getCorsConfigurationSource() != null) {
CorsConfiguration globalConfig = getCorsConfigurationSource().getCorsConfiguration(request);
config = (globalConfig != null ? globalConfig.combine(config) : config);
}
if (config != null) {
config.validateAllowCredentials();
}
// executionChain 虽然再进行改变,最终 handler 肯定是不变的,所以改变的是拦截器,这里很明显是添加了跨域相关的拦截器
executionChain = getCorsHandlerExecutionChain(request, executionChain, config);
}
return executionChain;
}
寻找能处理 handler 的处理器适配器
protected HandlerAdapter getHandlerAdapter(Object handler) throws ServletException {
if (this.handlerAdapters != null) {
// 遍历所有处理器适配器
for (HandlerAdapter adapter : this.handlerAdapters) {
// DispatcherServlet 初始化的时候维护了一批适配器
// @RequestMapping 实现的 handler 都是 RequestMappingHandlerAdapter 这个适配器去执行
if (adapter.supports(handler)) {
return adapter;
}
}
// 找不到能执行的抛出异常
}
}
处理器适配器执行 handler,返回一个 ModelView
// ha 就是 HandlerAdapter 即 RequestMappingHandlerAdapter
// 根据断点 handle 方法是由其子类 AbstractHandlerMethodAdapter 实现
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
// AbstractHandlerMethodAdapter.handle
@Override
@Nullable
public final ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {
// 又调用了 handleInternal 来处理,返回一个 ModelAndView
return handleInternal(request, response, (HandlerMethod) handler);
}
// AbstractHandlerMethodAdapter.handleInternal
@Override
protected ModelAndView handleInternal(HttpServletRequest request,
HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {
ModelAndView mav;
checkRequest(request);
// Execute invokeHandlerMethod in synchronized block if required.
// 默认是 false
if (this.synchronizeOnSession) {
// ...
}
else {
// No synchronization on session demanded at all...
// 执行处理器(就是执行哪个 controller 的具体的方法),结果是一个 ModelView
mav = invokeHandlerMethod(request, response, handlerMethod);
}
// ...
return mav;
}
ViewReslover 解析 ModelView
这一步根据 ModelView 解析出具体 View 和 Model
DispatcherServlet 渲染 View
DispatcherServlet 对 View 进行渲染,将 model 渲染到 view
DispatcherServlet 响应用户