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

网友投稿 566 2022-12-24

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

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 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 {

converter总接口:

@FunctionalInterface

public interface Converter {

//自定义转换器:实现按照自己的规则给相应对象赋值

@Override

public void addFormatters(FormatterRegistry registry) {

registry.addConverter(new Converter() {

@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 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 model,

HttpServletRequest request) throws Exception {

model.forEach((name, value) -> {

if (value != null) {

request.setAttribute(name, value);

}

else {

request.removeAttribute(name);

}

});

}

不管我们在方法形参位置放 Map集合或者Molde 最终在底层源码都是同一个对象在mvcContainer容器中进行保存

版权声明:本文内容由网络用户投稿,版权归原作者所有,本站不拥有其著作权,亦不承担相应法律责任。如果您发现本站中有涉嫌抄袭或描述失实的内容,请联系我们jiasou666@gmail.com 处理,核实后本网站将在24小时内删除侵权内容。

上一篇:智能车载终端行业市场分析(智能汽车市场)
下一篇:解决JDK8的ParallelStream遍历无序的问题
相关文章

 发表评论

暂时没有评论,来抢沙发吧~