深入解析SpringBoot中的Model和Map参数处理机制

在SpringBoot中,处理请求的控制器方法可以接受多种类型的参数,如Model和Map<String, Object>。你是否好奇它们是如何被解析和处理的?本篇文章将带你深入探究SpringBoot源码,揭示Model和Map参数背后的工作原理。通过详细分析ModelAndViewContainer、MapMethodProcessor、ModelMethodProcessor等核心类,以及视图渲染过程中数据如何从BindingAwareModelMap转移到请求域,你将全面理解SpringBoot如何确保Model和Map的无缝互换及数据一致性。阅读本文,你将掌握SpringBoot参数解析的内在机制,提升对Spring MVC框架的深入理解。准备好了吗?让我们一同揭开这神秘面纱

SpringBoot源码解析之Model和Map参数解析

在Spring MVC中,处理请求的方法可以接受多种类型的参数,比如HttpServletRequestModelMap<String, Object>等。这篇文章将详细解析ModelMap参数在SpringBoot中的处理机制,帮助大家更深入地理解其工作原理。

测试代码

首先,我们通过一个简单的示例代码来展示ModelMap在控制器方法中的使用。

@Controller
public class HelloController {

    @RequestMapping("/helloModelAndMap")
    public String helloModelAndMap(HttpServletRequest request, Model model, Map<String, Object> map) {
        System.out.println(model.getClass().getSimpleName());
        System.out.println(map.getClass().getSimpleName());
        System.out.println(model == map);
        
        request.setAttribute("message", "Hello World!");
        model.addAttribute("name", "张三");
        map.put("age", 18);
        
        // 这里拿不到Model和Map中的值,转发后能拿到 在视图渲染阶段才会放到请求域中
        System.out.println("helloModelAndMap:" + request.getAttribute("name"));
        System.out.println("helloModelAndMap:" + request.getAttribute("age"));
        
        return "forward:/success";
    }

    @RequestMapping("/success")
    @ResponseBody
    public Object success(HttpServletRequest request) {
        Map<String, Object> result = new HashMap<>();
        result.put("message", request.getAttribute("message"));
        result.put("name", request.getAttribute("name"));
        result.put("age", request.getAttribute("age"));
        return result;
    }
}

控制台输出

运行上述代码并访问/helloModelAndMap时,控制台会输出以下信息:

BindingAwareModelMap
BindingAwareModelMap
true
helloModelAndMap:null
helloModelAndMap:null

请求响应

访问/helloModelAndMap后,浏览器会收到以下响应:

{"name":"张三","message":"Hello World!","age":18}

结果分析

通过控制台输出我们可以看到:

  • modelmap的类型都是BindingAwareModelMap
  • modelmap指向同一个对象。

这表明Spring在处理ModelMap参数时,将它们都映射到同一个BindingAwareModelMap对象。

深入源码解析

ModelAndViewContainer

ModelAndViewContainer是Spring MVC在处理请求过程中用于存储模型数据的容器。在其内部,包含了一个ModelMap对象:

public class ModelAndViewContainer {
   private final ModelMap defaultModel = new BindingAwareModelMap();
  
   public ModelMap getModel() {
      if (useDefaultModel()) {
         return this.defaultModel;
      } else {
         if (this.redirectModel == null) {
            this.redirectModel = new ModelMap();
         }
         return this.redirectModel;
      }
   }

   // 其他代码省略...
}
MapMethodProcessor

MapMethodProcessor用于处理Map类型的参数。它实现了HandlerMethodArgumentResolver接口,用于解析方法参数。

public class MapMethodProcessor implements HandlerMethodArgumentResolver, HandlerMethodReturnValueHandler {

   @Override
   public boolean supportsParameter(MethodParameter parameter) {
      return Map.class.isAssignableFrom(parameter.getParameterType()) &&
            parameter.getParameterAnnotations().length == 0;
   }

   @Override
   public Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer,
         NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory) throws Exception {
      
      Assert.state(mavContainer != null, "ModelAndViewContainer is required for model exposure");
      return mavContainer.getModel();
   }

   @Override
   public boolean supportsReturnType(MethodParameter returnType) {
      return Map.class.isAssignableFrom(returnType.getParameterType());
   }

   @Override
   public void handleReturnValue(@Nullable Object returnValue, MethodParameter returnType,
         ModelAndViewContainer mavContainer, NativeWebRequest webRequest) throws Exception {

      if (returnValue instanceof Map){
         mavContainer.addAllAttributes((Map) returnValue);
      } else if (returnValue != null) {
         throw new UnsupportedOperationException("Unexpected return type: " +
               returnType.getParameterType().getName() + " in method: " + returnType.getMethod());
      }
   }
}

resolveArgument方法中,MapMethodProcessorModelAndViewContainer中获取Model,即BindingAwareModelMap对象。

ModelMethodProcessor

ModelMethodProcessor用于处理Model类型的参数。它同样实现了HandlerMethodArgumentResolver接口。

public class ModelMethodProcessor implements HandlerMethodArgumentResolver, HandlerMethodReturnValueHandler {

	@Override
	public boolean supportsParameter(MethodParameter parameter) {
		return Model.class.isAssignableFrom(parameter.getParameterType());
	}

	@Override
	public Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer,
			NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory) throws Exception {

		Assert.state(mavContainer != null, "ModelAndViewContainer is required for model exposure");
		return mavContainer.getModel();
	}

	@Override
	public boolean supportsReturnType(MethodParameter returnType) {
		return Model.class.isAssignableFrom(returnType.getParameterType());
	}

	@Override
	public void handleReturnValue(@Nullable Object returnValue, MethodParameter returnType,
			ModelAndViewContainer mavContainer, NativeWebRequest webRequest) throws Exception {

		if (returnValue == null) {
			return;
		} else if (returnValue instanceof Model) {
			mavContainer.addAllAttributes(((Model) returnValue).asMap());
		} else {
			throw new UnsupportedOperationException("Unexpected return type: " +
					returnType.getParameterType().getName() + " in method: " + returnType.getMethod());
		}
	}
}

resolveArgument方法中,ModelMethodProcessor也从ModelAndViewContainer中获取Model,即BindingAwareModelMap对象。

AbstractView

在视图渲染过程中,Spring会将BindingAwareModelMap中的数据设置到请求域中。关键代码在AbstractView类的exposeModelAsRequestAttributes方法:

protected void exposeModelAsRequestAttributes(Map<String, Object> model, HttpServletRequest request) throws Exception {
    model.forEach((name, value) -> {
        if (value != null) {
            request.setAttribute(name, value);
        } else {
            request.removeAttribute(name);
        }
    });
}

渲染视图的调用链

在处理完控制器方法后,Spring MVC会调用视图解析和渲染逻辑。在渲染视图的过程中,BindingAwareModelMap中的数据被转移到请求域中,这样前端页面便可以通过请求域获取这些数据。具体调用链如下:

  1. DispatcherServlet:处理请求,调用处理器适配器。
  2. HandlerAdapter:调用具体的控制器方法。
  3. ModelAndView:控制器方法返回ModelAndView对象。
  4. ViewResolver:解析视图名称,获取视图对象。
  5. AbstractView:在视图对象的render方法中调用exposeModelAsRequestAttributes,将模型数据暴露到请求域。

Model and View Processing

总结

通过以上分析,我们了解了SpringBoot中ModelMap参数解析的工作机制。总结如下:

  1. ModelMap参数都被解析为BindingAwareModelMap对象。
  2. ModelAndViewContainer内部使用BindingAwareModelMap来存储模型数据。
  3. MapMethodProcessorModelMethodProcessorModelAndViewContainer中获取相同的BindingAwareModelMap对象。
  4. 在视图渲染过程中,BindingAwareModelMap中的数据被转移到请求域中。

这种设计使得ModelMap可以互换使用,并且保证了数据的一致性和简便性。希望这篇文章能帮助大家更深入地理解SpringBoot的参数解析机制。如果有任何疑问或建议,欢迎在评论区讨论。

  • 4
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值