• Spring Boot开发之参数处理


    1 普通参数

    1 注解

    包括: @PathVariable、@RequestHeader、@ModelAttribute、@RequestParam、@MatrixVariable、@CookieValue、@RequestBody等

     @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){
            // xxx
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    2 Servlet的API

    涉及类为: WebRequest、ServletRequest、MultipartRequest、 HttpSession、javax.servlet.http.PushBuilder、Principal、InputStream、Reader、HttpMethod、Locale、TimeZone、ZoneId.

    @Override
    	public 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) ||
    				InputStream.class.isAssignableFrom(paramType) ||
    				Reader.class.isAssignableFrom(paramType) ||
    				HttpMethod.class == paramType ||
    				Locale.class == paramType ||
    				TimeZone.class == paramType ||
    				ZoneId.class == paramType);
    	}
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    3 复杂参数

    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中取值 
    request.getAttribute();
    
    • 1
    • 2
    • 3

    4 自定义对象参数

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

    /**
     *     姓名:  
    * 年龄:
    * 生日:
    * 宠物姓名:
    * 宠物年龄: */
    @Data public class Person { private String userName; private Integer age; private Date birth; private Pet pet; } @Data public class Pet { private String name; private String age; }
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24

    2 参数处理原理

    • HandlerMapping中找到能处理请求的Handler(Controller.method())
    • 为当前Handler 找一个适配器 HandlerAdapter
    • 适配器执行目标方法并确定方法参数的每一个值

    1 HandlerAdapter

    支持方法上标注的@RequestMapping , 支持函数式编程的.

    2 执行目标方法

    // Actually invoke the handler.
    //DispatcherServlet -- doDispatch
    mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
    
    
    mav = invokeHandlerMethod(request, response, handlerMethod); //执行目标方法
    
    
    //ServletInvocableHandlerMethod
    Object returnValue = invokeForRequest(webRequest, mavContainer, providedArgs);
    //获取方法的参数值
    Object[] args = getMethodArgumentValues(request, mavContainer, providedArgs);
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    3 参数解析器

    HandlerMethodArgumentResolver, 参数解析器:

    确定将要执行的目标方法的每一个参数的值是什么, SpringMVC目标方法能写多少种参数类型。取决于参数解析器.

    提供两个方法:

    • 当前解析器是否支持解析这种参数 supportsParameter
    • 支持就调用 resolveArgument

    4 确定方法参数值

    protected Object[] getMethodArgumentValues(NativeWebRequest request, @Nullable ModelAndViewContainer mavContainer,
    			Object... providedArgs) throws Exception {
    
    		MethodParameter[] parameters = getMethodParameters();
    		if (ObjectUtils.isEmpty(parameters)) {
    			return EMPTY_ARGS;
    		}
    
    		Object[] args = new Object[parameters.length];
    		for (int i = 0; i < parameters.length; i++) {
    			MethodParameter parameter = parameters[i];
    			parameter.initParameterNameDiscovery(this.parameterNameDiscoverer);
    			args[i] = findProvidedArgument(parameter, providedArgs);
    			if (args[i] != null) {
    				continue;
    			}
    			if (!this.resolvers.supportsParameter(parameter)) {
    				throw new IllegalStateException(formatArgumentError(parameter, "No suitable resolver"));
    			}
    			try {
    				args[i] = this.resolvers.resolveArgument(parameter, mavContainer, request, this.dataBinderFactory);
    			}
    			catch (Exception ex) {
    				// Leave stack trace for later, exception may actually be resolved and handled...
    				if (logger.isDebugEnabled()) {
    					String exMsg = ex.getMessage();
    					if (exMsg != null && !exMsg.contains(parameter.getExecutable().toGenericString())) {
    						logger.debug(formatArgumentError(parameter, exMsg));
    					}
    				}
    				throw ex;
    			}
    		}
    		return args;
    	}
    
    • 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
    • 35

    遍历判断所有参数解析器

    	@Nullable
    	private HandlerMethodArgumentResolver getArgumentResolver(MethodParameter parameter) {
    		HandlerMethodArgumentResolver result = this.argumentResolverCache.get(parameter);
    		if (result == null) {
    			for (HandlerMethodArgumentResolver resolver : this.argumentResolvers) {
    				if (resolver.supportsParameter(parameter)) {
    					result = resolver;
    					this.argumentResolverCache.put(parameter, result);
    					break;
    				}
    			}
    		}
    		return result;
    	}
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    解析参数

    调用各自 HandlerMethodArgumentResolver 的 resolveArgument 方法即可
    
    • 1

    5 目标方法执行完成

    将所有的数据都放在 ModelAndViewContainer;包含要去的页面地址View。还包含Model数据。

    6 处理派发结果

    结果派发涉及到:

    processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);

    renderMergedOutputModel(mergedModel, getRequestToExpose(request), response);

    @Override
    	protected void renderMergedOutputModel(
    			Map<String, Object> model, HttpServletRequest request, HttpServletResponse response) throws Exception {
    
    		// Expose the model object as request attributes.
    		exposeModelAsRequestAttributes(model, request);
    
    		// Expose helpers as request attributes, if any.
    		exposeHelpers(request);
    
    		// Determine the path for the request dispatcher.
    		String dispatcherPath = prepareForRendering(request, response);
    
    		// Obtain a RequestDispatcher for the target resource (typically a JSP).
    		RequestDispatcher rd = getRequestDispatcher(request, dispatcherPath);
    		if (rd == null) {
    			throw new ServletException("Could not get RequestDispatcher for [" + getUrl() +
    					"]: Check that the corresponding file exists within your web application archive!");
    		}
    
    		// If already included or response already committed, perform include, else forward.
    		if (useInclude(request, response)) {
    			response.setContentType(getContentType());
    			if (logger.isDebugEnabled()) {
    				logger.debug("Including [" + getUrl() + "]");
    			}
    			rd.include(request, response);
    		}
    
    		else {
    			// Note: The forwarded resource is supposed to determine the content type itself.
    			if (logger.isDebugEnabled()) {
    				logger.debug("Forwarding to [" + getUrl() + "]");
    			}
    			rd.forward(request, response);
    		}
    	}
    
    • 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
    • 35
    • 36
    • 37

    暴露模型作为请求域属性: 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);
    			}
    		});
    	}
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
  • 相关阅读:
    重置Mac电脑的SMC怎么操作,重置SMC方法分享~
    取模和取余
    nginx未配置proxy_set_header Host $host;引起的服务请求失败
    mybatis-plus实现逻辑删除(详细!)
    LabVIEW程序框图保存为图像
    【Leetcode刷题】二分查找
    重新认识下JVM级别的本地缓存框架Guava Cache——优秀从何而来
    RabbitMQ系列【16】AmqpTemplate接口详解
    Python——实现微博评论情绪分析(生成情绪时空分布地图(pyecharts))
    2023数维杯国际赛数学建模B题思路模型分析
  • 原文地址:https://blog.csdn.net/ABestRookie/article/details/127414799