转载

SpringMVC源码系列:九大组件小记

前面几篇文章都是针对于SpringMVC中的具体组件进行源码分析的;本文主要用于补充记录一下关于SpringMVC中九大组件的学习。这个会牵扯出除之前的几篇 HandlerMapping 之外的其他一些基础组件。

之前简单的有介绍过 DispatcherServlet 这个类的体系结构,此处就不再赘述了。在 DispatcherServlet 类中,其在mvc子容器进行初始化时就会完成对九大组件的初始化工作,具体哪九大组件后面会慢慢说到。先来看下在 DispatcherServlet 中是通过哪些方法来完成初始化工作的,先贴一段代码:

protected void onRefresh(ApplicationContext context) {
    this.initStrategies(context);
}

protected void initStrategies(ApplicationContext context) {
    this.initMultipartResolver(context);
    this.initLocaleResolver(context);
    this.initThemeResolver(context);
    this.initHandlerMappings(context);
    this.initHandlerAdapters(context);
    this.initHandlerExceptionResolvers(context);
    this.initRequestToViewNameTranslator(context);
    this.initViewResolvers(context);
    this.initFlashMapManager(context);
}

上面代码中的 onRefresh 方法就是 DispatcherServlet 的入口方法。在 onRefresh 中又通过调用 initStrategies 方法来将各个组件的初始化逻辑进行整合,个人理解其实就是策略套策略,在一个就是职责也明确。

initStrategies 方法中又通过调用组件各自的初始化方法来完成具体的初始化工作。从这个地方其实就可以清楚的看出SpringMVC中的9个组件名称了。下面就来捋一捋这九大组件的基本职责。

HandlerMapping

关于handlermapping在下面几篇文章中做过一些基本介绍,但是还不是很全,对于handlermapping的子类还没有分析完,这个会后期更新的。

  • SpringMVC源码系列:HandlerMapping
  • SpringMVC源码系列:AbstractHandlerMapping
  • SpringMVC源码系列:AbstractUrlHandlerMapping

对于 HandlerMapping 来说,其作用就是根据 request 找到相应的处理器 HandlerIntecepter 拦截器。具体细节参数上面第一篇文章。

HandlerAdapter

如果说 HandlerMapping 是一支笔,那么 HandlerAdapter 就是用笔的人。也就是说 HandlerAdapter 就是使用处理器干活的人。为什么呢?来看下代码:

public interface HandlerAdapter {
    boolean supports(Object var1);
    ModelAndView handle(HttpServletRequest var1, HttpServletResponse var2, Object var3) throws Exception;
    long getLastModified(HttpServletRequest var1, Object var2);
}

是不是一目了然了,在 HandlerAdapter 接口中提供了 handle 这样一个方法,参数中Object handler第三个参数其实就是一个处理器,那我们就知道了, handle 方法就是使用 handler 来处理逻辑的。处理之后返回一个 ModelAndView

HandlerExceptionResolver

这个是 SpringMVC 中的异常处理组件, HandlerExceptionResolver 这个组件的作用就是根据异常设置 ModelAndView ,然后再将处理结果交给 render 方法进行渲染。当然 render 也仅仅只是负责将 ModelAndView 渲染成页面, ModelAndView 的具体来源它不关心。

这里需要说明一下,加入在渲染过程中发生异常怎么办?从上面的分析我们可以清楚的知道, HandlerExceptionResolver 这个组件对异常的处理结果是 ModelAndView ,然后再由 render 方法进行渲染,也就是说 HandlerExceptionResolver 是在渲染之前工作的,因此渲染过程中发生异常, HandlerExceptionResolver 是不会处理的。

public interface HandlerExceptionResolver {
    ModelAndView resolveException(HttpServletRequest var1, HttpServletResponse var2, Object var3, Exception var4);
}

HandlerExceptionResolver 中也只有一个方法,这个方法就是从异常中解析出 ModelAndView

ViewResolver

ViewResolver 的作用是将 String 类型的逻辑视图根据local解析为 View 视图的。下面是 ViewResolver 的源码接口定义:

public interface ViewResolver {
    View resolveViewName(String viewName, Locale local) throws Exception;
}

从代码中可以看到,在 ViewResolver 中也是只有一个方法,从 resolveViewName 方法的参数和返回结果就很好的解释了其作用。

  • viewName String类型的视图名
  • local 区域,可以用来做国际化。

View 实际上是用来渲染页面的,也就是说将程序返回的结果填入到具体的模板里面,生成具体的视图文件,比如:jsp,ftl,html等。

但是这里又会牵扯出两个问题:

  • 用什么模板?
  • 参数怎么填入?

当然,这两个问题也就是本小节说的 ViewResolver 需要解决的问题。大体分为两种:

针对单一视图类型的解析器

  • InternalResourceViewResolver
  • FreeMarkerViewResolver

上面两种是用的最多的两种, InternalResourceViewResolver 用来解析jsp,而 FreeMarkerViewResolver 则是针对FreeMarker。

针对同时解析多种类型视图的解析器

  • BeanNameViewResolver

    需要同时使用视图名和对应的local来解析视图。它需要将每一个视图名和对应的视图类型配置到相应的properties文件中。(后面讲组件实现细节时给出列子)

  • XmlViewResolver

    XmlViewResolver和BeanNameViewResolver有点差不多,BeanNameViewResolver使用的是xml格式的配置文件。

  • ResourceBundleViewResolver

    这个其实就是根据viewName从Spring容器中查找bean,再根据这个bean来找到对应的视图。

LocalResolver

在上面的 ViewResolver 中提到,解析视图需要两个参数,一个是String类型的逻辑视图名,另外一个是local。 LocalResolver 的作用就是从request中解析出local的。

public interface LocaleResolver {
    Locale resolveLocale(HttpServletRequest request);

    void setLocale(HttpServletRequest request, HttpServletResponse response, Locale local);
}

第一个方法是从request中解析出local,第二个方法是将local设置到request中。

关于local大多数情况下都是用来做国际化处理的。

ThemeResolver

解析主题的。这个我平时除了SpringMVC自己提供的功能外,很少自己去扩展使用,即使是换主题也没有做过。不过既然存在肯定是有存在的原因的。对于我们常见的网页界面活着手机界面来说,一套主题无非就是换一套图片,活着css样式文件等等。我们通过 ThemeResolver 这个就可以实现这样的功能。具体使用其实也就是配一套properties文件供系统在不同的时候读取切换;当然使用这个也是可以实现国际化的。

public interface ThemeResolver {
    String resolveThemeName(HttpServletRequest request);

    void setThemeName(HttpServletRequest request, HttpServletResponse response, String themeName);
}

RequestToViewNameTranslator

这个其实还是挺有意思的,就是将request请求转换为视图名称。

public interface RequestToViewNameTranslator {
    String getViewName(HttpServletRequest request) throws Exception;
}

RequestToViewNameTranslator 只有一个默认的实现类 DefaultRequestToViewNameTranslator

DefaultRequestToViewNameTranslator 具体实现了getViewName(HttpServletRequest request)方法:

public String getViewName(HttpServletRequest request) {
    String lookupPath = this.urlPathHelper.getLookupPathForRequest(request);
    return this.prefix + this.transformPath(lookupPath) + this.suffix;
}

主要是委派给urlPathHelper帮助类得到请求的后缀名称,比如通过 请求路径比如/glmapper/login.do转换得到/login.do ;具体怎么转换成视图也会在后面的组件介绍中给出具体的例子。

MultipartResolver

这个相应小伙伴们也不陌生,做网站多多少少会涉及到文件上传。 MultipartResolver 就是用来处理上传请求的。其处理方式就是将request包装成 MultipartHttpServletRequest 。然后我们就可以用 MultipartHttpServletRequest 这个直接调用getFile获取的文件了。

FlashMapManager

这个在redirect是进行参数传递需要用到。

public interface FlashMapManager {
    FlashMap retrieveAndUpdate(HttpServletRequest request, HttpServletResponse response);

    void saveOutputFlashMap(FlashMap flashMap, HttpServletRequest request, HttpServletResponse response);
}

retrieveAndUpdate 这个方法是用来恢复参数的,对于恢复过的和超时的参数将都会被删除掉。

saveOutputFlashMap 这个方法是用来保存参数的。

FlashMapManager 的默认实现机制中参数的存储是放在session中的。我之前在一个项目中就有遇到过这种情况,对于一些我们不想暴露在url中的参数,在进行请求转发时,可以使用@RedirectAttributes将参数保存,然后在下一个处理器中获取到。

小结

本篇主要是来对九大组件做一个笼统的介绍,细节实现及案例均不涉及;在后续的SpringMVC源码系列中对各个组件的实现细节分析时再一探究竟吧。

原文  https://juejin.im/post/5aae7669f265da23826dc408
正文到此结束
Loading...