HandlerAdapter
因为SpringMVC中的Handler可以是任意的形式,只要能处理请求就ok,但是Servlet需要的处理方法的结构却是固定的,都是以request和response为参数的方法。如何让固定的Servlet处理方法调用灵活的Handler来进行处理呢?这就是HandlerAdapter要做的事情。
Handler是用来干活的工具;HandlerMapping用于根据需要干的活找到相应的工具;HandlerAdapter是使用工具干活的人
public interface HandlerAdapter { //当前 HandlerAdapter 是否支持这个 Handler boolean supports(Object handler); //调用handle处理这个请求,然后返回ModelAndView 对象 @Nullable ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception; long getLastModified(HttpServletRequest request, Object handler); }
因为它和HandlerMapping联系紧密,因此且听下文分解
HandlerAdapter可以有多个
HandlerExceptionResolver
其它组件都是用来干活的。在干活的过程中难免会出现问题,出问题后怎么办呢?这就需要有一个专门的角色对异常情况进行处理,在SpringMVC中就是HandlerExceptionResolver。具体来说,此组件的作用是根据异常设置ModelAndView,之后再交给render方法进行渲染。
以前我们可以用web.xml的<error-page>标签来捕获状态码500 400的异常,但是这个已经out了,现在全局的异常都可以交给HandlerExceptionResolver去捕获处理
public interface HandlerExceptionResolver { @Nullable ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response, @Nullable Object handler, Exception ex); }
这个接口捕获的是所有异常,而官方推荐的是使用@ExceptionHandler注解去捕获固定的异常
这个类建议交给Spring子容器管理(我们可以多实现),因为它就像一个特殊的controller
关于这块Spring MVC的全局异常的处理的一些技巧(比如页面、get、post、ajax等),请关注后面博文
RequestToViewNameTranslator
Spring MVC是通过ViewName来找到对应的视图的,而此接口的作用就是从request中获取viewName。
public interface RequestToViewNameTranslator { @Nullable String getViewName(HttpServletRequest request) throws Exception; }
它只有一个实现,默认实现:DefaultRequestToViewNameTranslator:
@Override public String getViewName(HttpServletRequest request) { String lookupPath = this.urlPathHelper.getLookupPathForRequest(request); return (this.prefix + transformPath(lookupPath) + this.suffix); } @Nullable protected String transformPath(String lookupPath) { String path = lookupPath; if (this.stripLeadingSlash && path.startsWith(SLASH)) { path = path.substring(1); } if (this.stripTrailingSlash && path.endsWith(SLASH)) { path = path.substring(0, path.length() - 1); } if (this.stripExtension) { path = StringUtils.stripFilenameExtension(path); } if (!SLASH.equals(this.separator)) { path = StringUtils.replace(path, SLASH, this.separator); } return path; }
主要实现就是调用UrlPathHelper的getLookupPathForRequest的方法获取一个looup路径。transformPath方法主要是对获取的路径字符串再做个简单处理罢了。
所以核心是UrlPathHelper的getLookupPathForRequest的实现:
(解析路径这块,这里就不做太多的解释了,自己读读源码,相对来说比较简单)
ViewResolver
ViewResolver用来将String类型的视图名和Locale解析为View类型的视图。View是用来渲染页面的,也就是将程序返回的参数填入模板里,生成html(也可能是其它类型)文件。这里就有两个关键问题:使用哪个模板?用什么技术(规则)填入参数?这其实是ViewResolver主要要做的工作
ViewResolver需要找到渲染所用的模板和所用的技术(也就是视图的类型)进行渲染,具体的渲染过程则交由不同的视图自己完成。
public interface ViewResolver { @Nullable View resolveViewName(String viewName, Locale locale) throws Exception; }
1.AbstractCachingViewResolver 基于缓存的抽象视图解析器
2.UrlBasedViewResolver 实现了缓存 提供了prefix suffix拼接的url视图解析器。
3.InternalResourceViewResolver 基于url 的内部资源视图解析器。
4.XmlViewResolver 基于xml的缓存视图解析器
5.BeanNameViewResolver beanName来自容器,并且不支持缓存。
6.ResourceBundleViewResolver 这个有点复杂
7.reeMarkerViewResolver、VolocityViewResolver 都基于url 但会解析成特定的view
实现类也非常的多,在Spring MVC里是一个非常非常重要的概念(比如什么时候返回页面,什么时候返回json呢?),因此后面会有专门的文章进行深入解读
ViewResolverComposite简单来说就是使用简单的List来保存你配置使用的视图解析器。
ViewResolvers可以有多个
FlashMapManager
用来管理FlashMap的,FlashMap主要用在redirect中传递参数。
public interface FlashMapManager { @Nullable FlashMap retrieveAndUpdate(HttpServletRequest var1, HttpServletResponse var2); void saveOutputFlashMap(FlashMap var1, HttpServletRequest var2, HttpServletResponse var3); }
可以看出结构图非常简单,抽象类采用模板模式定义整个流程,具体实现类用SessionFlashMapManager通过模板方法提供了具体操作FlashMap的功能。
功能说明:
- 实际的Session中保存的FlashMap是List类型,也就是说一个Session可以保存多个FlashMap,一个FlashMap保存着一套Redirect转发所传递的参数
- FlashMap继承自HashMap,除了用于HashMap的功能和设置有效期,还可以保存Redirect后的目标路径和通过url传递的参数,这两项内容主要用来从Session保存的多个FlashMap中查找当前的FalshMap
具体请持续关注吧,后面再详说
至此,SpringMVC中的9大组件也就简单地概述了一遍。通过对此9大组件的宏观认识,对分析SpringMVC的设计、原理与实现都会有很大的帮助作用。
onRefresh(wac) / initStrategies(wac)详解
这可以说是DisparcherServlet初始化时的核心逻辑
initMultipartResolver
private void initMultipartResolver(ApplicationContext context) { try { this.multipartResolver = context.getBean(MULTIPART_RESOLVER_BEAN_NAME, MultipartResolver.class); } catch (NoSuchBeanDefinitionException ex) { this.multipartResolver = null; } }
这个很简单,若我们向容器里配置了此Bean就有,否则默认是不支持文件上传的
备注:注意配置此些配型Bean的名称,都是有固定值的,请必须保证一样,否则你的配置将不生效。下同