Spring boot,springboot请求处理

 2023-09-28 阅读 33 评论 0

摘要:请求映射 使用REST映射 请求映射也就是我们编写一个controller,在每一个方法上使用requestMapping来声明我们这个方法能处理xxx请求。把这个过程称之为请求映射。 @xxxMapping; @GetMapping @PostMapping @PutMapping @DeleteMapping Rest风格支持

请求映射

使用REST映射

请求映射也就是我们编写一个controller,在每一个方法上使用requestMapping来声明我们这个方法能处理xxx请求。把这个过程称之为请求映射。
@xxxMapping;
@GetMapping
@PostMapping
@PutMapping
@DeleteMapping

Rest风格支持(使用HTTP请求方式动词来表示对资源的操作)
以前:
      /getUser 获取用户
      /deleteUser 删除用户
      /editUser 修改用户
      /saveUser保存用户
现在: /user
      GET-获取用户
      DELETE-删除用户
      PUT-修改用户
      POST-保存用户

核心Filter;HiddenHttpMethodFilter

在HelloController中加入以下代码

@RequestMapping(value = "/user",method = RequestMethod.GET)public String getUser(){return "GET-张三";}@RequestMapping(value = "/user",method = RequestMethod.POST)public String saveUser(){return "POST-张三";}@RequestMapping(value = "/user",method = RequestMethod.PUT)public String putUser(){return "PUT-张三";}@RequestMapping(value = "/user",method = RequestMethod.DELETE)public String deleteUser(){return "DELETE-张三";}

index.html中

<form action="/user" method="get"><input value="REST-GET提交" type="submit" />
</form><form action="/user" method="post"><input value="REST-POST提交" type="submit" />
</form><form action="/user" method="post"><input value="REST-DELETE 提交" type="submit"/>
</form><form action="/user" method="post"><input value="REST-PUT提交"type="submit" /><form>

Spring boot,因为form表单中,method只有get和post两种方式,此时delete和put发不出来。
提交之后都是
在这里插入图片描述
在这里插入图片描述

在spring底层,其实给我们配置过了使用delete和put请求的方式。此时表单必须要是POST方式
想要在页面上提交REST风格,必须给表单上加上_method隐藏参数。然后在隐藏参数上写上请求类型。

<form action="/user" method="post"><input name="_method" type="hidden" value="DELETE"/><input value="REST-DELETE 提交" type="submit"/>
</form><form action="/user" method="post"><input name="_method" type="hidden" value="PUT" /><input value="REST-PUT提交"type="submit" /><form>

Springboot配置文件中还要手动开启REST风格

spring:mvc:hiddenmethod:filter:enabled: true   #开启页面表单的Rest功能

此时就可以返回正常结果。
在这里插入图片描述

用法总结

开启页面表单的Rest功能
页面 form的属性method=post,隐藏域 _method=put、delete等(如果直接get或post,无需隐藏域)
编写请求映射

Rest原理(表单提交要使用REST的时候)

springboot批量请求接口,表单提交会带上_method=PUT
请求过来被HiddenHttpMethodFilter拦截
    请求是否正常,并且是POST
      获取到_method的值。
这里可以忽略大小写,也即允许delete.但是之后会统一转化成DELETE
      兼容以下请求;PUT.DELETE.PATCH
      原生request(post),包装模式requesWrapper重写了getMethod方法,返回的是传入的值
      过滤器链放行的时候用wrapper。以后的方法调用getMethod是调用requesWrapper的

public class HiddenHttpMethodFilter extends OncePerRequestFilter {private static final List<String> ALLOWED_METHODS =Collections.unmodifiableList(Arrays.asList(HttpMethod.PUT.name(),HttpMethod.DELETE.name(), HttpMethod.PATCH.name()));/** Default method parameter: {@code _method}. */public static final String DEFAULT_METHOD_PARAM = "_method";private String methodParam = DEFAULT_METHOD_PARAM;/*** Set the parameter name to look for HTTP methods.* @see #DEFAULT_METHOD_PARAM*/public void setMethodParam(String methodParam) {Assert.hasText(methodParam, "'methodParam' must not be empty");this.methodParam = methodParam;}@Overrideprotected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)throws ServletException, IOException {HttpServletRequest requestToUse = request;if ("POST".equals(request.getMethod()) && request.getAttribute(WebUtils.ERROR_EXCEPTION_ATTRIBUTE) == null) {String paramValue = request.getParameter(this.methodParam);if (StringUtils.hasLength(paramValue)) {String method = paramValue.toUpperCase(Locale.ENGLISH);if (ALLOWED_METHODS.contains(method)) {requestToUse = new HttpMethodRequestWrapper(request, method);}}}filterChain.doFilter(requestToUse, response);}/*** Simple {@link HttpServletRequest} wrapper that returns the supplied method for* {@link HttpServletRequest#getMethod()}.*/private static class HttpMethodRequestWrapper extends HttpServletRequestWrapper {private final String method;public HttpMethodRequestWrapper(HttpServletRequest request, String method) {super(request);this.method = method;}@Overridepublic String getMethod() {return this.method;}}}

Rest使用客户端工具。
如PostMan可直接发送put、delete等方式请求。

注意,在springMVC里面派生了一些新的注解,如GetMapping,PostMapping,PutMapping,DeleteMapping,可以替换原有的RequestMapping

@GetMapping("/user")
//@RequestMapping(value = "/user",method = RequestMethod.GET)public String getUser(){return "GET-张三";}@PostMapping("/user")
//@RequestMapping(value = "/user",method = RequestMethod.POST)public String saveUser(){return "POST-张三";}@PutMapping("/user")
//@RequestMapping(value = "/user",method = RequestMethod.PUT)public String putUser(){return "PUT-张三";}@DeleteMapping("/user")
//@RequestMapping(value = "/user",method = RequestMethod.DELETE)public String deleteUser(){return "DELETE-张三";}

怎么改变默认的_method

@Configuration(proxyBeanMethods = false)
@ConditionalOnWebApplication(type = Type.SERVLET)
@ConditionalOnClass({ Servlet.class, DispatcherServlet.class, WebMvcConfigurer.class })
@ConditionalOnMissingBean(WebMvcConfigurationSupport.class)
@AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE + 10)
@AutoConfigureAfter({ DispatcherServletAutoConfiguration.class, TaskExecutionAutoConfiguration.class,ValidationAutoConfiguration.class })
public class WebMvcAutoConfiguration {...@Bean@ConditionalOnMissingBean(HiddenHttpMethodFilter.class)@ConditionalOnProperty(prefix = "spring.mvc.hiddenmethod.filter", name = "enabled", matchIfMissing = false)public OrderedHiddenHttpMethodFilter hiddenHttpMethodFilter() {return new OrderedHiddenHttpMethodFilter();}...
}

@ConditionalOnMissingBean(HiddenHttpMethodFilter.class)意味着在没有HiddenHttpMethodFilter时,才执行hiddenHttpMethodFilter()。因此,我们可以自定义filter,改变默认的_method。例如:

@Configuration(proxyBeanMethods = false)
public class WebConfig{//自定义filter@Beanpublic HiddenHttpMethodFilter hiddenHttpMethodFilter(){HiddenHttpMethodFilter methodFilter = new HiddenHttpMethodFilter();methodFilter.setMethodParam("_m");return methodFilter;}    
}

将_method改成_m。

<form action="/user" method="post"><input name="_m" type="hidden" value="DELETE"/><input value="REST-DELETE 提交" type="submit"/>
</form>

请求映射原理

DispatcherServlet是处理所有请求的开始。它一直向上继承到HttpServlet
在这里插入图片描述
FrammworkServlet实现了HttpServlet的doGet方法,里面用到了自己的processRequest方法,然后又调用了核心的doService方法,在DispatcherServlet中又对doService方法做了实现。然后doService中又有doDispatch方法,这是每一个请求都需要调用的方法。

java删除目录?SpringMVC功能分析都从 org.springframework.web.servlet.DispatcherServlet 中的 doDispatch()开始

protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {HttpServletRequest processedRequest = request;HandlerExecutionChain mappedHandler = null;boolean multipartRequestParsed = false;WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);try {ModelAndView mv = null;Exception dispatchException = null;try {processedRequest = checkMultipart(request);multipartRequestParsed = (processedRequest != request);// 找到当前请求使用哪个Handler(Controller的方法)处理mappedHandler = getHandler(processedRequest);//HandlerMapping:处理器映射。/xxx->>xxxx...
}

getHandler()方法如下:

@Nullable
protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {if (this.handlerMappings != null) {for (HandlerMapping mapping : this.handlerMappings) {HandlerExecutionChain handler = mapping.getHandler(request);if (handler != null) {return handler;}}}return null;
}

this.handlerMappings在Debug模式下展现的内容:
在这里插入图片描述
其中,RequestMappingHandlerMapping保存了所有@RequestMapping 和handler的映射规则
在这里插入图片描述
所有的请求映射都在HandlerMapping中:

    SpringBoot自动配置欢迎页的 WelcomePageHandlerMapping 。访问 /能访问到index.html;

    SpringBoot自动配置了默认 的 RequestMappingHandlerMapping

    请求进来,挨个尝试所有的HandlerMapping看是否有请求信息。

spring boot常用注解,        如果有就找到这个请求对应的handler

        如果没有就是下一个 HandlerMapping

    我们需要一些自定义的映射处理,我们也可以自己给容器中放HandlerMapping。自定义 HandlerMapping

常用参数注解使用

注解:

@PathVariable 路径变量
接收请求路径中占位符的值

@RequestHeader 获取请求头
在这里插入图片描述

Springboot教程、@RequestParam 获取请求参数(指问号后的参数,url?a=1&b=2)

@CookieValue 获取Cookie值
在这里插入图片描述

@RequestAttribute 获取request域属性

@RequestBody 获取请求体[POST]
只有post请求才有请求体。获取表单里面所有数据

@MatrixVariable 矩阵变量

@ModelAttribute

springboot获取请求头,测试的Controller

@RestController
public class ParameterTestController {//car/2/owner/zhangsan@GetMapping("car/{id}/owner/{username}")public Map<String,Object> getCar(@PathVariable("id") Integer id,@PathVariable("id") String name,@PathVariable Map<String,String> pv,@RequestHeader("User-Agent") String userAgent,@RequestHeader Map<String,String> headers,@RequestParam("age") Integer age,@RequestParam("inters") List<String> inters,@RequestParam Map<String,String> params,@CookieValue("__utma") String __utma,@CookieValue("__utma")Cookie cookie){Map<String,Object> map=new HashMap<>();
//        map.put("id",id);
//        map.put("name",name);
//        map.put("pv",pv);
//        map.put("userAgent",userAgent);
//        map.put("headers",headers);         //整个全量的请求头信息map.put("age",age);map.put("inters",inters);map.put("params",params);             //获取所有参数信息map.put("__utma",__utma);System.out.println(cookie.getName()+"=======>"+cookie.getValue());return map;}@PostMapping("/save")public Map postMethod(@RequestBody String content){Map<String,Object> map=new HashMap<>();map.put("content",content);return map;}
}

测试的index.html

<a href="car/3/owner/lisi?age=19&inters=basketball&inters=game">点击查看car/{id}/owner/{username}结果</a>
<form action="/save" method="post">测试@RequestBody后去数据<br/>用户名:<input name="username"/> <br/>邮箱:<input name="email"/><input type="submit" value="提交"/>
</form>

@RequestAttribute

@Controller
public class RequestController {@GetMapping("/goto")public String goToPage(HttpServletRequest request){request.setAttribute("msg","成功了");request.setAttribute("code",200);return "forward:/success";  //转发到/success请求}@ResponseBody@GetMapping("/success")public Map success(@RequestAttribute("msg") String msg,@RequestAttribute("code") Integer id,HttpServletRequest request){Object msg1=request.getAttribute("msg");Map<String,Object> map=new HashMap<>();map.put("reMethod_msg",msg1);map.put("annotation_msg",msg);return map;}

}

在这里插入图片描述

@MatrixVariable与UrlPathHelper

矩阵变量在spring boot中手动开启
根据RFC3986的规范,矩阵变量应当绑定在路径变量中。
若是有多个矩阵变量,应当使用英文符号,进行分隔。
若是一个矩阵变量有多个值,应当使用英文符号,进行分隔,获之命名多个重复的key即可。
如:/cars/sell;low=34;brand=byd,audi,yd

/cars/{path}?xxx=xxx&aaa=ccc queryString 查询字符串 @RequestParam
例子,cookie禁用了,session里面的内容怎么使用
session.set(a,b))----->jsessionid---->cookie---->每次发请求携带
url重写:/abc;jsessionid=xxxx 把cookie的值使用矩阵变量的方式进行传递。

Springboot框架,1.矩阵变量语法: 请求路径:/cars/sell;low=34;brand=byd,audi,yd
分号前面是访问路径,分号后边是矩阵变量,多个变量使用;分隔。

2.SpringBoot默认是禁用了矩阵变量的功能

手动开启:原理。对于路径的处理。UrlPathHelper的removeSemicolonContent(移除分号内容)设置为false,让其支持矩阵变量的。
矩阵变量必须有url路径变量才能被解析

手动开启矩阵变量:

实现WebMvcConfigurer接口:

@Configuration(proxyBeanMethods = false)
public class WebConfig implements WebMvcConfigurer {@Overridepublic void configurePathMatch(PathMatchConfigurer configurer) {UrlPathHelper urlPathHelper = new UrlPathHelper();// 不移除;后面的内容。矩阵变量功能就可以生效urlPathHelper.setRemoveSemicolonContent(false);configurer.setUrlPathHelper(urlPathHelper);}
}

创建返回WebMvcConfigurerBean:

@Configuration(proxyBeanMethods = false)
public class WebConfig{@Beanpublic WebMvcConfigurer webMvcConfigurer(){return new WebMvcConfigurer() {@Overridepublic void configurePathMatch(PathMatchConfigurer configurer) {UrlPathHelper urlPathHelper = new UrlPathHelper();// 不移除;后面的内容。矩阵变量功能就可以生效urlPathHelper.setRemoveSemicolonContent(false);configurer.setUrlPathHelper(urlPathHelper);}}}
}

springboot拦截所有请求?@MatrixVariable的用例

@RestController
public class ParameterTestController {///cars/sell;low=34;brand=byd,audi,yd@GetMapping("/cars/{path}")public Map carsSell(@MatrixVariable("low") Integer low,@MatrixVariable("brand") List<String> brand,@PathVariable("path") String path){Map<String,Object> map = new HashMap<>();map.put("low",low);map.put("brand",brand);map.put("path",path);return map;}// /boss/1;age=20/2;age=10@GetMapping("/boss/{bossId}/{empId}")public Map boss(@MatrixVariable(value = "age",pathVar = "bossId") Integer bossAge,@MatrixVariable(value = "age",pathVar = "empId") Integer empAge){Map<String,Object> map = new HashMap<>();map.put("bossAge",bossAge);map.put("empAge",empAge);return map;}}

各种类型参数解析原理

这要从DispatcherServlet开始说起:

public class DispatcherServlet extends FrameworkServlet {protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {HttpServletRequest processedRequest = request;HandlerExecutionChain mappedHandler = null;boolean multipartRequestParsed = false;WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);try {ModelAndView mv = null;Exception dispatchException = null;try {processedRequest = checkMultipart(request);multipartRequestParsed = (processedRequest != request);// Determine handler for the current request.mappedHandler = getHandler(processedRequest);if (mappedHandler == null) {noHandlerFound(processedRequest, response);return;}//决定一个handler的适配器// Determine handler adapter for the current request.HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());...

HandlerMapping中找到能处理请求的Handler(Controller.method())。

为当前Handler 找一个适配器 HandlerAdapter,用的最多的是RequestMappingHandlerAdapter

适配器执行目标方法并确定方法参数的每一个值

HandlerAdapter

默认会加载所有HandlerAdapter

public class DispatcherServlet extends FrameworkServlet {/** Detect all HandlerAdapters or just expect "handlerAdapter" bean?. */private boolean detectAllHandlerAdapters = true;...private void initHandlerAdapters(ApplicationContext context) {this.handlerAdapters = null;if (this.detectAllHandlerAdapters) {// Find all HandlerAdapters in the ApplicationContext, including ancestor contexts.Map<String, HandlerAdapter> matchingBeans =BeanFactoryUtils.beansOfTypeIncludingAncestors(context, HandlerAdapter.class, true, false);if (!matchingBeans.isEmpty()) {this.handlerAdapters = new ArrayList<>(matchingBeans.values());// We keep HandlerAdapters in sorted order.AnnotationAwareOrderComparator.sort(this.handlerAdapters);}}...

springboot访问路径,有这些HandlerAdapter:
在这里插入图片描述

1.支持方法上标注@RequestMapping

2.支持函数式编程的

3.…

4.…

我们默认写的这些Controller,找到的都是第一个handlerAdapter

执行目标方法

public class DispatcherServlet extends FrameworkServlet {protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {ModelAndView mv = null;...// Determine handler for the current request.mappedHandler = getHandler(processedRequest);if (mappedHandler == null) {noHandlerFound(processedRequest, response);return;}// Determine handler adapter for the current request.HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());...//本节重点// Actually invoke the handler.DispatcherServlet中doDispatch中重要的一句mv = ha.handle(processedRequest, response, mappedHandler.getHandler());

springboot默认请求路径,HandlerAdapter接口实现类RequestMappingHandlerAdapter(主要用来处理@RequestMapping)

public class RequestMappingHandlerAdapter extends AbstractHandlerMethodAdapterimplements BeanFactoryAware, InitializingBean {...//AbstractHandlerMethodAdapter类的方法,RequestMappingHandlerAdapter继承AbstractHandlerMethodAdapterpublic final ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler)throws Exception {return handleInternal(request, response, (HandlerMethod) handler);}@Overrideprotected ModelAndView handleInternal(HttpServletRequest request,HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {ModelAndView mav;//handleInternal的核心mav = invokeHandlerMethod(request, response, handlerMethod);//解释看下节//...return mav;}
}

mav = invokeHandlerMethod(request, response, handlerMethod); //执行目标方法。

参数解析器

在这里插入图片描述
确定将要执行的目标方法的每一个参数的值是什么;

比如上面第一个是用来解析RequestParam,第二个PathVariable。

SpringMVC目标方法能写多少种参数类型。取决于参数解析器argumentResolvers
在这里插入图片描述
当前解析器是否支持解析哪种参数

支持就调用解析方法

@Nullable
protected ModelAndView invokeHandlerMethod(HttpServletRequest request,HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {ServletWebRequest webRequest = new ServletWebRequest(request, response);try {WebDataBinderFactory binderFactory = getDataBinderFactory(handlerMethod);ModelFactory modelFactory = getModelFactory(handlerMethod, binderFactory);ServletInvocableHandlerMethod invocableMethod = createInvocableHandlerMethod(handlerMethod);if (this.argumentResolvers != null) {//<-----关注点invocableMethod.setHandlerMethodArgumentResolvers(this.argumentResolvers);}...

SpringBoot项目、this.argumentResolvers在afterPropertiesSet()方法内初始化

public class RequestMappingHandlerAdapter extends AbstractHandlerMethodAdapterimplements BeanFactoryAware, InitializingBean {@Nullableprivate HandlerMethodArgumentResolverComposite argumentResolvers;@Overridepublic void afterPropertiesSet() {...if (this.argumentResolvers == null) {//初始化argumentResolversList<HandlerMethodArgumentResolver> resolvers = getDefaultArgumentResolvers();this.argumentResolvers = new HandlerMethodArgumentResolverComposite().addResolvers(resolvers);}...}//初始化了一堆的实现HandlerMethodArgumentResolver接口的private List<HandlerMethodArgumentResolver> getDefaultArgumentResolvers() {List<HandlerMethodArgumentResolver> resolvers = new ArrayList<>(30);// Annotation-based argument resolutionresolvers.add(new RequestParamMethodArgumentResolver(getBeanFactory(), false));resolvers.add(new RequestParamMapMethodArgumentResolver());resolvers.add(new PathVariableMethodArgumentResolver());resolvers.add(new PathVariableMapMethodArgumentResolver());resolvers.add(new MatrixVariableMethodArgumentResolver());resolvers.add(new MatrixVariableMapMethodArgumentResolver());resolvers.add(new ServletModelAttributeMethodProcessor(false));resolvers.add(new RequestResponseBodyMethodProcessor(getMessageConverters(), this.requestResponseBodyAdvice));resolvers.add(new RequestPartMethodArgumentResolver(getMessageConverters(), this.requestResponseBodyAdvice));resolvers.add(new RequestHeaderMethodArgumentResolver(getBeanFactory()));resolvers.add(new RequestHeaderMapMethodArgumentResolver());resolvers.add(new ServletCookieValueMethodArgumentResolver(getBeanFactory()));resolvers.add(new ExpressionValueMethodArgumentResolver(getBeanFactory()));resolvers.add(new SessionAttributeMethodArgumentResolver());resolvers.add(new RequestAttributeMethodArgumentResolver());// Type-based argument resolutionresolvers.add(new ServletRequestMethodArgumentResolver());resolvers.add(new ServletResponseMethodArgumentResolver());resolvers.add(new HttpEntityMethodProcessor(getMessageConverters(), this.requestResponseBodyAdvice));resolvers.add(new RedirectAttributesMethodArgumentResolver());resolvers.add(new ModelMethodProcessor());resolvers.add(new MapMethodProcessor());resolvers.add(new ErrorsMethodArgumentResolver());resolvers.add(new SessionStatusMethodArgumentResolver());resolvers.add(new UriComponentsBuilderMethodArgumentResolver());if (KotlinDetector.isKotlinPresent()) {resolvers.add(new ContinuationHandlerMethodArgumentResolver());}// Custom argumentsif (getCustomArgumentResolvers() != null) {resolvers.addAll(getCustomArgumentResolvers());}// Catch-allresolvers.add(new PrincipalMethodArgumentResolver());resolvers.add(new RequestParamMethodArgumentResolver(getBeanFactory(), true));resolvers.add(new ServletModelAttributeMethodProcessor(true));return resolvers;}}

HandlerMethodArgumentResolverComposite类如下:(众多参数解析器argumentResolvers的包装类)。

public class HandlerMethodArgumentResolverComposite implements HandlerMethodArgumentResolver {private final List<HandlerMethodArgumentResolver> argumentResolvers = new ArrayList<>();...public HandlerMethodArgumentResolverComposite addResolvers(@Nullable HandlerMethodArgumentResolver... resolvers) {if (resolvers != null) {Collections.addAll(this.argumentResolvers, resolvers);}return this;}...
}

我们看看HandlerMethodArgumentResolver的源码:

public interface HandlerMethodArgumentResolver {//当前解析器是否支持解析这种参数boolean supportsParameter(MethodParameter parameter);@Nullable//如果支持,就调用 resolveArgumentObject resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer,NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory) throws Exception;}

返回值处理器

ValueHandler

@Nullable
protected ModelAndView invokeHandlerMethod(HttpServletRequest request,HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {ServletWebRequest webRequest = new ServletWebRequest(request, response);try {WebDataBinderFactory binderFactory = getDataBinderFactory(handlerMethod);ModelFactory modelFactory = getModelFactory(handlerMethod, binderFactory);ServletInvocableHandlerMethod invocableMethod = createInvocableHandlerMethod(handlerMethod);if (this.argumentResolvers != null) {invocableMethod.setHandlerMethodArgumentResolvers(this.argumentResolvers);}if (this.returnValueHandlers != null) {//<---关注点invocableMethod.setHandlerMethodReturnValueHandlers(this.returnValueHandlers);}...

this.returnValueHandlers在afterPropertiesSet()方法内初始化

public class RequestMappingHandlerAdapter extends AbstractHandlerMethodAdapterimplements BeanFactoryAware, InitializingBean {@Nullableprivate HandlerMethodReturnValueHandlerComposite returnValueHandlers;@Overridepublic void afterPropertiesSet() {...if (this.returnValueHandlers == null) {List<HandlerMethodReturnValueHandler> handlers = getDefaultReturnValueHandlers();this.returnValueHandlers = new HandlerMethodReturnValueHandlerComposite().addHandlers(handlers);}}//初始化了一堆的实现HandlerMethodReturnValueHandler接口的private List<HandlerMethodReturnValueHandler> getDefaultReturnValueHandlers() {List<HandlerMethodReturnValueHandler> handlers = new ArrayList<>(20);// Single-purpose return value typeshandlers.add(new ModelAndViewMethodReturnValueHandler());handlers.add(new ModelMethodProcessor());handlers.add(new ViewMethodReturnValueHandler());handlers.add(new ResponseBodyEmitterReturnValueHandler(getMessageConverters(),this.reactiveAdapterRegistry, this.taskExecutor, this.contentNegotiationManager));handlers.add(new StreamingResponseBodyReturnValueHandler());handlers.add(new HttpEntityMethodProcessor(getMessageConverters(),this.contentNegotiationManager, this.requestResponseBodyAdvice));handlers.add(new HttpHeadersReturnValueHandler());handlers.add(new CallableMethodReturnValueHandler());handlers.add(new DeferredResultMethodReturnValueHandler());handlers.add(new AsyncTaskMethodReturnValueHandler(this.beanFactory));// Annotation-based return value typeshandlers.add(new ServletModelAttributeMethodProcessor(false));handlers.add(new RequestResponseBodyMethodProcessor(getMessageConverters(),this.contentNegotiationManager, this.requestResponseBodyAdvice));// Multi-purpose return value typeshandlers.add(new ViewNameMethodReturnValueHandler());handlers.add(new MapMethodProcessor());// Custom return value typesif (getCustomReturnValueHandlers() != null) {handlers.addAll(getCustomReturnValueHandlers());}// Catch-allif (!CollectionUtils.isEmpty(getModelAndViewResolvers())) {handlers.add(new ModelAndViewResolverMethodReturnValueHandler(getModelAndViewResolvers()));}else {handlers.add(new ServletModelAttributeMethodProcessor(true));}return handlers;}
}

HandlerMethodReturnValueHandlerComposite类如下:

public class HandlerMethodReturnValueHandlerComposite implements HandlerMethodReturnValueHandler {private final List<HandlerMethodReturnValueHandler> returnValueHandlers = new ArrayList<>();...public HandlerMethodReturnValueHandlerComposite addHandlers(@Nullable List<? extends HandlerMethodReturnValueHandler> handlers) {if (handlers != null) {this.returnValueHandlers.addAll(handlers);}return this;}}

HandlerMethodReturnValueHandler接口:

public interface HandlerMethodReturnValueHandler {boolean supportsReturnType(MethodParameter returnType);void handleReturnValue(@Nullable Object returnValue, MethodParameter returnType,ModelAndViewContainer mavContainer, NativeWebRequest webRequest) throws Exception;}

回顾执行目标方法

public class DispatcherServlet extends FrameworkServlet {...protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {ModelAndView mv = null;...mv = ha.handle(processedRequest, response, mappedHandler.getHandler());

springboot异步调用方法。RequestMappingHandlerAdapter的handle()方法:

public class RequestMappingHandlerAdapter extends AbstractHandlerMethodAdapterimplements BeanFactoryAware, InitializingBean {...//AbstractHandlerMethodAdapter类的方法,RequestMappingHandlerAdapter继承AbstractHandlerMethodAdapterpublic final ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler)throws Exception {return handleInternal(request, response, (HandlerMethod) handler);}@Overrideprotected ModelAndView handleInternal(HttpServletRequest request,HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {ModelAndView mav;//handleInternal的核心mav = invokeHandlerMethod(request, response, handlerMethod);//解释看下节//...return mav;}
}

RequestMappingHandlerAdapter的invokeHandlerMethod()方法:

public class RequestMappingHandlerAdapter extends AbstractHandlerMethodAdapterimplements BeanFactoryAware, InitializingBean {protected ModelAndView invokeHandlerMethod(HttpServletRequest request,HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {ServletWebRequest webRequest = new ServletWebRequest(request, response);try {...ServletInvocableHandlerMethod invocableMethod = createInvocableHandlerMethod(handlerMethod);if (this.argumentResolvers != null) {invocableMethod.setHandlerMethodArgumentResolvers(this.argumentResolvers);}if (this.returnValueHandlers != null) {invocableMethod.setHandlerMethodReturnValueHandlers(this.returnValueHandlers);}...//关注点:执行目标方法invocableMethod.invokeAndHandle(webRequest, mavContainer);if (asyncManager.isConcurrentHandlingStarted()) {return null;}return getModelAndView(mavContainer, modelFactory, webRequest);}finally {webRequest.requestCompleted();}}

invokeAndHandle()方法如下:

public class ServletInvocableHandlerMethod extends InvocableHandlerMethod {public void invokeAndHandle(ServletWebRequest webRequest, ModelAndViewContainer mavContainer,Object... providedArgs) throws Exception {Object returnValue = invokeForRequest(webRequest, mavContainer, providedArgs);...try {//returnValue存储起来this.returnValueHandlers.handleReturnValue(returnValue, getReturnValueType(returnValue), mavContainer, webRequest);}catch (Exception ex) {...}}@Nullable//InvocableHandlerMethod类的,ServletInvocableHandlerMethod类继承InvocableHandlerMethod类public Object invokeForRequest(NativeWebRequest request, @Nullable ModelAndViewContainer mavContainer,Object... providedArgs) throws Exception {获取方法的参数值Object[] args = getMethodArgumentValues(request, mavContainer, providedArgs);...return doInvoke(args);}@Nullableprotected Object doInvoke(Object... args) throws Exception {Method method = getBridgedMethod();//@RequestMapping的方法ReflectionUtils.makeAccessible(method);try {if (KotlinDetector.isSuspendingFunction(method)) {return CoroutinesUtils.invokeSuspendingFunction(method, getBean(), args);}//通过反射调用return method.invoke(getBean(), args);//getBean()指@RequestMapping的方法所在类的对象。}catch (IllegalArgumentException ex) {...}catch (InvocationTargetException ex) {...}}}   

如何确定目标方法每一个参数的值

重点分析ServletInvocableHandlerMethod的getMethodArgumentValues方法

挨个判断所有参数解析器哪个支持解析这个参数

public class ServletInvocableHandlerMethod extends InvocableHandlerMethod {...@Nullable//InvocableHandlerMethod类的,ServletInvocableHandlerMethod类继承InvocableHandlerMethod类public Object invokeForRequest(NativeWebRequest request, @Nullable ModelAndViewContainer mavContainer,Object... providedArgs) throws Exception {获取方法的参数值Object[] args = getMethodArgumentValues(request, mavContainer, providedArgs);...return doInvoke(args);}//本节重点,获取方法的参数值protected Object[] getMethodArgumentValues(NativeWebRequest request, @Nullable ModelAndViewContainer mavContainer,Object... providedArgs) throws Exception {MethodParameter[] parameters = getMethodParameters();if (ObjectUtils.isEmpty(parameters)) {return EMPTY_ARGS;}Object[] args = new Object[parameters.length];for (int i = 0; i < parameters.length; i++) {MethodParameter parameter = parameters[i];parameter.initParameterNameDiscovery(this.parameterNameDiscoverer);args[i] = findProvidedArgument(parameter, providedArgs);if (args[i] != null) {continue;}//查看resolvers是否有支持if (!this.resolvers.supportsParameter(parameter)) {throw new IllegalStateException(formatArgumentError(parameter, "No suitable resolver"));}try {//支持的话就开始解析吧args[i] = this.resolvers.resolveArgument(parameter, mavContainer, request, this.dataBinderFactory);}catch (Exception ex) {....}}return args;}}

解析这个参数的值

java获取post请求的请求体,this.resolvers的类型为HandlerMethodArgumentResolverComposite(在参数解析器章节提及)

public class HandlerMethodArgumentResolverComposite implements HandlerMethodArgumentResolver {@Overridepublic boolean supportsParameter(MethodParameter parameter) {return getArgumentResolver(parameter) != null;}@Override@Nullablepublic Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer,NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory) throws Exception {HandlerMethodArgumentResolver resolver = getArgumentResolver(parameter);if (resolver == null) {throw new IllegalArgumentException("Unsupported parameter type [" +parameter.getParameterType().getName() + "]. supportsParameter should be called first.");}return resolver.resolveArgument(parameter, mavContainer, webRequest, binderFactory);}@Nullableprivate HandlerMethodArgumentResolver getArgumentResolver(MethodParameter parameter) {HandlerMethodArgumentResolver result = this.argumentResolverCache.get(parameter);if (result == null) {//挨个判断所有参数解析器那个支持解析这个参数for (HandlerMethodArgumentResolver resolver : this.argumentResolvers) {if (resolver.supportsParameter(parameter)) {result = resolver;this.argumentResolverCache.put(parameter, result);//找到了,resolver就缓存起来,方便稍后resolveArgument()方法使用break;}}}return result;}
}

小结

本节描述,一个请求发送到DispatcherServlet后的具体处理流程,也就是SpringMVC的主要原理。

本节内容较多且硬核,对日后编程很有帮助,需耐心对待。

可以运行一个示例,打断点,在Debug模式下,查看程序流程。

Model、Map原理

WebRequest

ServletRequest

java异步请求。MultipartRequest

HttpSession

javax.servlet.http.PushBuilder

Principal

InputStream

Reader

Springboot启动流程、HttpMethod

Locale

TimeZone

ZoneId

ServletRequestMethodArgumentResolver用来处理以上的参数

public class ServletRequestMethodArgumentResolver implements HandlerMethodArgumentResolver {@Nullableprivate static Class<?> pushBuilder;static {try {pushBuilder = ClassUtils.forName("javax.servlet.http.PushBuilder",ServletRequestMethodArgumentResolver.class.getClassLoader());}catch (ClassNotFoundException ex) {// Servlet 4.0 PushBuilder not found - not supported for injectionpushBuilder = null;}}@Overridepublic boolean supportsParameter(MethodParameter parameter) {Class<?> paramType = parameter.getParameterType();return (WebRequest.class.isAssignableFrom(paramType) ||ServletRequest.class.isAssignableFrom(paramType) ||MultipartRequest.class.isAssignableFrom(paramType) ||HttpSession.class.isAssignableFrom(paramType) ||(pushBuilder != null && pushBuilder.isAssignableFrom(paramType)) ||(Principal.class.isAssignableFrom(paramType) && !parameter.hasParameterAnnotations()) ||InputStream.class.isAssignableFrom(paramType) ||Reader.class.isAssignableFrom(paramType) ||HttpMethod.class == paramType ||Locale.class == paramType ||TimeZone.class == paramType ||ZoneId.class == paramType);}@Overridepublic Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer,NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory) throws Exception {Class<?> paramType = parameter.getParameterType();// WebRequest / NativeWebRequest / ServletWebRequestif (WebRequest.class.isAssignableFrom(paramType)) {if (!paramType.isInstance(webRequest)) {throw new IllegalStateException("Current request is not of type [" + paramType.getName() + "]: " + webRequest);}return webRequest;}// ServletRequest / HttpServletRequest / MultipartRequest / MultipartHttpServletRequestif (ServletRequest.class.isAssignableFrom(paramType) || MultipartRequest.class.isAssignableFrom(paramType)) {return resolveNativeRequest(webRequest, paramType);}// HttpServletRequest required for all further argument typesreturn resolveArgument(paramType, resolveNativeRequest(webRequest, HttpServletRequest.class));}private <T> T resolveNativeRequest(NativeWebRequest webRequest, Class<T> requiredType) {T nativeRequest = webRequest.getNativeRequest(requiredType);if (nativeRequest == null) {throw new IllegalStateException("Current request is not of type [" + requiredType.getName() + "]: " + webRequest);}return nativeRequest;}@Nullableprivate Object resolveArgument(Class<?> paramType, HttpServletRequest request) throws IOException {if (HttpSession.class.isAssignableFrom(paramType)) {HttpSession session = request.getSession();if (session != null && !paramType.isInstance(session)) {throw new IllegalStateException("Current session is not of type [" + paramType.getName() + "]: " + session);}return session;}else if (pushBuilder != null && pushBuilder.isAssignableFrom(paramType)) {return PushBuilderDelegate.resolvePushBuilder(request, paramType);}else if (InputStream.class.isAssignableFrom(paramType)) {InputStream inputStream = request.getInputStream();if (inputStream != null && !paramType.isInstance(inputStream)) {throw new IllegalStateException("Request input stream is not of type [" + paramType.getName() + "]: " + inputStream);}return inputStream;}else if (Reader.class.isAssignableFrom(paramType)) {Reader reader = request.getReader();if (reader != null && !paramType.isInstance(reader)) {throw new IllegalStateException("Request body reader is not of type [" + paramType.getName() + "]: " + reader);}return reader;}else if (Principal.class.isAssignableFrom(paramType)) {Principal userPrincipal = request.getUserPrincipal();if (userPrincipal != null && !paramType.isInstance(userPrincipal)) {throw new IllegalStateException("Current user principal is not of type [" + paramType.getName() + "]: " + userPrincipal);}return userPrincipal;}else if (HttpMethod.class == paramType) {return HttpMethod.resolve(request.getMethod());}else if (Locale.class == paramType) {return RequestContextUtils.getLocale(request);}else if (TimeZone.class == paramType) {TimeZone timeZone = RequestContextUtils.getTimeZone(request);return (timeZone != null ? timeZone : TimeZone.getDefault());}else if (ZoneId.class == paramType) {TimeZone timeZone = RequestContextUtils.getTimeZone(request);return (timeZone != null ? timeZone.toZoneId() : ZoneId.systemDefault());}// Should never happen...throw new UnsupportedOperationException("Unknown parameter type: " + paramType.getName());}/*** Inner class to avoid a hard dependency on Servlet API 4.0 at runtime.*/private static class PushBuilderDelegate {@Nullablepublic static Object resolvePushBuilder(HttpServletRequest request, Class<?> paramType) {PushBuilder pushBuilder = request.newPushBuilder();if (pushBuilder != null && !paramType.isInstance(pushBuilder)) {throw new IllegalStateException("Current push builder is not of type [" + paramType.getName() + "]: " + pushBuilder);}return pushBuilder;}}
}

用例:

@Controller
public class RequestController {@GetMapping("/goto")public String goToPage(HttpServletRequest request){request.setAttribute("msg","成功了...");request.setAttribute("code",200);return "forward:/success";  //转发到  /success请求}
}

Model、Map原理

复杂参数:

Map

Model(map、model里面的数据会被放在request的请求域 request.setAttribute)

Errors/BindingResult

RedirectAttributes( 重定向携带数据)

ServletResponse(response)

SessionStatus

UriComponentsBuilder

ServletUriComponentsBuilder

用例:

@GetMapping("/params")
public String testParam(Map<String,Object> map,Model model,HttpServletRequest request,HttpServletResponse response){//下面三位都是可以给request域中放数据map.put("hello","world666");model.addAttribute("world","hello666");request.setAttribute("message","HelloWorld");Cookie cookie = new Cookie("c1","v1");response.addCookie(cookie);return "forward:/success";
}@ResponseBody
@GetMapping("/success")
public Map success(@RequestAttribute(value = "msg",required = false) String msg,@RequestAttribute(value = "code",required = false)Integer code,HttpServletRequest request){Object msg1 = request.getAttribute("msg");Map<String,Object> map = new HashMap<>();Object hello = request.getAttribute("hello");//得出testParam方法赋予的值 world666Object world = request.getAttribute("world");//得出testParam方法赋予的值 hello666Object message = request.getAttribute("message");//得出testParam方法赋予的值 HelloWorldmap.put("reqMethod_msg",msg1);map.put("annotation_msg",msg);map.put("hello",hello);map.put("world",world);map.put("message",message);return map;
}

在这里插入图片描述
在这里插入图片描述

Map<String,Object> map

Model model

HttpServletRequest request

上面三位都是可以给request域中放数据,用request.getAttribute()获取

接下来我们看看,Map<String,Object> map与Model model用什么参数处理器。

Map<String,Object> map参数用MapMethodProcessor处理:
public class MapMethodProcessor implements HandlerMethodArgumentResolver, HandlerMethodReturnValueHandler {@Overridepublic boolean supportsParameter(MethodParameter parameter) {return (Map.class.isAssignableFrom(parameter.getParameterType()) &&parameter.getParameterAnnotations().length == 0);}@Override@Nullablepublic 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();}...}

Map类型的参数,会返回mavContainer.getModel().它之前用的是MapMethodProcesser
mavContainer.getModel()如下:

public class ModelAndViewContainer {...private final ModelMap defaultModel = new BindingAwareModelMap();@Nullableprivate ModelMap redirectModel;...public ModelMap getModel() {if (useDefaultModel()) {return this.defaultModel;}else {if (this.redirectModel == null) {this.redirectModel = new ModelMap();}return this.redirectModel;}}private boolean useDefaultModel() {return (!this.redirectModelScenario || (this.redirectModel == null && !this.ignoreDefaultModelOnRedirect));}...}

Model model用ModelMethodProcessor处理:

public class ModelMethodProcessor implements HandlerMethodArgumentResolver, HandlerMethodReturnValueHandler {@Overridepublic boolean supportsParameter(MethodParameter parameter) {return Model.class.isAssignableFrom(parameter.getParameterType());}@Override@Nullablepublic 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();}...
}

return mavContainer.getModel();这跟MapMethodProcessor的一致

在这里插入图片描述
Model也是另一种意义的Map

接下来看看Map<String,Object> map与Model model值是如何做到用request.getAttribute()获取的

众所周知,所有的数据都放在 ModelAndView包含要去的页面地址View,还包含Model数据。

先看ModelAndView接下来是如何处理的?

public class DispatcherServlet extends FrameworkServlet {...protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {...try {ModelAndView mv = null;...// Actually invoke the handler.mv = ha.handle(processedRequest, response, mappedHandler.getHandler());...}catch (Exception ex) {dispatchException = ex;}catch (Throwable err) {// As of 4.3, we're processing Errors thrown from handler methods as well,// making them available for @ExceptionHandler methods and other scenarios.dispatchException = new NestedServletException("Handler dispatch failed", err);}//处理分发结果processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);}...}private void processDispatchResult(HttpServletRequest request, HttpServletResponse response,@Nullable HandlerExecutionChain mappedHandler, @Nullable ModelAndView mv,@Nullable Exception exception) throws Exception {...// Did the handler return a view to render?if (mv != null && !mv.wasCleared()) {render(mv, request, response);...}...}protected void render(ModelAndView mv, HttpServletRequest request, HttpServletResponse response) throws Exception {...View view;String viewName = mv.getViewName();if (viewName != null) {// We need to resolve the view name.view = resolveViewName(viewName, mv.getModelInternal(), locale, request);if (view == null) {throw new ServletException("Could not resolve view with name '" + mv.getViewName() +"' in servlet with name '" + getServletName() + "'");}}else {// No need to lookup: the ModelAndView object contains the actual View object.view = mv.getView();if (view == null) {throw new ServletException("ModelAndView [" + mv + "] neither contains a view name nor a " +"View object in servlet with name '" + getServletName() + "'");}}view.render(mv.getModelInternal(), request, response);...}}

在Debug模式下,view属为InternalResourceView类。

public class InternalResourceView extends AbstractUrlBasedView {@Override//该方法在AbstractView,AbstractUrlBasedView继承了AbstractViewpublic void render(@Nullable Map<String, ?> model, HttpServletRequest request,HttpServletResponse response) throws Exception {...Map<String, Object> mergedModel = createMergedOutputModel(model, request, response);prepareResponse(request, response);//看下一个方法实现renderMergedOutputModel(mergedModel, getRequestToExpose(request), response);}@Overrideprotected 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);...}//该方法在AbstractView,AbstractUrlBasedView继承了AbstractViewprotected 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);}});}}

exposeModelAsRequestAttributes方法看出,Map<String,Object> map,Model model这两种类型数据可以给request域中放数据,用request.getAttribute()获取。

自定义对象参数

我们希望页面提交的数据能够直接被封装成Person对象,也支持级联属性的写法

index.html

测试封装POJO:
<form action="/saveUser" method="post">姓名: <input name="userName"/> <br/>年龄: <input name="age"/> <br/>生日: <input name="birth"/> <br/>宠物姓名:<input name="pet.name"/><br/>宠物年龄:<input name="pet.age"/><input type="submit" value="保存"/>
</form>

pojo文件


@Data
public class Person {private String userName;private Integer age;private Date birth;private Pet pet;}@Data
public class Pet {private String name;private String age;}result

controller文件

@PostMapping("/saveUser")public Person saveUser(Person person){return person;}

运行结果
在这里插入图片描述
数据绑定:页面提交的请求数据(GET、POST)都可以和对象属性进行绑定

封装过程用到ServletModelAttributeMethodProcessor

public class ServletModelAttributeMethodProcessor extends ModelAttributeMethodProcessor {@Override//本方法在ModelAttributeMethodProcessor类,public boolean supportsParameter(MethodParameter parameter) {return (parameter.hasParameterAnnotation(ModelAttribute.class) ||(this.annotationNotRequired && !BeanUtils.isSimpleProperty(parameter.getParameterType())));}@Override@Nullable//本方法在ModelAttributeMethodProcessor类,public final Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer,NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory) throws Exception {...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 instancetry {attribute = createAttribute(name, parameter, binderFactory, webRequest);}catch (BindException ex) {...}}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)) {//web数据绑定器,将请求参数的值绑定到指定的JavaBean里面**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.Optionalif (!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 modelMap<String, Object> bindingResultModel = bindingResult.getModel();mavContainer.removeAttributes(bindingResultModel);mavContainer.addAllAttributes(bindingResultModel);return attribute;}
}

WebDataBinder 利用它里面的 Converters 将请求数据转成指定的数据类型。再次封装到JavaBean中

在过程当中,用到GenericConversionService:在设置每一个值的时候,找它里面的所有converter那个可以将这个数据类型(request带来参数的字符串)转换到指定的类型

WebDataBinder binder =binderFactory.createBinder(webRequest,attribute,name):
WebDataBinder:web数据绑定器,将请求参数的值板顶到指定的Javabean里面
WebDataBinder利用它里面的Converters将请求数据转成指定的数据类型,再次封装到Javabean中

GenericConversionService:在设置每一个值的时候,找他里面所有converter那个可以将这个数据类型(request带来参数的字符串)转换成指定的类型(JavaBean----Integer)
byte------->file

自定义Converter原理

未来我们可以给WebDataBinder里面放自己的Converter;

下面演示将字符串“啊猫,3”转换成Pet对象。

也就是将宠物这个里面的属性以逗号分隔的方式传入进去。
<input type=“pet” value="阿猫,3”/>

如果不加转换器
在这里插入图片描述
出现了属性绑定异常。

//1、WebMvcConfigurer定制化SpringMVC的功能
@Bean
public WebMvcConfigurer webMvcConfigurer(){return new WebMvcConfigurer() {@Overridepublic void addFormatters(FormatterRegistry registry) {registry.addConverter(new Converter<String, Pet>() {@Overridepublic Pet convert(String source) {// 啊猫,3if(!StringUtils.isEmpty(source)){Pet pet = new Pet();String[] split = source.split(",");pet.setName(split[0]);pet.setAge(Integer.parseInt(split[1]));return pet;}return null;}});}};
}

版权声明:本站所有资料均为网友推荐收集整理而来,仅供学习和研究交流使用。

原文链接:https://hbdhgg.com/1/101521.html

发表评论:

本站为非赢利网站,部分文章来源或改编自互联网及其他公众平台,主要目的在于分享信息,版权归原作者所有,内容仅供读者参考,如有侵权请联系我们删除!

Copyright © 2022 匯編語言學習筆記 Inc. 保留所有权利。

底部版权信息