2.5.0版本
@ConfigurationProperties 装配properties中的对象,自动匹配
定制化mvc就是在配置里去创建webConfigurer组件

请求参数处理

rest请求

rest只是一个整合,不是新技术
利用http请求中,所带的method,进行区分

直接rest请求

直接工具是可以请求的,springboot默认开启rest请求

表单rest请求

由于表单提交只有get和post,无法实现rest请

  • from提交为post
  • 开启spring.mvc.hiddenmethod.filter.endabled=true
  • 在表单中添加input标签,name或id为_method,value为rest类型,对应controller就会请求到

修改_method名称

  • 自定一个配置即可。
1
2
3
4
5
6
7
8
9
10
@Configuration(proxyBeanMethods = false)
public class WebConfig {

@Bean
public HiddenHttpMethodFilter hiddenHttpMethodFilter(){
HiddenHttpMethodFilter hiddenHttpMethodFilter = new HiddenHttpMethodFilter();
hiddenHttpMethodFilter.setMethodParam("_mmm");
return hiddenHttpMethodFilter;
}
}

请求映射

就是springmvc的请求映射
mappingRegistry,用于mapping注册到当前HandlerMapping
MatchingMappings,跟当前路径匹配的地址,一个个适配,根据请求类型,没有匹配的会返回404,如果有两个相同的会抛异常,所以只能存在一个。
自动配置下面五个HandlerMapping

  • RequestMappingHandlerMapping 普通请求对应的handler
    • 扫描@RequestMapping
    • 同种路径和类型,只会存在一个
  • WelcomePageHandlerMapping 欢迎页的handler
    • springboot的,自动配置,/index,底层写死的
    • 如果修改静态资源访问路径,不是默认根目录,那么静态资源路径的index.html,无法找到欢迎页。
    • 可以自定义个请求路径,做欢迎页。
  • BeanNameUrlHandlerMapping
  • RouterFunctionMapping
  • SimpleUrlHandlerMapping

请求进来,会循环扫描以上五个Mapping,找到对应Handler进行处理
所有改的请求映射,都在HandlerMapping
自定义HandlerMapping,由于灰度发版,新旧版本并行。

普通参数和基本注解

注解

  • @PathVariable 路径变量
    • 可以用@PathVariable Map<Stirng,String> map,接收全部值
  • @RequestHeader 请求头
    • 可以用 @RequestHeader Map<Stirng,String> header,接收全部值
  • @RequestParam 请求参数
    • 同属性多值,不要用@RequestParam Map<String,String> map,只能放一个值。
    • 同属性多值,@RequestParam(“xxxx”) List<String> xxxx,接收
  • @RequestAttribute 获取request中的属性
  • @ModelAttribute ServletModelAttributeMethodProcessor解析
    • 加在方法上
    • 每次当前类的请求都会处理,并且会覆盖
    • 无法返回值,可以入参数上加上model
    • 有返回值,且@ModelAttribute(“XXXX”),key为XXX,value为返回值
    • 有返回值,且@ModelAttribute(),入参无需用model,返回值就是attribute中的value,返回值类型小写开头,就是key
  • @CookieValue cookie值
  • @RequestBody 请求体
  • @MatrixVariable 矩阵变量
    • 根据RFC3986的规则,应当使用英文字符;进行分割
    • 若是一个矩阵变量有多个值,应当使用英文符号,进行分割,或之命名多个重复的key即可。
    • 原来 /cars/{path}?xxx=xxx&sss=vvvv querString 查询字符串。@RequestParam
    • 用矩阵变量 /cars/{path;low=34;brand=byd,audi,yd}
    • 页面开发,cookie禁用了,session里面的内容怎么使用
      • session.set(a,b) —-> jsessionid —> cookie —–> 每次请求携带
      • url重写:/cars;jsessionid=xxxxx 把cookie的值使用矩阵变量的方式进行传递。
    • 矩阵变量需要在springboot中手动开启
      • 在WwebMvcAutoConfiguration中configurePathMatch中UrlPathHelper
      • UrlPathHelper中的一个属性,removeSemicolonContent,默认为true,对url中;之后的部分进行移除
      • 自定义配置
        • 第一种 实现WebMvcConfigurer,重写configurerPathMatch方法
        • 第二种 直接创建WebMvcConfigurer组件,重写configurerPathMatch方法
1
2
3
4
5
6
@Override
public void configurePathMatch(PathMatchConfigurer configurer) {
UrlPathHelper urlPathHelper = new UrlPathHelper();
urlPathHelper.setRemoveSemicolonContent(false);
configurer.setUrlPathHelper(urlPathHelper);
}
1
2
3
4
5
6
7
8
9
10
11
@Bean
public WebMvcConfigurer webMvcConfigurer(){
return new WebMvcConfigurer() {
@Override
public void configurePathMatch(PathMatchConfigurer configurer) {
UrlPathHelper urlPathHelper = new UrlPathHelper();
urlPathHelper.setRemoveSemicolonContent(false);
configurer.setUrlPathHelper(urlPathHelper);
}
};
}

Servlet API

ServletRequestMethodArgumentResolver解析以下参数

  • WebRequest
  • ServletRequest
  • MultipartRequest
  • HttpSession
  • java.servlet.http.PushBuilder
  • Principal
  • InputStream
  • Reader
  • HttpMethod
  • Locale
  • TimeZone
  • Zoneld

复杂参数

  • Map 向map里放东西,相当于request.setattribute()
    • mavContainer.getModel(); —–> BindingAwareModelMap 继承ModelMap,既是Model也是Map
  • Model 同上
  • Errors/BindingResult
  • RedirectAttributes 重定向携带数据
  • ServletResponse response
  • SessionStatus
  • UriComponentsBuilder
  • ServletUriComponentsbBuilder

自定义对象参数

ServletModelAttributeMethodProcessor对对象进行解析,提供自动绑定

  • 可以自动类型转换与格式化,可以级联封装

参数处理原理

  • HandlerMapping中到到能处理的请求的Handler(controller.method)
  • 为当前Handler找适配器HanlderAdapter
    • RequestMappingHandlerAdapter 支持@RequestMapping的处理
    • HandlderFuntionAdapter 支持函数式编程的处理
    • HttpRequestHandlerAdapter servlet的支持
    • SimpleControllerHandlerAdapter

拿到对相应的HandlerAdapter之后,执行handler
在handler中执行invokeHandlerMethod方法
在invokeHandlerMethod中封装了一个叫ServletInvocableHandlerMethod的对象
对象中封装了argumentResolvers的参数解析器
argumentResolvers的种类有:

  • RequestParamMethodArgumentResolver
  • RequestParamMapMethodArgumentResolver
  • PathVariableMethodArgumentResolver
  • PathVariableMapMethodArgumentResolver
  • MatrixVariableMethodArgumentResolver
  • MatrixVariableMapMethodArgumentResolver
  • ServletModelAttributeMethodProcessor
  • RequestResponseBodyMethodProcessor
  • RequestPartMethodArgumentResolver
  • RequestHeaderMethodArgumentResolver
  • RequestHeaderMapMethodArgumentResolver
  • ServletCookieValueMethodArgumentResolver
  • ExpressionValueMethodArgumentResolver
  • SessionAttributeMethodArgumentResolver
  • RequestAttributeMethodArgumentResolver
  • ServletRequestMethodArgumentResolver
  • ServletResponseMethodArgumentResolver
  • HttpEntityMethodProcessor
  • RedirectAttributesMethodArgumentResolver
  • ModelMethodProcessor
  • MapMethodProcessor
  • ErrorsMethodArgumentResolver
  • SessionStatusMethodArgumentResolver
  • UriComponentsBuilderMethodArgumentResolver
  • PrincipalMethodArgumentResolver
  • RequestParamMethodArgumentResolver
  • ServletModelAttributeMethodProcessor
  • 参数解析实现HandlerMethodArgumentResolver接口
    • resolveArgument 解析参数
    • supportsParameter 支持参数 当前解析器是否支持解析这个参数,支持才会调用resolveArgument进行参数的解析
  • method中能写多少种参数,取决参数解析器

还封装了returnValueHandlders 返回值处理器,类型如下

  • ModelAndViewMethodReturnValueHandler
  • ModelMethodProcessor
  • ViewMethodReturnValueHandler
  • ResponseBodyEmitterReturnValueHandler
  • StreamingResponseBodyReturnValueHandler
  • HttpEntityMethodProcessor
  • HttpHeadersReturnValueHandler
  • CallableMethodReturnValueHandler
  • DeferredResultMethodReturnValueHandler
  • AsyncTaskMethodReturnValueHandler
  • ServletModelAttributeMethodProcessor
  • RequestResponseBodyMethodProcessor
  • ViewNameMethodReturnValueHandler
  • MapMethodProcessor
  • ServletModelAttributeMethodProcessor
  • method能返回什么参数,取决于返回值处理器

封装完毕后,ServletInvocableHandlerMethod回去调用invokeAndHandler的方法
在invokeAndHandler中调用invokeForRequest方法
在invokeForRequest中调用getMethodArgumentValues获取controller.method需要的参数
getMethodArgumentValues在InvocableHanderMethod.java中

  • InvocableHanderMethod
    • getMethodParamters 获取值
    • 遍历参数
      • this.resolvers.supportsParameter(parameter)//是否支持当前参数的解析
        • 遍历26种参数解析器 break;
          • 判断参数上的注解类型,没有注解的,默认走RequestParam
      • this.resolvers.resolveArgument进行解析
        • 获取参数,在request.attribute中,是由UrlPathHelper解析出来,请求最开始的时候。

拿到参数之后,会执行doInvoke

数据响应与内容协商

响应JSON

  • 需要webmvc
  • 依赖jackson
  • @ResponseBody

请求是会携带当前的数据类型和能接收的数据类型,返回数据时,会根据接收类型,和当前的数据类型进行转换,这一步叫内容协商
HTTPMessageConverter

  • canRead 判断能读
  • canWrite 判断能写
  • getSupportedMediaTypes 是否支持MediaType
  • read 读
  • write 写
    将此class类型,转为MediaType类型的数据

内容协商原理

  • 判断当前相应头中是否已有确定的媒体类型。MediaType
  • 获取客户端(PostMan,浏览器)支持接收的内容类型。(获取客户端Accepte请求头字段)【application/xml】
    • contenttNegotiationManager 内容协商管理起默认使用基于请求头的策略
    • contenttNegotiationStraegy 确定客户端可以接受的内容类型
  • 遍历循环所有当前系统的MessageConverter,看谁支持操作这个对象
  • 找到支持操作的converter,把converter支持的媒体类型统计出来
  • 客户端需要【application/xml】。服务端能力【10种,json、xml】
    • application/json
    • application/*+json
    • application/*+xml;charset=UTF-8
    • application/xml;charset=UTF-8
    • text/xml;charset=UTF-8
    • application/*+xml;charset=UTF-8
  • 进行内容协商的租价匹配媒体类型
  • 用支持讲对象转为最佳匹配媒体类型的converter。调用他的转换化
    • ByteArrayHttpMessageConverter
    • StringHttpMessageConverter
    • StringHttpMessageConverter
    • ResourceHttpMessageConverter
    • ResourceRegionHttpMessageConverter
    • SourceHttpMessageConverter
    • AllEncompassingFormHttpMessageConverter
    • MappingJackson2HttpMessageConverter 直接返回true,啥都支持
    • MappingJackson2HttpMessageConverter
    • Jaxb2RootElementHttpMessageConverter 注解方式的xml

自定义MessageConverter

可以实现定制输入输出,自定义协议

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
/**
* 自定义
*/
public class SnmlmMessageConverter implements HttpMessageConverter<User> {

@Override
public boolean canRead(Class<?> clazz, MediaType mediaType) {
return false;//可以自定义读,格式化
}

@Override
public boolean canWrite(Class<?> clazz, MediaType mediaType) {
return clazz.isAssignableFrom(User.class);
}

/**
* 服务器要统计所有MessageConverter都能支持什么类型
* @return
*/
@Override
public List<MediaType> getSupportedMediaTypes() {
return MediaType.parseMediaTypes("application/snmlm");
}

@Override
public User read(Class<? extends User> clazz, HttpInputMessage inputMessage) throws IOException, HttpMessageNotReadableException {
return null;
}

@Override
public void write(User user, MediaType contentType, HttpOutputMessage outputMessage) throws IOException, HttpMessageNotWritableException {
outputMessage.getBody().write(user.toString().getBytes());
}
}

在配置中的webConfigure中添加实现即可 extendMessageConverters是扩充

1
2
3
4
@Override
public void extendMessageConverters(List<HttpMessageConverter<?>> converters) {
converters.add(new SnmlmMessageConverter());
}

开启浏览器参数方式内容协商功能

为了方便,开启基于请求的内容协商功能。

1
2
3
4
spring:
mvc:
contentnegotiation:
favor-parameter: true

参数为format
开启参数,参数优先,否则为请求头,最后才是*/*

自定义协议

configureContentNegotiation会覆盖掉默认的协议,支持参数format

1
2
3
4
5
6
7
8
9
10
11
@Override
public void configureContentNegotiation(ContentNegotiationConfigurer configurer) {
Map<String, MediaType> mediaTypes = new HashMap<>();
mediaTypes.put("json",MediaType.APPLICATION_JSON);
mediaTypes.put("xml",MediaType.APPLICATION_XML);
mediaTypes.put("snmlm",MediaType.parseMediaType("application/snmlm"));
ParameterContentNegotiationStrategy strategy = new ParameterContentNegotiationStrategy(mediaTypes);
strategy.setParameterName("_f");
configurer.strategies(Arrays.asList(strategy,
new HeaderContentNegotiationStrategy()));//把自定的和请求的都加进去的
}

模板引擎

srpingboot默认不支持jsp,有是jar的原因。

freemarker
groovy-templates
thymeleaf

防止表单重复提交,最好的方式是重定向。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
@GetMapping(value ={"/","/login"})
public String loginPage(){
return "login";
}

@PostMapping("/login")
public String login(){
return "redirect:/main.html";
}

@GetMapping("/main.html")
public String main(){
return "main.html";
}

视图解析

ViewResolver

  • ContentNegotiatingViewResolver
  • BeanNameViewResolver
  • ThymeleafViewResolver
  • ViewResolverComposite
  • InternalResourceViewResolver

    原理

  • 目标方法处理的过程中,所有数据都会被放在ModelAndViewContainer里面。包括数据和视图地址。
  • 方法的参数是一个自定义类型对象(从请求参数中确定的),把他重新放在ModelAndViewContainer
  • 任何目标方法执行完成以后都会返回ModelAndView(数据和视图地址)
  • processDispathResult处理派发结果(页面该如何响应)
    • render(mv,request,response);进行页面渲染逻辑
      • 根据方法的String返回值得到View对象【定义了页面的渲染逻辑】
        • 所有的视图解析器尝试是否能根据当前返回值得到View对象
        • 得到 redirect:/main.html –>RedirectView
        • 未完待续