SpringBoot2基础-请求参数处理和原理

SpringBoot2基础-请求参数处理和原理

tags:

  • Spring Boot
  • 2021尚硅谷
  • 雷丰阳

categories:

  • 静态文件配置
  • 静态文件配置原理
  • 欢迎页和自定义 Favicon
  • Rest表单请求原理
  • 请求映射原理
  • 各种参数使用和原理

文章目录

  • SpringBoot2基础-请求参数处理和原理
    • 第一节 [SpringMVC](https://so.csdn.net/so/search?q=SpringMVC&spm=1001.2101.3001.7020)自动配置概览
    • 第二节 静态资源访问
      • 2.1 静态资源目录
      • 2.2 欢迎页和自定义 Favicon
      • 2.3 静态资源配置流程
      • 2.4 资源处理的默认规则
    • 第三节 请求参数处理-请求映射
      • 3.1 rest使用与原理
      • 3.2 请求映射原理
    • 第四节 请求参数处理-普通参数和基本注解
      • 4.1 注解方式-常用注解
      • 4.2 注解方式- **@RequestAttribute**
      • 4.3 注解方式-**矩阵变量@MatrixVariable使用**
      • 4.4 各种参数解析原理
      • 4.2 Servlet API
      • 4.3 复杂参数
      • 4.4 自定义类型的参数对象

在这里插入图片描述

第一节 SpringMVC自动配置概览

  1. SpringBoot封装了SpringMVC, SpringBoot中大多场景我们都无需自定义配置。
  2. SpringBoot自动配置了哪些SpringMVC中哪些东西。官方文档:https://docs.spring.io/spring-boot/docs/current/reference/html/spring-boot-features.html#boot-features-spring-mvc-auto-configuration
    • 内容协商视图解析器和BeanName视图解析器
    • 静态资源(包括webjars)
    • 自动注册 Converter,GenericConverter,Formatter。比如:日期格式等自动转换
    • 支持 HttpMessageConverters (后来我们配合内容协商理解原理)
    • 自动注册 MessageCodesResolver (国际化用的) 用处不大,如果真的要用到国际化,一般开发两个网站。
    • 静态index.html 页支持
    • 自定义 Favicon
    • 自动使用 ConfigurableWebBindingInitializer ,(DataBinder负责将请求数据绑定到JavaBean上)
    • 不用@EnableWebMvc注解。使用 @Configuration + WebMvcConfigurer 自定义规则
    • 声明 WebMvcRegistrations 改变默认底层组件
    • 使用 @EnableWebMvc+@Configuration+DelegatingWebMvcConfiguration 全面接管SpringMVC

第二节 静态资源访问

  1. 官方文档:https://docs.spring.io/spring-boot/docs/current/reference/html/spring-boot-features.html#boot-features-spring-mvc-static-content

2.1 静态资源目录

  1. 只要静态资源放在类路径下: called /static (or /public or /resources or /META-INF/resources

    访问 : 当前项目根路径/ + 静态资源名

    • static
    • public
    • resources
    • META-INF/resources
  2. 原理: 请求进来,先去找Controller看能不能处理。不能处理的所有请求又都交给静态资源处理器。静态资源也找不到则响应404页面

  3. 修改静态资源访问的前缀

    • 当前项目 + static-path-pattern + 静态资源名 = 静态资源文件夹下找:http://127.0.0.1:8080/res/aaaaa.jpg
  4. 改变默认的静态资源路径

spring:mvc:static-path-pattern: /res/** # 配置访问前缀web:resources:static-locations: [classpath:/haha/] # 配置静态资源路径,可以写一个数组,也可以只写一个
  1. webjar 这个

    用的比较少

    。相当于jquery弄成一个jar包。通过依赖引用。

    • 自动映射 :访问资源http://localhost:8080/webjars/jquery/3.5.1/jquery.js
    • https://www.webjars.org/
        <dependency><groupId>org.webjars</groupId><artifactId>jquery</artifactId><version>3.5.1</version></dependency>

2.2 欢迎页和自定义 Favicon

  1. 静态资源路径下 index.html
    • 可以配置静态资源路径
    • 但是不可以配置静态资源的访问前缀。否则导致 index.html不能被默认访问
  2. controller能处理/index
spring:
#  mvc:
#    static-path-pattern: /res/**   这个会导致welcome page功能失效
  1. 自定义 Favicon
    • favicon.ico 放在静态资源目录下即可。
spring:
#  mvc:
#    static-path-pattern: /res/**   这个会导致自定义 Favicon配置时效

2.3 静态资源配置流程

  1. 首先,SpringBoot启动默认加载 xxxAutoConfiguration 类(自动配置类)

  2. SpringMVC功能的自动配置类 WebMvcAutoConfiguration,判断生效

  3. 看下给容器中配了什么。

    • org\springframework\boot\spring-boot-autoconfigure\2.4.5\spring-boot-autoconfigure-2.4.5-sources.jar!\org\springframework\boot\autoconfigure\web\servlet\WebMvcAutoConfiguration.java
  4. 配置类中的配置类

    如下。配置文件的相关属性和xxx进行了绑定

    • 发现WebMvcProperties和spring.mvc配置文件进行绑定、
    • 发现ResourceProperties和spring.resources配置进行绑定
	@Configuration(proxyBeanMethods = false)@Import(EnableWebMvcConfiguration.class)@EnableConfigurationProperties({ WebMvcProperties.class,org.springframework.boot.autoconfigure.web.ResourceProperties.class, WebProperties.class })@Order(0)public static class WebMvcAutoConfigurationAdapter implements WebMvcConfigurer {
  1. 拓展知识:如果一个配置类只有一个有参构造器,那么有参构造器所有参数的值都会从容器中确定
 //有参构造器所有参数的值都会从容器中确定
//ResourceProperties resourceProperties;获取和spring.resources绑定的所有的值的对象
//WebMvcProperties mvcProperties 获取和spring.mvc绑定的所有的值的对象
//ListableBeanFactory beanFactory Spring的beanFactory, 容器工厂
//HttpMessageConverters 找到所有的HttpMessageConverters
//ResourceHandlerRegistrationCustomizer 找到资源处理器的自定义器。===着重===
//DispatcherServletPath  DispatcherServlet处理的路径
//ServletRegistrationBean   给应用注册Servlet、Filter....public WebMvcAutoConfigurationAdapter(ResourceProperties resourceProperties, WebMvcProperties mvcProperties,ListableBeanFactory beanFactory, ObjectProvider<HttpMessageConverters> messageConvertersProvider,ObjectProvider<ResourceHandlerRegistrationCustomizer> resourceHandlerRegistrationCustomizerProvider,ObjectProvider<DispatcherServletPath> dispatcherServletPath,ObjectProvider<ServletRegistrationBean<?>> servletRegistrations) {this.resourceProperties = resourceProperties;this.mvcProperties = mvcProperties;this.beanFactory = beanFactory;this.messageConvertersProvider = messageConvertersProvider;this.resourceHandlerRegistrationCustomizer = resourceHandlerRegistrationCustomizerProvider.getIfAvailable();this.dispatcherServletPath = dispatcherServletPath;this.servletRegistrations = servletRegistrations;}
  1. 代码朝下找:找到函数addResourceHandlers 它处理资源处理的默认规则

2.4 资源处理的默认规则

  1. 函数addResourceHandlers, 在if上加断点看下默认规则怎么生效的。(我的是最新的框架,一些函数可能和视屏不一致)
    • 上面从容器中获得的这个resourceProperties(resource的配置文件),有isAddMappings属性。到配置文件中测试下这个属性的作用。
    • 发现默认为True, 如果配置成False下面一堆配置不生效。
		@Overrideprotected void addResourceHandlers(ResourceHandlerRegistry registry) {super.addResourceHandlers(registry);if (!this.resourceProperties.isAddMappings()) {logger.debug("Default resource handling disabled");return;}ServletContext servletContext = getServletContext();// webjars的规则addResourceHandler(registry, "/webjars/**", "classpath:/META-INF/resources/webjars/");// resouseraddResourceHandler(registry, this.mvcProperties.getStaticPathPattern(), (registration) -> {registration.addResourceLocations(this.resourceProperties.getStaticLocations());if (servletContext != null) {registration.addResourceLocations(new ServletContextResource(servletContext, SERVLET_LOCATION));}});}
  web:resources:static-locations: [classpath:/haha/]add-mappings: false # 禁用所有的静态资源配置cache:period: 11000 # 配置静态资源的缓存时间 以秒为单位
  1. 它调用了addResourceHandler,这个里面设置了静态资源的缓存
  2. 找静态资源的默认位置。resourceProperties.getStaticLocations()
		private static final String[] CLASSPATH_RESOURCE_LOCATIONS = { "classpath:/META-INF/resources/","classpath:/resources/", "classpath:/static/", "classpath:/public/" };
  1. 欢迎页面的函数WelcomePageHandlerMapping
// HandlerMapping:处理器映射。保存了每一个Handler能处理哪些请求。@Beanpublic WelcomePageHandlerMapping welcomePageHandlerMapping(ApplicationContext applicationContext,FormattingConversionService mvcConversionService, ResourceUrlProvider mvcResourceUrlProvider) {WelcomePageHandlerMapping welcomePageHandlerMapping = new WelcomePageHandlerMapping(new TemplateAvailabilityProviders(applicationContext), applicationContext, getWelcomePage(),this.mvcProperties.getStaticPathPattern());welcomePageHandlerMapping.setInterceptors(getInterceptors(mvcConversionService, mvcResourceUrlProvider));welcomePageHandlerMapping.setCorsConfigurations(getCorsConfigurations());return welcomePageHandlerMapping;}// 上面代码点入这个WelcomePageHandlerMapping, 第一个if也解释了上面定义路径欢迎页找不到因为 "/**".equals(staticPathPattern)WelcomePageHandlerMapping(TemplateAvailabilityProviders templateAvailabilityProviders,ApplicationContext applicationContext, Resource welcomePage, String staticPathPattern) {if (welcomePage != null && "/**".equals(staticPathPattern)) {logger.info("Adding welcome page: " + welcomePage);setRootViewName("forward:index.html");}else if (welcomeTemplateExists(templateAvailabilityProviders, applicationContext)) {logger.info("Adding welcome page template: index");setRootViewName("index");}}
  1. favicon这个和我们的代码没什么关系了,浏览器会默认发/favicon.ico

第三节 请求参数处理-请求映射

3.1 rest使用与原理

  1. 表单Rest风格支持(使用HTTP请求方式动词来表示对资源的操作)

    • 以前:/getUser 获取用户 /deleteUser 删除用户 /editUser 修改用户 /saveUser 保存用户
    • 现在: /user GET-获取用户 DELETE-删除用户 PUT-修改用户 POST-保存用户
  2. 前端表单中的method并不支持delete和put, 默认用get处理

    ,如何让它可以处理我们

    表单

    中的delete和put。

    • 第一步:给我们带一个DEFAULT_METHOD_PARAM = "_method"的隐藏字段可以。
    • 第二步:开启配置手动开启,hiddenHttpMethodFilter配置(默认不开启)
<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>
12345678
# 开启配置手动开启mvc:hiddenmethod:filter:enabled: true
  1. 函数流程:WebMvcAutoConfiguration中找到hiddenHttpMethodFilter,点击去->在点到父类中。找到org.springframework.web.filter.HiddenHttpMethodFilter的DEFAULT_METHOD_PARAM配置。
// 测试代码//@RequestMapping(value = "/user",method = RequestMethod.GET)// 简写@GetMapping("/user")public String getUser(){return "GET-张三";}//@RequestMapping(value = "/user",method = RequestMethod.POST)// 简写//@PostMapping("/user")public String saveUser(){return "POST-张三";}//@RequestMapping(value = "/user",method = RequestMethod.PUT)// 简写@PutMapping("/user")public String putUser(){return "PUT-张三";}//@RequestMapping(value = "/user",method = RequestMethod.DELETE)// 简写@DeleteMapping("/user")public String deleteUser(){return "DELETE-张三";}
  1. Rest原理(表单提交要使用REST的时候)
    • 表单提交会带上_method=PUT
    • 请求过来被HiddenHttpMethodFilter拦截
    • 请求是否正常,并且是POST
    • 获取到_method的值。兼容以下请求;PUT.DELETE.PATCH (传过来的值不区分大小写)
    • 原生request(post),包装模式requesWrapper重写了getMethod方法,返回的是传入的值。
    • 过滤器链放行的时候用wrapper。以后的方法调用getMethod是调用requesWrapper的。
	@Overrideprotected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)throws ServletException, IOException {HttpServletRequest requestToUse = request;// 这里判断必须用POST提交 而且没有错误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)) {// 包装模式requesWrappe重写了getMethod方法requestToUse = new HttpMethodRequestWrapper(request, method);}}}// 过滤器链放行的时候用wrapper。以后的方法调用getMethod是调用requesWrapper的filterChain.doFilter(requestToUse, response);}
  1. Rest使用客户端工具

    ,上面只是指的是

    表单请求

    • 如PostMan直接发送Put、delete等方式请求,无需Filter
    • 所以它选择开启,我们实际不会用它做页面。都是前后端分离的模式,页面都是别人写的,只要掉我们接口就可以了。
  2. 如果我们不想用_method而想,自己定义一个隐藏字段比如:_m。写一个配置类修改,重新启动工程。

package com.atguigu.boot.config;import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.filter.HiddenHttpMethodFilter;// 没有依赖 效率高
@Configuration(proxyBeanMethods = false)
public class WebConfig {@Beanpublic HiddenHttpMethodFilter hiddenHttpMethodFilter(){HiddenHttpMethodFilter methodFilter = new HiddenHttpMethodFilter();// 修改成_m 之前的_method就不可以用了methodFilter.setMethodParam("_m");return methodFilter;}
}

3.2 请求映射原理

  1. CTRL+N全局搜索类DispatcherServlet,CTRL+H搜索继承树。去找重写原生HTTPServerlet的doget和dopost请求的方法。在FrameworkServlet中可以找到。
  2. 调用过程:doGet -> processRequest -> doService(抽象) -> 子类的doService(DispatcherServlet) -> doDispatch(这才是我们要研究的方法,每个请求都经过它)
    在这里插入图片描述
  3. SpringMVC功能分析都从 org.springframework.web.servlet.DispatcherServlet的doDispatch方法。断点打在这个函数上。运行。
    • 发现:getHandler(processedRequest)是确定请求用哪个Handler处理器处理的方法
    • 点击去:this.handlerMappings处理器映射(所有的请求映射都在HandlerMapping中),默认有五个。展开后可以看到一些细节。
      • 0中是RequestMappingHandlerMapping:保存了所有@RequestMapping 和handler的映射规则。
      • 1中是SpringBoot自动配置欢迎页的 WelcomePageHandlerMapping 。访问 /能访问到index.html;
        在这里插入图片描述在这里插入图片描述
  4. 配置类org.springframework.boot.autoconfigure.web.servlet.WebMvcAutoConfiguration中。requestMappingHandlerMapping注册了处理标了注解的方法。
    • 请求进来,挨个尝试所有的HandlerMapping看是否有请求信息。
    • 如果有就找到这个请求对应的handler
    • 如果没有就是下一个 HandlerMapping
  5. 我们需要一些自定义的映射处理,我们也可以自己给容器中放HandlerMapping。自定义 HandlerMapping

第四节 请求参数处理-普通参数和基本注解

4.1 注解方式-常用注解

  1. @PathVariable、@RequestHeader、@ModelAttribute、@RequestParam、@MatrixVariable、@CookieValue、@RequestBody
    //  car/2/owner/zhangsan@GetMapping("/car/{id}/owner/{username}")public Map<String, Object> getCar(// 路径变量id@PathVariable("id") Integer id,// 路径变量username@PathVariable("username") String name,// 获取所有的路径变量@PathVariable Map<String,String> pv,//获取请求头中User-Agent@RequestHeader("User-Agent") String userAgent,//获取所有请求头@RequestHeader Map<String,String> header,// 获取请求参数age ?age=18@RequestParam("age") Integer age,// 获取请求参数inters@RequestParam("inters") List<String> inters,// 获取所有的请求参数@RequestParam Map<String,String> params,// 获取cookie中的_ga的值@CookieValue("_ga") String _ga,// 获取cookie中所有信息@CookieValue("_ga") 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",header);map.put("age",age);map.put("inters",inters);map.put("params",params);map.put("_ga",_ga);System.out.println(cookie.getName()+"===>"+cookie.getValue());return map;}// 获取请求体中的值, 只有Post方式有请求体@PostMapping("/save")public Map postMethod(@RequestBody String content){Map<String,Object> map = new HashMap<>();map.put("content",content);return map;}

4.2 注解方式- @RequestAttribute

  1. @RequestAttribute
@Controller // 普通的控制器,方法的返回 是要进行跳转的
public class RequestController {@GetMapping("/goto")public String goToPage(HttpServletRequest request){request.setAttribute("msg","成功了...");request.setAttribute("code",200);return "forward:/success";  //转发到  /success请求}@GetMapping("/params")public String testParam(Map<String,Object> map,Model model,HttpServletRequest request,HttpServletResponse response){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(// 获取请求域中的msg 上面转发的时设置的属性 required = false请求域中这个属性不是必须的@RequestAttribute(value = "msg",required = false) String msg,// 这个没写可以获取全部属性 点进去看看@RequestAttribute(value = "code",required = false)Integer code,// 通过原生请求获取requestHttpServletRequest request){Object msg1 = request.getAttribute("msg");Map<String,Object> map = new HashMap<>();Object hello = request.getAttribute("hello");Object world = request.getAttribute("world");Object message = request.getAttribute("message");map.put("reqMethod_msg",msg1);map.put("annotation_msg",msg);map.put("hello",hello);map.put("world",world);map.put("message",message);return map;}
}

4.3 注解方式-矩阵变量@MatrixVariable使用

  1. 矩阵变量@MatrixVariable使用

    • 语法: 请求路径:/cars/sell;low=34;brand=byd,audi,yd

    • 面试题:页面开发,cookie禁用了,session里面的内容怎么使用;

      • 默认:session.set(a,b)—> jsessionid —> cookie ----> 每次发请求携带。
      • 可以使用url重写:/abc;jsesssionid=xxxx 把cookie的值使用矩阵变量的方式进行传递.
    • SpringBoot默认是禁用了矩阵变量的功能

      .

      • 自动配置类中configurePathMatch这个进行处理。
      • 手动开启:原理。对于路径的处理。UrlPathHelper进行解析,点进去发现。removeSemicolonContent = true;(移除分号内容)支持矩阵变量。
<a href="/cars/sell;low=34;brand=byd,audi,yd">@MatrixVariable(矩阵变量)</a>
<a href="/cars/sell;low=34;brand=byd;brand=audi;brand=yd">@MatrixVariable(矩阵变量)</a>
<a href="/boss/1;age=20/2;age=10">@MatrixVariable(矩阵变量)/boss/{bossId}/{empId}</a>
  1. 手动开启矩阵变量的功能,两种方式
    • 第一种写法:@Bean 给容器中直接放入WebMvcConfigurer组件
    • 第二种写法:实现WebMvcConfigurer,因为有默认实现只用修改需要修改的方法。
// 第一种写法: 给容器中放入WebMvcConfigurer组件
@Configuration(proxyBeanMethods = false)
public class WebConfig {@Beanpublic HiddenHttpMethodFilter hiddenHttpMethodFilter(){HiddenHttpMethodFilter methodFilter = new HiddenHttpMethodFilter();methodFilter.setMethodParam("_m");return methodFilter;}@Beanpublic  WebMvcConfigurer webMvcConfigurer(){return new WebMvcConfigurer() {@Overridepublic void configurePathMatch(PathMatchConfigurer configurer) {UrlPathHelper urlPathHelper = new UrlPathHelper();urlPathHelper.setRemoveSemicolonContent(false);configurer.setUrlPathHelper(urlPathHelper);}};}
}// 第二种写法 实现WebMvcConfigurer
@Configuration(proxyBeanMethods = false)
public class WebConfig implements WebMvcConfigurer {@Beanpublic HiddenHttpMethodFilter hiddenHttpMethodFilter(){HiddenHttpMethodFilter methodFilter = new HiddenHttpMethodFilter();methodFilter.setMethodParam("_m");return methodFilter;}@Overridepublic void configurePathMatch(PathMatchConfigurer configurer) {UrlPathHelper urlPathHelper = new UrlPathHelper();urlPathHelper.setRemoveSemicolonContent(false);configurer.setUrlPathHelper(urlPathHelper);}
}
  1. 矩阵变量必须有url路径变量才能被解析:要写成路径变量的表示方法{path}
    @GetMapping("/cars/{path}")public Map carsSell(@MatrixVariable("low") Integer low,@MatrixVariable("brand") List<String> brand,// 获取真正的访问路径就是sell@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;}

4.4 各种参数解析原理

  1. 初始:和之前一样依旧CTRL+N, 搜org.springframework.web.servlet.DispatcherServlet下的doDispatch。打断点调试。

    • http://127.0.0.1:8080/car/3/owner/lisi?age=18&inters=basketball&inters=game
  2. 第一步:mappedHandler = getHandler(processedRequest);点进去,发现HandlerMapping中找到能处理请求的Handler(Controller.method())

    • mappedHandler = getHandler(processedRequest);
  3. 第二步:为当前Handler 找一个适配器 HandlerAdapter; RequestMappingHandlerAdapter

    • HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());

    • HandlerAdapter是SpringMVC底层设计的接口。两个重要函数1. 支持接口,2. 调用处理

    • getHandlerAdapter点击去,在所有的HandlerAdapter中

      确定共有四种

      • 0 - 支持方法上标注@RequestMapping (默认)
      • 1 - 支持函数式编程的
      • xxxxxx
  4. 第三步:执行目标方法. 还是在DispatcherServlet中的 doDispatch

    • mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
    • 先追综getHandle,在连续追踪handle。找到org.springframework.web.servlet.ModelAndView。
      • 执行目标方法:mav = invokeHandlerMethod(request, response, handlerMethod);
      • 继续追进去看下目标方法怎么执行。发现字段,发现参数解析器和返回值处理器
        • argumentResolvers参数解析器。27种
        • 确定将要执行的目标方法的每一个参数的值是什么;
        • SpringMVC目标方法能写多少种参数类型。取决于参数解析器。
        • returnValueHandlers返回值处理器。15种
          在这里插入图片描述
  5. 第四步:真正执行目标方法。

    • 通过把invocableMethod.invokeAndHandle(webRequest, mavContainer);。继续点进去。
    • Object returnValue = invokeForRequest(webRequest, mavContainer, providedArgs);这个会先回到我们的Controller然后在回到下面函数。setResponseStatus(webRequest);
      • 继续点进去invokeForRequest看怎么执行控制器方法的。
        • 获取方法参数值:Object[] args = getMethodArgumentValues(request, mavContainer, providedArgs);
        • 继续点进去getMethodArgumentValues可以看到:如何获取参数值
          在这里插入图片描述
  6. 第五步:如何获取参数值。

    • 获取每个参数的详细信息:MethodParameter[] parameters = getMethodParameters();
    • 声明一个参数长度相同的args,最终把它返回。Object[] args = new Object[parameters.length];
    • 遍历参数:确定每一个参数参数解析器是否支持。if (!this.resolvers.supportsParameter(parameter)) 。具体是增强for循环一个个判断所有参数解析器的。
    • 解析参数值:确定参数支持后通过args[i] = this.resolvers.resolveArgument(parameter, mavContainer, request, this.dataBinderFactory);解析参数值。点进去。
    • AbstractNamedValueMethodArgumentResolver中resolveArgument是具体解析函数
  7. 第六步:目标方法执行完成

    • 将所有的数据都放在mavContainer;包含要去的页面地址View。还包含Model数据
      在这里插入图片描述
  8. 第七步:处理派发结果

    • org.springframework.web.servlet.DispatcherServlet#processDispatchResult
    • processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
    • 一直追踪到视图解析方法:InternalResourceView:
      -org.springframework.web.servlet.view.InternalResourceView#renderMergedOutputModel

4.2 Servlet API

  1. WebRequest、ServletRequest、MultipartRequest、 HttpSession、javax.servlet.http.PushBuilder、Principal、InputStream、Reader、HttpMethod、Locale、TimeZone、ZoneId
  2. 如下函数参数就是:HttpServletRequest request 类型。
    @GetMapping("/goto")public String goToPage(HttpServletRequest request){request.setAttribute("msg","成功了...");request.setAttribute("code",200);return "forward:/success";  //转发到  /success请求}
  1. 访问路径打断点。看下流程。
    • org.springframework.web.method.support.HandlerMethodArgumentResolverComposite#getArgumentResolver中解析到ServletRequestMethodArgumentResolver对象
    • 点击进去发现下面:
@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);}

4.3 复杂参数

  1. Map、Model(map、model里面的数据会被放在request的请求域 request.setAttribute)、Errors/BindingResult、RedirectAttributes( 重定向携带数据)、ServletResponse(response)、SessionStatus、UriComponentsBuilder、ServletUriComponentsBuilder
// Map<String,Object> map,  Model model, HttpServletRequest request 都是可以给request域中放数据,最后通过request.getAttribute()获取@GetMapping("/params")public String testParam(Map<String,Object> map,Model model,HttpServletRequest request,HttpServletResponse response){map.put("hello","world666");model.addAttribute("world","hello666");request.setAttribute("message","HelloWorld");Cookie cookie = new Cookie("c1","v1");response.addCookie(cookie);return "forward:/success";}
  1. 和上面分析一样断点追踪:
    • Map、Model类型的参数,底层都会返回 mavContainer.getModel();
    • BindingAwareModelMap 是Model 也是Map,mavContainer.getModel(); 获取到值的

4.4 自定义类型的参数对象

  1. 可以自动类型转换与格式化,可以级联封装。
package com.atguigu.boot.bean;import lombok.Data;import java.util.Date;/***     姓名: <input name="userName"/> <br/>*     年龄: <input name="age"/> <br/>*     生日: <input name="birth"/> <br/>*     宠物姓名:<input name="pet.name"/><br/>*     宠物年龄:<input name="pet.age"/>*/
@Data
public class Person {private String userName;private Integer age;private Date birth;private Pet pet;}@Data
public class Pet {private String name;private Integer age;}
  1. 先运行一下让index.html出来,在加断点点击表单请求。断点看下原理, Person怎么把页面中的数据跟我们Person的每个属性一一绑定。
    @PostMapping("/saveuser")public Person saveuser(Person person){return person;}
  1. 确定是ServletModelAttributeMethodProcessor这个参数处理器完成的。

  2. WebDataBinder :web数据绑定器

    ,将请求参数的值绑定到指定的JavaBean里面

    • WebDataBinder binder = binderFactory.createBinder(webRequest, attribute, name);
    • WebDataBinder 利用它里面的 Converters 将请求数据转成指定的数据类型。再次封装到JavaBean中
    • GenericConversionService:在设置每一个值的时候,找它里面的所有converter那个可以将这个数据类型(request带来参数的字符串)转换到指定的类型(JavaBean – Integer)
      byte – > file
  3. 添加自定义转换器让它可以识别 这种对象

  // 添加自定义转换器让它可以识别 <input name="pet" value="啊猫,3"/> 这种对象@Overridepublic void addFormatters(FormatterRegistry registry) {registry.addConverter(new Converter<String, Pet>() {@Overridepublic Pet convert(String source) {// 啊猫,3if(StringUtils.hasLength(source)){Pet pet = new Pet();String[] split = source.split(",");pet.setName(split[0]);pet.setAge(Integer.parseInt(split[1]));return pet;}return null;}});}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/555763.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Spring MVC 执行过程原理(请求映射原理、参数处理原理、返回值处理器)

Spring MVC 执行过程分析 文章目录Spring MVC 执行过程分析请求映射原理适配器Adapter执行目标方法参数处理器解析器HandlerMethodArgumentResolverRequestBody测试RequestParam测试RequestParamMethodArgumentResolver分析执行目标方法体返回值处理器解析器目标方法执行完成处…

推箱子android源代码,android自定义view实现推箱子小游戏

本文实例为大家分享了android推箱子游戏的具体实现代码&#xff0c;供大家参考&#xff0c;具体内容如下自定义view&#xff1a;package com.jisai.materialdesigndemo.tuixiangzhi;import android.content.Context;import android.graphics.Bitmap;import android.graphics.Bi…

android没有捂脸表情,微信新emoji表情安卓机怎么没有?微信新emoji表情安卓机没有怎么回事?...

在最新版ios版微信中不少用户在聊天时发现了新emoji表情哟&#xff0c;但是安卓机最新版即没有发现&#xff0c;那么微信新emoji表情安卓机怎么没有&#xff1f;微信新emoji表情安卓机没有怎么回事&#xff1f;下面小编就给大家具体介绍下。iOS版微信总共加入了9款新表情&#…

springboot源码分析: 请求方式+请求映射原理+获取参数原理

注解相关 AliasFor&#xff1a;.在同个注解中为同一个功能定义两个名称不一样的属性&#xff0c;那么这两个属性彼此互为别名 RequestMapping注解里面的代码 AliasFor("path")String[] value() default {};AliasFor("value")String[] path() default {};G…

android 或者vide的高度和宽度,关于Android中videoView.setVideoPath(“PATH”)的问题!!!急!!...

满意答案yfdsan31972015.06.03采纳率&#xff1a;52% 等级&#xff1a;9已帮助&#xff1a;364人path 是获取的路径&#xff0c;如果你把视频文件夹写在raw文件夹下 &#xff0c;/*** raw文件夹下的文件处理工具类** */public class RawFileUtils {private RawFileUtils( ){…

吃透Java IO:字节流、字符流、缓冲流

文章目录前言1 初识Java IO1.1 IO流分类1.2 案例实操2 IO流对象2.1 File类2.2 字节流2.3 字符流2.4 序列化3 IO流方法3.1 字节流方法3.2 字符流方法4 附加内容4.1 位、字节、字符4.2 IO流效率对比4.3 NIO前言 有人曾问fastjson的作者&#xff08;阿里技术专家高铁&#xff09;&…

android 侧滑删除功能,200行代码让你在Android中完美实现iOS版侧滑删除效果

使用几个月的IOS之后,发现IOS中侧滑删除俺就大家好&#xff0c;自己开始学习Android已经差不多半年了吧&#xff0c;前前后后看了不少的博客获益匪浅。渐渐的随着技术的提升&#xff0c;慢慢感觉网上其它的一些功能的实现又不是那么完美&#xff0c;今天就给大家带来一篇在Andr…

java IO体系的学习总结

1.Java Io流的概念&#xff0c;分类&#xff0c;类图。 1.1 Java Io流的概念 ? ? java的io是实现输入和输出的基础&#xff0c;可以方便的实现数据的输入和输出操作。在java中把不同的输入/输出源&#xff08;键盘&#xff0c;文件&#xff0c;网络连接等&#xff09;抽象表…

android10获取imei,Android 10 root用户获取imei

IMEI(International Mobile Equipment Identity)是国际移动设备识别码的缩写&#xff0c;由15-17位数字组成&#xff0c;与手机是一一对应的关系。无论刷机还是恢复出厂设置&#xff0c;该设备标识码都不会改变&#xff0c;所以在广告和流量统计等方面特别好用&#xff0c;备受…

JAVA基础之HttpServletResponse响应

JAVA基础之HttpServletResponse响应 用户在客户端输入网址&#xff08;虚拟路径&#xff09;时&#xff0c;开始发送一个HTTP请求&#xff08;请求行、请求头、请求体&#xff09;至服务器。服务器内的Tomcat引擎会解析请求的地址&#xff0c;去找XML文件&#xff0c;然后根据…

Logger之Logger.getLogger(CLass)

之前一直在使用System.out.println()来调试.但是用这种方式开发项目部署到生产环境,会因为众多的控制台输出降低应用的性能.这时候Log4J就成为可平衡开发和部署应用的利器了. 在项目中使用Log4J并不是一件困难的事情,简单粗暴的方式就是在每个类A中声明一个Logger私有属性 pri…

订阅号助手android,微信订阅号助手app

微信订阅号助手app属于微信官方推出的软件&#xff0c;可以把我们的手机当做公众号的平台&#xff0c;让你直接用手机来实现公众号的各种工作、互动内容&#xff0c;微信订阅号助手app不用担心使用一些第三方软件被封号了&#xff0c;非常实用可靠。【应用介绍】订阅号助手是一…

使用LoggerFactory.getLogger(xxx.class)方法在控制台打印日志信息

使用System.out.println()来调试.但是用这种方式开发项目部署到生产环境,会因为众多的控制台输出降低应用的性能.这时候Log4J就成为可平衡开发和部署应用的利器了. 使用指定的类XXX初始化日志对象&#xff0c;方便在日志输出的时候&#xff0c;可以打印出日志信息所属的类。 …

html自动增加vbs代码,vbs脚本病毒代码大全编步骤四个

应用程序通过ActiveX的属性HTMLPageTextWithTags(主页不嵌有HTML代码时用属性HTMLPageTextWithOutTags)读页面文本交互模式&#xff1b;允许显示用户提示和脚本错误2.ntlm.vbs***************ntlm.vbsby黑嘿黑***************dimwshsetwshCreateObject("WScript.Shell&quo…

java util logger slf4j_别再自己用LoggerFactory生成logger实例了,试试slf4j注解

背景 在项目开发中&#xff0c;记录日志是必做的一件事情。日志的实现有很多种&#xff1a;Logback&#xff0c;Log4j2&#xff0c;log4j&#xff0c;JAVA Util Logging等等。 而slf4j是一个适配层&#xff0c;由适配层决定使用哪一种日志系统&#xff0c;而调用端只需要做的…

日志框架介绍(SLF4J及其使用)

本文主要介绍 1.日志框架基本介绍及Spring Boot中默认使用框架(SLF4JLogBack); 2.SLF4J的使用 3.项目中日志框架统一问题 4.Spring Boot中如何实现日志框架统一问题(3的最佳实践) 1. 日志框架基本介绍 一般情况下搭建日志框架时&#xff0c;搭建 日志抽象层&#xff08;定…

jmeter生成html报告修改,Jmeter生成html报告(示例代码)

新的JMeter版本中引入了Dashboard Report&#xff0c;用于生成HTML页面格式图形化报告的扩展模块。注&#xff1a;本文用的是3.2版本。生成html报告一、检查.jtl文件&#xff0c;如果没有.jtl文件&#xff0c;运行如下命令&#xff1a;jmeter -n -t 天气api.jmx -l result.jtl …

SpringBoot使用Slf4j+Log4j2完成项目的日志记录

SpringBoot使用Slf4jLog4j完成项目的日志记录 前言 本示例采用SpringBoot项目使用SpringAOP记录日志&#xff0c;Slf4j作为日志门面&#xff0c;Log4j2作为日志实现实&#xff0c;实现开发中的日志记录. 部分效果展示 &#xff1a; 日志文件 : 日志信息 : 代码具体实现如下…

鸿蒙构架谁提供的,科普丨关于“鸿蒙”,不知道这些你都不好意思跟别人打招呼!...

鸿蒙的英文名是Harmony OS华为消费者业务CEO余承东8月9日正式发布鸿蒙系统&#xff0c;英文名是Harmony OS 。国家知识产权局商标局网站显示&#xff0c;华为已经申请注册“华为鸿蒙”商标&#xff0c;申请日期是2018年8月24日&#xff0c;注册公告日期是2019年5月14日。鸿蒙是…

springBoot Logging 日志详解

文章目录日志格式控制台输出彩色编码输出文件输出文件级别自定义日志配置Logback 扩展profile 指定 配置文件Environment 属性springBoot 日志使用Commons Logging作为抽象层&#xff0c;并将具体实现开放&#xff0c;支持Java Util Logging、Log4j2和Logback。loggers 默认配置…