Spring Boot学习笔记总结
Posted IT_Holmes
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Spring Boot学习笔记总结相关的知识,希望对你有一定的参考价值。
文章目录
- 1. Web请求参数处理 Rest映射源码解析
- 2. Web请求参数处理 请求映射原理
- 3. 请求处理常用的参数注解
- 4. 请求参数处理 类型参数的解析原理
- 5. 请求参数处理 Servlet API参数解析原理
- 6. 请求参数处理 Model和Map原理
1. Web请求参数处理 Rest映射源码解析
1.1 OrderedHiddenHttpMethodFilter对象原理
Rest风格:
- 注意这里是Rest风格,并不是Restful风格不要混淆!
- 可以直接认为Rest风格就是@RestController和@ResponseBody。
package com.itholmes.boot.controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
//RestController就是rest风格,还有@Responsebody
@RestController
public class HelloController
//相同的路径/user,来处理不同的请求类型。
@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-张三";
对于rest风格的参数类型处理,必须要给容器中注入一个OrderedHiddenHttpMethodFilter对象,这个对象也是在WebMvcAutoConfiguration类自动配置注入。
在WebMvcAutoConfiguration类下,有一个hiddenHttpMethodFilter(排序隐藏Http方法过滤器)方法,这个方法的作用是过滤请求参数的方法类型,例如:get,post,delete,put等等。注意这个hiddenHttpMethodFilter装配到容器中,该对象本身是一个filter过滤器!
@Bean
@ConditionalOnMissingBean(HiddenHttpMethodFilter.class)
@ConditionalOnProperty(
prefix = "spring.mvc.hiddenmethod.filter",
name = "enabled"
)
public OrderedHiddenHttpMethodFilter hiddenHttpMethodFilter()
return new OrderedHiddenHttpMethodFilter();
但是在@ConditionalOnProperty注解中有一个matchIfMissing注解属性,默认是false的!这个属性的功能如下:
/**
*该属性为true时,配置文件中缺少对应的value或name的对应的属性值,也会注入成功
*/
boolean matchIfMissing() default false;
因为是matchIfMissing默认是false,所以开始的HiddenHttpMethodFilter过滤器是没有装配到容器中的,因此我们要设置spring.mvc.hiddenmethod.filter.enabled=true来启动该过滤器(装配到容器中)。
可以理解的是spring.mvc.hiddenmethod.filter.enabled=true是专门用来处理表单的Rest功能。
spring:
mvc:
hiddenmethod:
filter:
enabled: true # 启用HiddenHttpMethodFilter过滤器,专门用来处理表单传送过来的_method的参数数据,进而来出来delete,put等类型参数。
1.2 表单提交要使用Rest的使用
对应上面内容。
这样我们在前端发送不同类型,后台就可以处理了:
- 表单提交要带上_method=PUT , 这样的方式来能让SpringBoot自动配置的hiddenHttpMethodFilter方法识别到!
- 再次提醒,不要忘记启动hiddenHttpMethodFilter组件对象,也就是必须要装配到容器中(被启动)。因为上面的这种方式必须基于该组件对象。
测试REST风格:
<form action="/user" method="get">
<input type="submit" value="REST-GET">
</form>
<form action="/user" method="POST">
<input type="submit" value="REST-POST">
</form>
<!--
form表单是不能提交delete,put等类型的,直接定义这些类型,form表单还是走默认的get方法。
根据hiddenHttpMethodFilter方法源码:通过传递一个name为"_method"值的参数来指定,并且方法的类型必须是post方法,因为它的源码规定这里必须是post方式!。
-->
<form action="/user" method="post">
<input name="_method" type="hidden" value="DELETE">
<input type="submit" value="REST-delete">
</form>
<form action="/user" method="post">
<input name="_method" type="hidden" value="PUT">
<input type="submit" value="REST-put">
</form>
hiddenHttpMethodFilter在容器中本质是一个OrderedHiddenHttpMethodFilter对象,该对象又继承了HiddenHttpMethodFilter对象,在HiddenHttpMethodFilter对象中,有一个doFilterInternal方法:
private static final List<String> ALLOWED_METHODS;
static
//这里定义支持哪几种请求,有delete,put,patch这三种。也就是允许这三种请求。
ALLOWED_METHODS = Collections.unmodifiableList(Arrays.asList(HttpMethod.PUT.name(), HttpMethod.DELETE.name(), HttpMethod.PATCH.name()));
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException
//拿到当前的请求request
HttpServletRequest requestToUse = request;
//先判断是不是post方法,也说明了前面form表单传递_method=PUT形式为什么要用post,这里规定的就是post。并且判断请求是否正常,有没有error。
if ("POST".equals(request.getMethod()) && request.getAttribute("javax.servlet.error.exception") == null)
//在request获取参数,这里的this.methodParam是整个类写死的一个值就是"_method";
//也就是在request中获取到了_method的参数了,也就是form表单传的delete,put等等。
String paramValue = request.getParameter(this.methodParam);
//这里判断有没有长度,也就是判断是不是空
if (StringUtils.hasLength(paramValue))
//将值进行一个大写转换。也就迎合了前端发送大写小的方法名都可以。
String method = paramValue.toUpperCase(Locale.ENGLISH);
//对应上面ALLOWED_METHODS,判断是否属于ALLOWED_METHODS,仅能支持delete,put,patch。
if (ALLOWED_METHODS.contains(method))
//将原生request(post请求) , 包装模式requestWrapper重写了getMethod方法,返回的是重写后的request(就是这里的method请求[delete,put,patch请求])
requestToUse = new HiddenHttpMethodFilter.HttpMethodRequestWrapper(request, method);
//之后就是将处理后的requestToUse放行了。过滤器放行。
filterChain.doFilter((ServletRequest)requestToUse, response);
==========================================================================================
//对应上面的HttpMethodRequestWrapper
private static class HttpMethodRequestWrapper extends HttpServletRequestWrapper
private final String method;
public HttpMethodRequestWrapper(HttpServletRequest request, String method)
super(request);
this.method = method;
//这里将request的值重写了,也就是原来是request的method是post,现在重写为了根据_method传入的类型(delete,put,patch)
public String getMethod()
return this.method;
这样也就将原来的post请求类型,通过OrderedHiddenHttpMethodFilter过滤器转成了不同的请求类型。
1.3 Rest使用客户端工具发送不同类型的请求
例如:使用postman发送的put参数。
也就是不影响原生的功能。
1.4 如何改变默认的_method?
自己创建一个HiddenHttpMethodFilter对象,放入容器中。
自己写一个配置类,注入到容器中:
package com.itholmes.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
@Bean
//注意这里有两个HiddenHttpMethodFilter对象,一个是普通servlet使用的,一个是响应式使用的。
public HiddenHttpMethodFilter hiddenHttpMethodFilter()
HiddenHttpMethodFilter hiddenHttpMethodFilter = new HiddenHttpMethodFilter();
//设置为自己想要的名字。
hiddenHttpMethodFilter.setMethodParam("_m");
return hiddenHttpMethodFilter;
2. Web请求参数处理 请求映射原理
DispatcherServlet类实现的过程,例如,实现get方法的过程,也是从上往下:
SpringMvc功能分析都从org.springframework.web.servlet.DispatcherServlet =》doDispatch()方法实现。
分析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
try
ModelAndView mv = null;
Object dispatchException = null;
try
processedRequest = this.checkMultipart(request);
multipartRequestParsed = processedRequest != request;
//找到当前请求使用哪个Handler(Controller的方法)处理。
mappedHandler = this.getHandler(processedRequest);
=================================================================
//查看getHandler方法,里面有一个HandlerMapping处理器映射。
@Nullable
protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception
if (this.handlerMappings != null)
Iterator var2 = this.handlerMappings.iterator();
while(var2.hasNext())
HandlerMapping mapping = (HandlerMapping)var2.next();
HandlerExecutionChain handler = mapping.getHandler(request);
if (handler != null)
return handler;
return null;
通过发送一个路径请求,查看handlerMappings中的内容:
- RequestMappingHandlerMapping作用就是处理所有@RequestMapping注解 和 handler的映射规则。
- WelcomePageHandlerMapping是处理欢迎页功能的映射。
SpringBoot自动配置欢迎页的WelcomePageHandlerMapping的源码分析(访问 " / " 能访问到index.html):
因为这里while循环的遍历,依次尝试所有的HandlerMapping看是否有对应路径的请求信息
- 如果找到了对应的请求handler就会return handler。
- 如果没有找到就继续遍历下一个handler。
总结起来就是知道RequestMappingHandlerMappin和WelcomePageHandlerMapping在SpringMvc中什么作用!剩余的三个了解
此外,还可以自定义映射处理,我们也可以自己给容器中放HandlerMapping。自定义HandlerMapping。
3. 请求处理常用的参数注解
3.1 SpringMvc常用的一些注解
@PathVariable的用法:
- @PathVariable(“id”) Integer id://@PathVariable(“id”)拿到对应的值。
- @PathVariable Map<String,String> path_value://拿到所有的@PathVariable注解对应的key-value(也就是指定@PathVariable注解的value拿指定的,没有指定就拿所有和@PathVariable绑定的key-value。)
@RequestHeader的用法:
- @RequestHeader(“User-Agent”) String userAgent://@RequestHeader注解拿到请求头的信息。
- @RequestHeader Map<String,String> headers://和上面一样,拿到所有的请求头信息。指定@RequestHeader注解的value值就拿到对应指定的,没有指定就拿全部的。
@RequestParam的用法:
- @RequestParam(“age”) Integer age://接受form表单格式单个。
- @RequestParam(“inters”) List inters://当form表单对一个key指定了多个value可以使用list来接受,例如: ?inters=1&inters=2&inters=4。
- @RequestParam Map<String,String> params://和上面一样,获取全部的form表单形式的参数。
@CookieValue的用法:
- @CookieValue(“token”) String value://在cookie中拿到token的value值。
- @CookieValue(“token”) Cookie cookie://拿到token的整个cookie。
@RequestBody的用法:
- @RequestBody String content://如果是form表单提交,例如:name=zhangsan&pwd=123123 , 这里的content就会变成“name”:“zhangsan”,“pwd”:“123123”
@RequestAttribute的用法:
3.2 SpringBoot的矩阵变量 和 UrlPathHelper类
什么是矩阵变量,矩阵变量以及对比其他的方式?
- /cars/path?xxx=xxx&aaa=ccc 这可以是queryString查询字符串,后台用@RequestParam接受。
- /cars/sell;low=34;brand=byd,audi,yd 使用" ; "的这种形式叫做矩阵变量。
页面开发,cookie禁用了,session里面的内容怎么使用?
- 可以使用url重写:/abc;jsessionid=xxxx 把cookie的值使用矩阵变量的方式进行传递。
/boss/1/2 对比 /boss/1;age=20/2;age=18 分号;前面是路径,后面是矩阵变量。
SpringBoot默认是禁用了矩阵变量的功能。
原理:在WebMvcAutoConfiguration中,有一个configurePathMatch方法,在该方法中有一个UrlPathHelper(Url路径帮助器) 类,在这个类中,有一个removeSemicolonContent(Semicolon分号)成员变量属性,这个属性就是规定SpringBoot支不支持矩阵变量的(也就是是否移除分号后面的内容。)。
UrlPathHelper类有很多的功能:
如何开启SpringBoot支持矩阵变量的功能?
- 可以在配置类中自定义配置WebMvcConfigurer容器对象,来修改UrlPathHelper对象中的removeSemicolonContent变量。
package com.itholmes.boot.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.filter.HiddenHttpMethodFilter;
import org.springframework.web.servlet.config.annotation.PathMatchConfigurer;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import org.springframework.web.util.UrlPathHelper;
@Configuration(proxyBeanMethods = false)
/*
第一种方式:直接implement的WebMvcConfigurer。
并且重写configure
*/
public class WebConfig implements WebMvcConfigurer
@Override
public void configurePathMatch(PathMatchConfigurer configurer)
UrlPathHelper urlPathHelper = new UrlPathHelper();
//设置为false,也就是不移除分号后面的内容,这样矩阵变量可以生效。
urlPathHelper.setRemoveSemicolonContent(false);
configurer.setUrlPathHelper(urlPathHelper);
/*
第二种方式:
使用@Bean,直接向容器中装配一个WebMvcConfigurer。
@Bean
public WebMvcConfigurer webMvcConfigurer()
return new WebMvcConfigurer()
@Override
public void configurePathMatch(PathMatchConfigurer configurer)
UrlPathHelper urlPathHelper = new UrlPathHelper();
//设置为false,也就是不移除分号后面的内容,这样矩阵变量可以生效。
urlPathHelper.setRemoveSemicolonContent(false);
configurer.setUrlPathHelper(urlPathHelper);
;
*/
获取矩形变量也有格式上的一些要求:
- 例如: “/cars/path” 。
- @PathVariable(“path”) String path拿到的就是path里面分号前面的路径。
- @MatrixVariable(“xxx”) 拿到的就是矩形变量xxx对应的内容。
/*
矩形变量注意点:
1.SpringBoot默认是禁用了矩形变量的功能。
2.矩形变量必须有url路径变量才能被解析,也就是/cars/sell;low=34 => path sell就是路径,low就是矩形变量。
*/
//第一种情况: 一个key对应一个值,一个key对应多个值。
// 例如矩阵变量的语法为: /cars/sell;low=34;brand=byd,audi,yd
@GetMapping("/cars/path")
public Map carsSell(
//获取path的路径。
@PathVariable("path") String path,
//拿到path中的矩阵变量。
@MatrixVariable("low") Integer low,
@MatrixVariable("brand") List<String> brand
)
HashMap<String, Object> map = new HashMap<>();
map.put("low",low);
map.put("brand",brand);
map.put("path",path);
return map;
//第二种情况: 两个下的相同key。
// /boss/1;age=20/2;age=18
@GetMapping("/boss/bossId/empId")
public Map boss(
//通过pathVar来指定获取哪一个的age。
@MatrixVariable(value = "age" , pathVar = "bossId") Integer bossAge,
@MatrixVariable(value = "age", pathVar = "empId") Integer empAge
)
HashMap<String, Object> map = new HashMap<>();
map.put("bossAge",bossAge);
map.put("empAge",empAge);
return map;
4. 请求参数处理 类型参数的解析原理
4.1 找到合适的HandlerAdapter处理器的源码分析
同样和HandlerMapping一样,HandlerAdapter也会是在DispatcherServlet的doDispatch方法中运行:
接下来说的是Handler适配器,HandlerAdapter如何处理一些参数。
对于各种类型参数的解析原理,使用的是适配器HandlerAdapter来解决。
- handlerMapping中找到能处理请求的handler(controller)。为当前Handler(controller)找一个适配器HandlerAdapter。
上图是SpringMvc默认的4个HandlerAdapter,handler适配器:
- 0 - RequestMappingHandlerAdapter是支持方法上标注@RequestMapping。
- 1 - HandlerFunctionAdapter是支持函数式变成的。
执行的核心又在这里:
//拿到能处理的handlerAdapter
HandlerAdapter ha = this.getHandlerAdapter(mappedHandler.getHandler());
//在这里执行handlerAdapter
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
4.2 HandlerAdapter的执行目标方法
//在这里执行handlerAdapter
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
mappedHandler.getHandler()的源码解析:
handle方法源码:
继续step into深入,就会看到继承了abstractHandlerMethodAdapter的RequestMappingHandlerAdapter类,在该类有一个handleInternal方法,该方法如下代码:
//这段代码就是来执行目标方法的。就是controller那些类里面以上是关于Spring Boot学习笔记总结的主要内容,如果未能解决你的问题,请参考以下文章
看完这份学习笔记,Spring Boot于你而言就是小菜一碟