• SpringBoot2核心技术与响应式编程- 请求响应原理


    请求映射

    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 复杂参数

     MapModel(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);
                }
            });
        }

     
     

     

     

  • 相关阅读:
    Delphi 与 DirectX 之 DelphiX(28): TDIB.Emboss;
    Delphi 与 DirectX 之 DelphiX(29): TDIB.AddMonoNoise();
    Delphi 与 DirectX 之 DelphiX(33): TDIB.SmoothRotateWrap();
    如何用w.bloggar从桌面发表文章
    可以插入图片了
    在首页可以查看阅读次数了
    .Text的MainFeed.aspx生成RSS的问题
    首页文章显示说明
    欢迎光临博客园
    如果想学习.Net Remoting,请看看MSDN上的一篇文章
  • 原文地址:https://www.cnblogs.com/yxj808/p/15316995.html
Copyright © 2020-2023  润新知