SpringBoot SpringMVC参数传递 SpringBoot与SpringMVC中参数传递的原理解析
感谢一切给予 人气:0一:普通参数与基本注解
HandlerMapping中找到能处理请求的Handler(Controller,method())
为当前Handler找一个适配器HandlerAdapter:RequestMappingHandlerAdapter
1.HandlerAdapter
0-支持方法上标注@RequestMapping
1-支持函数式编程的
xxxx
2.执行目标方法
3.参数解析器:确定要执行的目标方法每一个参数的值是什么
boolean supportsParameter(MethodParameter parameter);
Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer,
先判断是否支持该参数类型, 如果支持, 就调用resolveArgument解析方法
4.返回值处理器
5.挨个判断所有参数解析器哪个支持这个参数:HandlerMethodArgumentResolver: 把控着支持的方法参数类型
请求进来后, 首先从handlerMapping中查找是否有对应的映射处理, 得到映射适配器Adapter,再通过适配器,查找有哪些方法匹配请求,首先判断方法名,以及参数类型是否匹配,首先获得方法中声明的参数名字, 放到数组里,循环遍历27种解析器判断是否有支持处理对应参数名字类型的解析器,如果有的话,根据名字进行解析参数,根据名字获得域数据中的参数, 循环每个参数名字进行判断, 从而为每个参数进行赋值.
对于自定义的POJO类参数:
ServletRequestMethodArgumentResolver 这个解析器用来解析: 是通过主要是通过判断是否是简单类型得到的
@Override public boolean supportsParameter(MethodParameter parameter) { return (parameter.hasParameterAnnotation(ModelAttribute.class) || (this.annotationNotRequired && !BeanUtils.isSimpleProperty(parameter.getParameterType()))); } public static boolean isSimpleValueType(Class<?> type) { return (Void.class != type && void.class != type && (ClassUtils.isPrimitiveOrWrapper(type) || Enum.class.isAssignableFrom(type) || CharSequence.class.isAssignableFrom(type) || Number.class.isAssignableFrom(type) || Date.class.isAssignableFrom(type) || Temporal.class.isAssignableFrom(type) || URI.class == type || URL.class == type || Locale.class == type || Class.class == type)); } public final Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer, NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory) throws Exception { Assert.state(mavContainer != null, "ModelAttributeMethodProcessor requires ModelAndViewContainer"); Assert.state(binderFactory != null, "ModelAttributeMethodProcessor requires WebDataBinderFactory"); String name = ModelFactory.getNameForParameter(parameter); ModelAttribute ann = parameter.getParameterAnnotation(ModelAttribute.class); if (ann != null) { mavContainer.setBinding(name, ann.binding()); } Object attribute = null; BindingResult bindingResult = null; if (mavContainer.containsAttribute(name)) { attribute = mavContainer.getModel().get(name); } else { // Create attribute instance try { attribute = createAttribute(name, parameter, binderFactory, webRequest); } catch (BindException ex) { if (isBindExceptionRequired(parameter)) { // No BindingResult parameter -> fail with BindException throw ex; } // Otherwise, expose null/empty value and associated BindingResult if (parameter.getParameterType() == Optional.class) { attribute = Optional.empty(); } else { attribute = ex.getTarget(); } bindingResult = ex.getBindingResult(); } } if (bindingResult == null) { // Bean property binding and validation; // skipped in case of binding failure on construction. WebDataBinder binder = binderFactory.createBinder(webRequest, attribute, name); if (binder.getTarget() != null) { if (!mavContainer.isBindingDisabled(name)) { bindRequestParameters(binder, webRequest); } validateIfApplicable(binder, parameter); if (binder.getBindingResult().hasErrors() && isBindExceptionRequired(binder, parameter)) { throw new BindException(binder.getBindingResult()); } } // Value type adaptation, also covering java.util.Optional if (!parameter.getParameterType().isInstance(attribute)) { attribute = binder.convertIfNecessary(binder.getTarget(), parameter.getParameterType(), parameter); } bindingResult = binder.getBindingResult(); } // Add resolved attribute and BindingResult at the end of the model Map<String, Object> bindingResultModel = bindingResult.getModel(); mavContainer.removeAttributes(bindingResultModel); mavContainer.addAllAttributes(bindingResultModel); return attribute; }
WebDataBinder binder =binderFactory.createBinder(webRequest,attribute,name)
WebDataBinder:web数据绑定器,将请求参数的值绑定到指定的javaBean里面
WebDataBinder 利用它里面的Converters将请求数据转成指定的数据类型,通过反射一系列操作,再次封装到javabean中
GenericConversionService:在设置每一个值的时候,找它里面所有的converter哪个可以将这个数据类型(request带来参数的字符串)转换到指定的类型(javabean—某一个类型)
未来我们可以给WebDataBinder里面放自己的Converter
private static final class StringToNumber implements Converter<String, T> { converter总接口: @FunctionalInterface public interface Converter<S, T> {
//自定义转换器:实现按照自己的规则给相应对象赋值
@Override public void addFormatters(FormatterRegistry registry) { registry.addConverter(new Converter<String, Pet>() { @Override public Pet convert(String source) { if (!StringUtils.isEmpty(source)){ Pet pet = new Pet(); String[] split = source.split(","); pet.setName(split[0]); pet.setAge(split[1]); return pet; } return null; } }); }
二:复杂参数
Map/Model(map/model里面的数据会被放在request的请求域 相当于request.setAttribute)/Errors/BindingResult/RedirectAttributes(重定向携带数据)/ServletRespons().SessionStaus.UriComponentsBuilder
6.在上面第五步目标方法执行完成后:
将所有的数据都放在ModelAdnViewContainer;包含要去的页面地址View,还包含Model数据
7.处理派发结果
processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
在页面进行响应前, 进行视图渲染的时候:
exposeModelAsRequestAttributes(model, request); 该方法将model中所有参数都放在请求域数据中
protected void renderMergedOutputModel( Map<String, Object> model, HttpServletRequest request, HttpServletResponse response) throws Exception { // Expose the model object as request attributes. exposeModelAsRequestAttributes(model, request); // Expose helpers as request attributes, if any. exposeHelpers(request); // Determine the path for the request dispatcher. String dispatcherPath = prepareForRendering(request, response); // Obtain a RequestDispatcher for the target resource (typically a JSP). RequestDispatcher rd = getRequestDispatcher(request, dispatcherPath); if (rd == null) { throw new ServletException("Could not get RequestDispatcher for [" + getUrl() + "]: Check that the corresponding file exists within your web application archive!"); } // If already included or response already committed, perform include, else forward. if (useInclude(request, response)) { response.setContentType(getContentType()); if (logger.isDebugEnabled()) { logger.debug("Including [" + getUrl() + "]"); } rd.include(request, response); } else { // Note: The forwarded resource is supposed to determine the content type itself. if (logger.isDebugEnabled()) { logger.debug("Forwarding to [" + getUrl() + "]"); } rd.forward(request, response); } }
通过循环遍历model中的所有数据放在请求域中
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); } }); }
不管我们在方法形参位置放 Map集合或者Molde 最终在底层源码都是同一个对象在mvcContainer容器中进行保存
加载全部内容