Spring MVC
springMVC是一种web层mvc框架,用于替代servlet,简化 web 编程。是一种基于Java的实现了Web MVC 设计模式的请求驱动类型的轻量级Web框架,即使用了MVC架构模式的思想,将web层进行职责解耦。
九大组件
- HandlerMapping(处理器映射器):
HandlerMapping 是用来查找 Handler的,可以是类也可以是一个方法。用 @RequestMapping 标注的就是一个 Handler,负责实际的请求处理。也就是说 HandlerMapping负责根据请求的URL来找到具体
public interface HandlerMapping {
@Nullable
HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception;
}
- HandlerAdapter(处理器适配器):在 MVC 中 Handler可以是任意形式的,只有能处理就可以。而 Servlet 里面的机构都是固定的doService(HttpServletRequest request, HttpServletResponse response) 只能处理 request 和 response。任意形式的Handler通过使用适配器,可以“转换”成固定形式,然后交给Servlet来处理。
public interface HandlerAdapter {
// 判断是否支持传入的handler
boolean supports(Object handler);
// 使用给定的handler处理请求
@Nullable
ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception;
// 返回上次修改时间,可以返回-1表示不支持
long getLastModified(HttpServletRequest request, Object handler);
}
- HandlerExceptionResolver(处理器异常解析器):
是用来处理 Handler 产生异常情况的组件,据异常设置ModelAndView。
public interface HandlerExceptionResolver {
@Nullable
ModelAndView resolveException(
HttpServletRequest request, HttpServletResponse response, @Nullable Object handler, Exception ex);
}
- ViewResolver(实体解析器):将 String 类型的视图名和 Locale 解析为 View 类型的视图。 ViewResolver 做两件事情,一是找到所用渲染的模板,二是所用到的技术(JSP)
public interface ViewResolver {
@Nullable
View resolveViewName(String viewName, Locale locale) throws Exception;
}
- RequestToViewNameTranslator(请求获取视图名称转换器):从请求中获取视图名称。 ViewResolver 根据 ViewName 获取到 View,而某些 Handler 处理完成之后并没有设置 View 或 ViewName,便从这个组件去查找。
public interface RequestToViewNameTranslator {
@Nullable
String getViewName(HttpServletRequest request) throws Exception;
}
- LocaleResolver(本地化解析器):ViewResolver 需要两个参数,viewName 和 locale,而 Locale 就是从 LocaleResolver 出来的。主要是用来处理 request 里面的区域,如:zh-cn。也可以用来做国际化处理。
public interface LocaleResolver {
Locale resolveLocale(HttpServletRequest request);
void setLocale(HttpServletRequest request, @Nullable HttpServletResponse response, @Nullable Locale locale);
}
- ThemeResolver(主题解析器):用来解析主题的。主题就是样式、图片。
- MultipartResolver(上传解析器):用来处理上传请求。通过将普通的请求包装成 MultipartHttpServletRequest 来实现。封装普通请求让其拥有文件删除的功能。
- FlashMapManager: 用来管理FlashMap的,FlashMap主要用在redirect中传递参数。
MVC 初始化时序图
字母转换类
- A:DispatcherServlet
- B:HttpServletBean
- C:AbstractDetectingUrlHandlerMapping
步骤说明:
- [0]:Spring MVC 初始化过程,init() 具体实现又 DispatcherServlet 父类 HttpServletBean 进行初始化工作。
public final void init() throws ServletException {
// Set bean properties from init parameters.
// 从init参数设置bean属性。
PropertyValues pvs = new ServletConfigPropertyValues(getServletConfig(), this.requiredProperties);
if (!pvs.isEmpty()) {
try {
// 定位资源
BeanWrapper bw = PropertyAccessorFactory.forBeanPropertyAccess(this);
// 加载配置信息
ResourceLoader resourceLoader = new ServletContextResourceLoader(getServletContext());
bw.registerCustomEditor(Resource.class, new ResourceEditor(resourceLoader, getEnvironment()));
initBeanWrapper(bw);
bw.setPropertyValues(pvs, true);
}
catch (BeansException ex) {
if (logger.isErrorEnabled()) {
logger.error("Failed to set bean properties on servlet '" + getServletName() + "'", ex);
}
throw ex;
}
}
// Let subclasses do whatever initialization they like.
// 初始化 IOC 容器
initServletBean();
}
- [1]:定义 MVC 的初始化, DispatcherServlet.onRefresh() 继承父类 FrameworkServlet 并重写 Bean 初始化方法,在 [0] 步骤中 == initServletBean()== 进行触发,使用委派给子类进行实现
protected void onRefresh(ApplicationContext context) {
initStrategies(context);
}
- [2]:MVC 九大组件初始化定义策略
protected void initStrategies(ApplicationContext context) {
// 初始化 多文件上传 组件
initMultipartResolver(context);
// 初始化 本地化解析器 组件
initLocaleResolver(context);
// 初始化 主题解析器 组件
initThemeResolver(context);
// 初始化 处理器映射器 组件
initHandlerMappings(context);
// 初始化 处理器适配器 组件
initHandlerAdapters(context);
// 初始化 处理器异常解析器 组件
initHandlerExceptionResolvers(context);
// 初始化 请求获取视图名称转换器 组件
initRequestToViewNameTranslator(context);
// 初始化 视图解析器 组件
initViewResolvers(context);
// 初始化 FlashMap 组件
initFlashMapManager(context);
}
- [3]:初始化 HandlerMappings
private void initHandlerMappings(ApplicationContext context) {
this.handlerMappings = null;
if (this.detectAllHandlerMappings) {
// Find all HandlerMappings in the ApplicationContext, including ancestor contexts.
// 在ApplicationContext中查找所有HandlerMappings,包括祖先上下文。
Map<String, HandlerMapping> matchingBeans =
BeanFactoryUtils.beansOfTypeIncludingAncestors(context, HandlerMapping.class, true, false);
if (!matchingBeans.isEmpty()) {
this.handlerMappings = new ArrayList<>(matchingBeans.values());
// We keep HandlerMappings in sorted order.
AnnotationAwareOrderComparator.sort(this.handlerMappings);
}
}
else {
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.
}
}
// Ensure we have at least one HandlerMapping, by registering
// a default HandlerMapping if no other mappings are found.
// 通过注册一个默认的HandlerMapping(如果没有找到其他映射),确保我们至少有一个HandlerMapping。
if (this.handlerMappings == null) {
this.handlerMappings = getDefaultStrategies(context, HandlerMapping.class);
if (logger.isTraceEnabled()) {
logger.trace("No HandlerMappings declared for servlet '" + getServletName() +
"': using default strategies from DispatcherServlet.properties");
}
}
}
- [4]:使用默认策略,确保我们至少有一个HandlerMapping,默认的配置在 DispatcherServlet.properties 下
org.springframework.web.servlet.HandlerMapping=org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping,\
org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping,\
org.springframework.web.servlet.function.support.RouterFunctionMapping
protected <T> List<T> getDefaultStrategies(ApplicationContext context, Class<T> strategyInterface) {
String key = strategyInterface.getName();
// 获取 DispatcherServlet.properties 下 HandlerMapping 的值
String value = defaultStrategies.getProperty(key);
if (value != null) {
String[] classNames = StringUtils.commaDelimitedListToStringArray(value);
List<T> strategies = new ArrayList<>(classNames.length);
for (String className : classNames) {
try {
Class<?> clazz = ClassUtils.forName(className, DispatcherServlet.class.getClassLoader());
// 实际初始化 HandlerMapping 入口
Object strategy = createDefaultStrategy(context, clazz);
strategies.add((T) strategy);
}
catch (ClassNotFoundException ex) {
throw new BeanInitializationException(
"Could not find DispatcherServlet's default strategy class [" + className +
"] for interface [" + key + "]", ex);
}
catch (LinkageError err) {
throw new BeanInitializationException(
"Unresolvable class definition for DispatcherServlet's default strategy class [" +
className + "] for interface [" + key + "]", err);
}
}
return strategies;
}
else {
return new LinkedList<>();
}
}
- [5]:以 BeanNameUrlHandlerMapping 为例, 初始化工作在其父类AbstractDetectingUrlHandlerMapping.initApplicationContext() 方法中。
public void initApplicationContext() throws ApplicationContextException {
super.initApplicationContext();
// 进行 HandlerMapping 初始化
detectHandlers();
}
- [6]:建立当前 ApplicationContext 中所有 Controller 和 URL 的关系,HandlerMapping 初始化完成。
protected void detectHandlers() throws BeansException {
ApplicationContext applicationContext = obtainApplicationContext();
// 获取 IOC 容器所有 Bean 的名字
String[] beanNames = (this.detectHandlersInAncestorContexts ?
BeanFactoryUtils.beanNamesForTypeIncludingAncestors(applicationContext, Object.class) :
applicationContext.getBeanNamesForType(Object.class));
// Take any bean name that we can determine URLs for.
// 遍历 beanNames,并找到 Bean 所对应的 URL
for (String beanName : beanNames) {
String[] urls = determineUrlsForHandler(beanName);
if (!ObjectUtils.isEmpty(urls)) {
// URL paths found: Let's consider it a handler.
// 保存 URL 和 beanName 的关系
registerHandler(urls, beanName);
}
}
if ((logger.isDebugEnabled() && !getHandlerMap().isEmpty()) || logger.isTraceEnabled()) {
logger.debug("Detected " + getHandlerMap().size() + " mappings in " + formatMappingName());
}
}
MVC 运行流程图
用户发出一个请求,进入 DispatcherServlet.doService() 方法。跳转至 doDispatch() 方法,为 MVC 请求实际处理方法。
- 进入 HandlerMapping,找到该请求所对应的 Handler 并返回 HandlerExecutionChain(封装了 Handler 和 Interceptor ),如果没找到对应的 Handler 则返回 404
- 根据获取到的 Handler 传入 HandlerAdapter 找到对应的适配器,找到HandlerMethod(Controller 里面的业务方法)并执行返回ModelAndView,如果在此过程中有拦截器则运行拦截器,如果没有则不运行。
- DispatcherServlet 传入 ModelAndView,这是的View 还是只是个 ViewName(String),经过 ViewResolvers 解析成实际的渲染页面
- 把数据(数据来源于request,modelMap,session中)与模板的变量进行替换,最终通过 Servlet 返回给用户
MVC 运行时序图
字母替换类说明:
- A:DispatcherServlet
- B: AbstractHandlerMethodAdapter
- C: RequestMappingHandlerAdapter
步骤说明:
- [0]:用户发出一个请求,进入 DispatcherServlet,因为 DispatcherServlet 继承 HttpServlet 所以会进 doService() 方法。
protected void doService(HttpServletRequest request, HttpServletResponse response) throws Exception {
logRequest(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<>();
Enumeration<?> attrNames = request.getAttributeNames();
while (attrNames.hasMoreElements()) {
String attrName = (String) attrNames.nextElement();
if (this.cleanupAfterInclude || attrName.startsWith(DEFAULT_STRATEGIES_PREFIX)) {
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());
if (this.flashMapManager != null) {
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 {
// 进入 MVC 实际控制方法
doDispatch(request, response);
}
finally {
if (!WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted()) {
// Restore the original attribute snapshot, in case of an include.
if (attributesSnapshot != null) {
restoreAttributesAfterInclude(request, attributesSnapshot);
}
}
}
}
- [1]:DispatcherServlet 核心方法 doDispatch()
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
HttpServletRequest processedRequest = request;
HandlerExecutionChain mappedHandler = null;
boolean multipartRequestParsed = false;
WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
try {
ModelAndView mv = null;
Exception dispatchException = null;
try {
// 检查是否是文件上传的请求
processedRequest = checkMultipart(request);
multipartRequestParsed = (processedRequest != request);
// Determine handler for the current request.
// 获取处理当前请求的 Controller(Handler)
// 返回 HandlerExecutionChain(处理器执行链),封装了 Handler 和 Interceptor
mappedHandler = getHandler(processedRequest);
// 如果 Handler 为空,则返回 404
if (mappedHandler == null) {
noHandlerFound(processedRequest, response);
return;
}
// Determine handler adapter for the current request.
// 获取 处理器适配器 HandlerAdapter
HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
// Process last-modified header, if supported by the handler.
// 处理 last-modified 请求头
String method = request.getMethod();
boolean isGet = "GET".equals(method);
if (isGet || "HEAD".equals(method)) {
long lastModified = ha.getLastModified(request, mappedHandler.getHandler());
if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) {
return;
}
}
if (!mappedHandler.applyPreHandle(processedRequest, response)) {
return;
}
// Actually invoke the handler.
// 实际处理器请求
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
if (asyncManager.isConcurrentHandlingStarted()) {
return;
}
// 返回结果 ModelAndView
applyDefaultViewName(processedRequest, mv);
mappedHandler.applyPostHandle(processedRequest, response, mv);
}
catch (Exception ex) {
dispatchException = ex;
}
catch (Throwable err) {
// As of 4.3, we're processing Errors thrown from handler methods as well,
// making them available for @ExceptionHandler methods and other scenarios.
dispatchException = new NestedServletException("Handler dispatch failed", err);
}
processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
}
catch (Exception ex) {
triggerAfterCompletion(processedRequest, response, mappedHandler, ex);
}
catch (Throwable err) {
triggerAfterCompletion(processedRequest, response, mappedHandler,
new NestedServletException("Handler processing failed", err));
}
finally {
if (asyncManager.isConcurrentHandlingStarted()) {
// Instead of postHandle and afterCompletion
// 请求成功响应之后的方法
if (mappedHandler != null) {
mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);
}
}
else {
// Clean up any resources used by a multipart request.
if (multipartRequestParsed) {
cleanupMultipart(processedRequest);
}
}
}
}
- [2]:获取 HandlerExecutionChain
protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
if (this.handlerMappings != null) {
for (HandlerMapping mapping : this.handlerMappings) {
HandlerExecutionChain handler = mapping.getHandler(request);
if (handler != null) {
return handler;
}
}
}
return null;
}
- [3]:根据 Handler 查找出对应的 HandlerAdapter
protected HandlerAdapter getHandlerAdapter(Object handler) throws ServletException {
if (this.handlerAdapters != null) {
for (HandlerAdapter adapter : this.handlerAdapters) {
if (adapter.supports(handler)) {
return adapter;
}
}
}
throw new ServletException("No adapter for handler [" + handler +
"]: The DispatcherServlet configuration needs to include a HandlerAdapter that supports this handler");
}
- [4]:实际处理器请求
public final ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {
return handleInternal(request, response, (HandlerMethod) handler);
}
- [5]:根据 HandlerAdapter 查找到对应的 RequestMappingHandlerAdapter 实际处理对象,Adapter的实现子类并返回 ModelAndView,拦截器也在这里面进行处理
protected ModelAndView handleInternal(HttpServletRequest request,
HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {
ModelAndView mav;
checkRequest(request);
// Execute invokeHandlerMethod in synchronized block if required.
if (this.synchronizeOnSession) {
HttpSession session = request.getSession(false);
if (session != null) {
Object mutex = WebUtils.getSessionMutex(session);
synchronized (mutex) {
mav = invokeHandlerMethod(request, response, handlerMethod);
}
}
else {
// No HttpSession available -> no mutex necessary
mav = invokeHandlerMethod(request, response, handlerMethod);
}
}
else {
// No synchronization on session demanded at all...
mav = invokeHandlerMethod(request, response, handlerMethod);
}
if (!response.containsHeader(HEADER_CACHE_CONTROL)) {
if (getSessionAttributesHandler(handlerMethod).hasSessionAttributes()) {
applyCacheSeconds(response, this.cacheSecondsForSessionAttributeHandlers);
}
else {
prepareResponse(response);
}
}
return mav;
}
总结
熟悉 Spring MVC 的请求流程和底层结构可以更好的了解它,知道拦截器在那个位置进行触发。用户发出请求后了解整个的请求过程具体做了什么、干了什么。而没了解之前就跟一个黑盒一样,只知传入的参数和返回的结果,具体怎么转变的却不知道。熟悉后对写业务更加的得心应手。