SpringMVC的Map共享数据的原理
SpringMVC流程
简单来说就是一个请求到达前端控制器,先由处理器映射器处理请求获得chain执行链,这个执行链还包含了拦截器,这个执行链经过处理器适配器拿到合适的处理器适配器,去执行相应的处理器方法,最终返回ModelAndView,接着通过视图解析器获取逻辑视图结合数据渲染为物理视图最终返回给用户
前言
在参考一个开源项目的时候看到在其代码编写在Controller层使用Map来共享数据,因为以前一直都是使用ModelAndView或者Model共享数据,想当然的以为这是SpringMVC专门用来共享数据的组件,其他的不行,于是看到可以这样用时,便开始疑惑,在查阅了一些资料以及自己debug之后,写下这一篇小小的分析总结
- 罪魁祸首
@RequestMapping(value = "/detail",method = RequestMethod.GET)
//这里使用Map来共享数据
public String detail(Long id,Map map){
OrderInfo orderInfo = orderInfoService.getById(id);
UserInfo userInfo = userInfoService.getById(orderInfo.getUserId());
List<OrderAction> orderActionList = orderActionService.getOrderAction(id);
map.put("userInfo",userInfo);
map.put("orderInfo",orderInfo);
map.put("orderActionList",orderActionList);
return "order/order_detail";
}
这个map是一个BindingAwareModelMap,这个map是怎么来的,SpringMVC是怎么帮我们封装参数的
从DispatcherServlet开始
在配置好web.xml之后,一切能够到达服务的请求都会到达DispatcherServlet,在DispatcherServlet做分发,在Servlet中,获取到适配器中后,调用适配器的handle来执行
DispatcherServlet
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
...
// Actually invoke the handler.
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
...
}
这里的ha是RequestMappingHandlerAdapter对象,用来找到controller中贴了@RequestMapping的方法
RequestMappingHandlerAdapter
// RequestMappingHandlerAdapter并没有重写这个方法,这个方法实际上是其父类的
public final ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
//调用handleInternal方法
return handleInternal(request, response, (HandlerMethod) handler);
}
protected ModelAndView handleInternal(HttpServletRequest request,HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {
ModelAndView mav;
...
//最终会执行这个方法,并且返回ModelAndView
mav = invokeHandlerMethod(request, response, handlerMethod);
...
}
protected ModelAndView invokeHandlerMethod(HttpServletRequest request,HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {
...
//新建一个ServletInvocableHandlerMethod(Servlet反射型处理)对象,会通过这个对象反射调用Controller里面的方法
ServletInvocableHandlerMethod invocableMethod = createInvocableHandlerMethod(handlerMethod);
//装载参数解析器
invocableMethod.setHandlerMethodArgumentResolvers(this.argumentResolvers);
//装载返回值解析器
invocableMethod.setHandlerMethodReturnValueHandlers(this.returnValueHandlers);
...
//执行方法,mavContainer是用于共享数据的一个存储空间
invocableMethod.invokeAndHandle(webRequest, mavContainer);
}
其中,argumentResolvers和returnValueHandlers是默认的解析器,是在bean初始化前配置好的,在afterPropertiesSet
方法中配置这些解析器,而afterPropertiesSet
这个方法将在所有的属性被初始化后调用,但是会在init前调用
public void afterPropertiesSet() {
// Do this first, it may add ResponseBody advice beans
initControllerAdviceCache();
if (this.argumentResolvers == null) {
//getDefaultArgumentResolvers这个方法将默认的参数解析器加载进一个List
List<HandlerMethodArgumentResolver> resolvers = getDefaultArgumentResolvers();
this.argumentResolvers = new HandlerMethodArgumentResolverComposite().addResolvers(resolvers);
}
...
}
最终这些解析器会被装载进HandlerMethodArgumentResolverComposite类(这是一种组合模式)
回到invokeAndHandle(webRequest, mavContainer)
方法中
ServletInvocableHandlerMethod
public void invokeAndHandle(ServletWebRequest webRequest, ModelAndViewContainer mavContainer,Object... providedArgs) throws Exception {
//在该方法中,会为用户封装好需要的请求参数,再执行用户自己编写的业务方法,最终返回其结果,其实是逻辑视图
Object returnValue = invokeForRequest(webRequest, mavContainer, providedArgs);
...
//将逻辑视图交给对应的返回值解析器处理
this.returnValueHandlers.handleReturnValue(returnValue, getReturnValueType(returnValue), mavContainer, webRequest);
InvocableHandlerMethod
InvocableHandlerMethod是ServletInvocableHandlerMethod的父类
public Object invokeForRequest(NativeWebRequest request, ModelAndViewContainer mavContainer,Object... providedArgs) throws Exception {
//这里拿到的是我们编写业务的形参所对应的真实对象
Object[] args = getMethodArgumentValues(request, mavContainer, providedArgs);
...
//执行方法,返回结果
Object returnValue = doInvoke(args);
...
return returnValue;
}
private Object[] getMethodArgumentValues(NativeWebRequest request, ModelAndViewContainer mavContainer, Object... providedArgs) throws Exception {
/*
*RequestMappingAdaptor不会将providedArgs传过来,所以是null
args[i] = resolveProvidedArgument(parameter, providedArgs);
if (args[i] != null) {
continue;
}
*/
//找到适配的解析器
if (this.argumentResolvers.supportsParameter(parameter)) {
try {
//通过解析器解析方法上的形式参数类型,按照顺序生成相对应的对象装进args中返回
args[i] = this.argumentResolvers.resolveArgument(parameter, mavContainer, request, this.dataBinderFactory);
continue;
}
catch (Exception ex) {
if (logger.isDebugEnabled()) {
logger.debug(getArgumentResolutionErrorMessage("Failed to resolve", i), ex);
}
throw ex;
}
}
...
return args;
再往下其实就是遍历已有的解析器,找到对应的解析器并进行解析,Map类型的解析器是MapMethodProcessor,调用其resolveArgument方法
//MapMethodProcessor的resolveArgument方法,事实上就是拿到Model,存到map里的数据其实就是存到model里
return mavContainer.getModel();
结论
最终我们发现,在Controller上使用Map来共享数据,其实就是存放在Model