SpringBoot与SpringMVC中参数传递的原理解析

 更新时间:2021年07月28日 10:47:51   作者:感谢一切给予  
这篇文章主要介绍了SpringBoot与SpringMVC中参数传递的原理,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下

一:普通参数与基本注解

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容器中进行保存

到此这篇关于SpringBoot与SpringMVC中参数传递的原理的文章就介绍到这了,更多相关SpringBoot SpringMVC参数传递内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • 关于spring属性占位符用法

    关于spring属性占位符用法

    这篇文章主要介绍了关于spring属性占位符用法,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教
    2023-11-11
  • 详解Spring配置及事务的使用

    详解Spring配置及事务的使用

    这篇文章主要介绍了详解Spring配置及事务的使用,文中附含详细的示例代码说明,有需要的朋友可以借鉴参考下,希望能够有所帮助
    2021-09-09
  • java对ArrayList排序代码示例

    java对ArrayList排序代码示例

    本文通过代码示例给大家介绍java对arraylist排序,代码简洁易懂,感兴趣的朋友一起学习吧
    2015-11-11
  • Java练习之潜艇小游戏的实现

    Java练习之潜艇小游戏的实现

    这篇文章主要和大家分享一个Java小练习——利用Java编写一个潜艇小游戏,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下
    2022-03-03
  • pagehelper插件显示total为-1或1的问题

    pagehelper插件显示total为-1或1的问题

    这篇文章主要介绍了pagehelper插件显示total为-1或1,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2020-09-09
  • SpringBoot创建自定义starter详解

    SpringBoot创建自定义starter详解

    这篇文章主要介绍了SpringBoot创建自定义starter详解,Starter是Spring Boot中的一个非常重要的概念,Starter相当于模块,它能将模块所需的依赖整合起来并对模块内的Bean根据环境(条件)进行自动配置,需要的朋友可以参考下
    2024-01-01
  • java中httpclient封装post请求和get的请求实例

    java中httpclient封装post请求和get的请求实例

    这篇文章主要介绍了java中httpclient封装post请求和get的请求实例,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2021-10-10
  • JDK8接口的默认与静态方法-接口与抽象类的区别详解

    JDK8接口的默认与静态方法-接口与抽象类的区别详解

    这篇文章主要介绍了JDK8接口的默认与静态方法-接口与抽象类的区别详解,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,,需要的朋友可以参考下
    2019-06-06
  • Java数据结构之红黑树的原理及实现

    Java数据结构之红黑树的原理及实现

    红黑树是一种特殊的二叉查找树,每个结点都要储存位表示结点的颜色,或红或黑。本文将通过示例为大家详细讲讲红黑树的原理及Java实现,感兴趣的可以了解一下
    2022-09-09
  • springboot内嵌Tomcat安全漏洞修复方式

    springboot内嵌Tomcat安全漏洞修复方式

    针对CVE-2020-1938漏洞,建议升级Tomcat至安全版本以避免受影响,影响版本包括:Apache Tomcat 9.x小于9.0.31、Apache Tomcat 8.x小于8.5.51、Apache Tomcat 7.x小于7.0.100及Apache Tomcat 6.x,
    2024-10-10

最新评论