请求映射
rest使用与原理 @xxxMapping; Rest风格支持(使用HTTP请求方式动词来表示对资源的操作) 核心Filter;HiddenHttpMethodFilter 用法: 表单method=post,隐藏域 _method=put SpringBoot中手动开启(开启页面表单的Rest功能)spring: mvc: hiddenmethod: filter: enabled: true #开启页面表单的Rest功能
Rest原理(表单提交要使用REST的时候)
- 表单提交会带上_method=PUT
- 请求过来被HiddenHttpMethodFilter拦截
- 请求是否正常,并且是POST
- 获取到_method的值。
- 兼容以下请求;PUT.DELETE.PATCH
- 原生request(post),包装模式requesWrapper重写了getMethod方法,返回的是传入的值。
- 过滤器链放行的时候用wrapper。以后的方法调用getMethod是调用requesWrapper的。
如PostMan直接发送Put、delete等方式请求,无需Filter。
请求映射原理
DispatchServlet
请求映射原理
DispatchServlet
doGet
doDispatch
SpringMVC功能分析都从 org.springframework.web.servlet.DispatcherServlet-》doDispatch()
找到当前是哪个Handler处理当前请求
getHandler()
HandlerMapping 保存了所有@RequestMapping 和 handler的映射规则
所有的请求映射都在HandlerMapping中。
SpringBoot自动配置了默认 的 RequestMappingHandlerMapping
请求进来,挨个尝试所有的HandlerMapping看是否有请求信息。
如果有就找到这个请求对应的handler
如果没有就是下一个 HandlerMapping
我们需要一些自定义的映射处理,我们也可以自己给容器中放HandlerMapping。自定义 HandlerMapping
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
普通参数与基本注解(urlPathHelper)
1.1 注解
@PathVariable 路径变量
@GetMapping("/car/{id}")
public Map getCar(@PathVariable("id") Integer id)
@RequestHeader、@ModelAttribute、@RequestParam、@MatrixVariable、@CookieValue(获取cookie值)、@RequestBody(获取请求体只有post请求)
@MatrixVariable 矩阵变量 必须绑定在路径变量中
页面开发,cookie禁用了,session 中的内容怎么使用
session->jessionid-->cookie-->每次发送请求携带
url重写:/abc;jessionnid=xxxxxx 把cookie 的值使用矩阵变量方式传递。
@RestController public class ParameterTestController { // car/2/owner/zhangsan @GetMapping("/car/{id}/owner/{username}") public Map<String,Object> getCar(@PathVariable("id") Integer id, @PathVariable("username") String name, @PathVariable Map<String,String> pv, @RequestHeader("User-Agent") String userAgent, @RequestHeader Map<String,String> header, @RequestParam("age") Integer age, @RequestParam("inters") List<String> inters, @RequestParam Map<String,String> params, @CookieValue("_ga") String _ga, @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; } @PostMapping("/save") public Map postMethod(@RequestBody String content){ Map<String,Object> map = new HashMap<>(); map.put("content",content); return map; } //1、语法: 请求路径:/cars/sell;low=34;brand=byd,audi,yd //2、SpringBoot默认是禁用了矩阵变量的功能 // 手动开启:原理。对于路径的处理。UrlPathHelper进行解析。 // removeSemicolonContent(移除分号内容)支持矩阵变量的 //3、矩阵变量必须有url路径变量才能被解析 @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; } }
1.2 servler API:WebRequest、ServletRequest、MultipartRequest
ServletRequestMethodArgumentResolver 以上的部分参数
1.3 复杂参数
Map、Model(map、model里面的数据会被放在request的请求域 request.setAttribute
@RequestAttribute (获取request域中的属性) 不能用map封装注解中所有值
Map<String,Object> map, Model model, HttpServletRequest request 都是可以给request域中放数据, request.getAttribute();
Map、Model类型的参数,会返回 mavContainer.getModel();---> BindingAwareModelMap 是Model 也是Map
mavContainer.getModel(); 获取到值的
1.4 自定义对象参数 封装POJO
ServletModelAttributeMethodProcessor 这个参数处理器支持、
判断是否为简单类型。
public static boolean isSimpleValueType(Class<?> type){}
@Override @Nullable public final Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer, NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory) throws Exception { Assert.state(mavContainer != null, "ModelAttributeMethodProcessor requires ModelAndViewContainer"); Assert.state(binderFactory != null, "ModelAttributeMethodProcessor requires WebDataBinderFactory"); String name = ModelFactory.getNameForParameter(parameter); ModelAttribute ann = parameter.getParameterAnnotation(ModelAttribute.class); if (ann != null) { mavContainer.setBinding(name, ann.binding()); } Object attribute = null; BindingResult bindingResult = null; if (mavContainer.containsAttribute(name)) { attribute = mavContainer.getModel().get(name); } else { // Create attribute instance try { attribute = createAttribute(name, parameter, binderFactory, webRequest); } catch (BindException ex) { if (isBindExceptionRequired(parameter)) { // No BindingResult parameter -> fail with BindException throw ex; } // Otherwise, expose null/empty value and associated BindingResult if (parameter.getParameterType() == Optional.class) { attribute = Optional.empty(); } bindingResult = ex.getBindingResult(); } } if (bindingResult == null) { // Bean property binding and validation; // skipped in case of binding failure on construction. WebDataBinder binder = binderFactory.createBinder(webRequest, attribute, name); if (binder.getTarget() != null) { if (!mavContainer.isBindingDisabled(name)) { bindRequestParameters(binder, webRequest); } validateIfApplicable(binder, parameter); if (binder.getBindingResult().hasErrors() && isBindExceptionRequired(binder, parameter)) { throw new BindException(binder.getBindingResult()); } } // Value type adaptation, also covering java.util.Optional if (!parameter.getParameterType().isInstance(attribute)) { attribute = binder.convertIfNecessary(binder.getTarget(), parameter.getParameterType(), parameter); } bindingResult = binder.getBindingResult(); } // Add resolved attribute and BindingResult at the end of the model Map<String, Object> bindingResultModel = bindingResult.getModel(); mavContainer.removeAttributes(bindingResultModel); mavContainer.addAllAttributes(bindingResultModel); return attribute; }
WebDataBinder binder = binderFactory.createBinder(webRequest, attribute, name);
WebDataBinder :web数据绑定器,将请求参数的值绑定到指定的JavaBean里面
WebDataBinder 利用它里面的 Converters 将请求数据转成指定的数据类型。再次封装到JavaBean中
GenericConversionService:在设置每一个值的时候,找它里面的所有converter那个可以将这个数据类型(request带来参数的字符串)转换到指定的类型(JavaBean -- Integer)
byte -- > file
未来我们可以给WebDataBinder里面放自己的Converter;
private static final class StringToNumber<T extends Number> implements Converter<String, T>
参数解析原理
- HandlerMapping中找到能处理请求的Handler(Controller.method())
- 为当前Handler 找一个适配器 HandlerAdapter; RequestMappingHandlerAdapter
- 适配器执行目标方法并确定方法参数的每一个值
执行目标方法
handle()-》 invokeHandlerMethod()
// Actually invoke the handler. //DispatcherServlet -- doDispatch mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
mav = invokeHandlerMethod(request, response, handlerMethod); //执行目标方法参数解析器-HandlerMethodArgumentResolver
SpringMVC目标方法能写多少种参数类型。取决于参数解析器。
- 当前解析器是否支持解析这种参数
- 支持就调用 resolveArgument
先确定目标方法每一个参数的值 getMethodArgumentValues()
//ServletInvocableHandlerMethod Object returnValue = invokeForRequest(webRequest, mavContainer, providedArgs); //获取方法的参数值 Object[] args = getMethodArgumentValues(request, mavContainer, providedArgs);
挨个判断所有参数解析器那个支持解析这个参数 解析这个参数的值 调用各自 HandlerMethodArgumentResolver 的 resolveArgument 方法即可
目标方法执行完成
将所有的数据都放在 ModelAndViewContainer;包含要去的页面地址View。还包含Model数据。
处理派发结果
processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
renderMergedOutputModel(mergedModel, getRequestToExpose(request), response);
暴露模型作为请求域属性 // Expose the model object as request attributes. exposeModelAsRequestAttributes(model, request);
protected void exposeModelAsRequestAttributes(Map<String, Object> model, HttpServletRequest request) throws Exception { //model中的所有数据遍历挨个放在请求域中 model.forEach((name, value) -> { if (value != null) { request.setAttribute(name, value); } else { request.removeAttribute(name); } }); }