At present, all series of articles have been synchronized to the personal blog (itsoku.com). The personal blog has been revised to make it easier to read. Click the original text on the left at the end of the article to go directly to the blog.
1. Let's look at two good questions
Have you considered the following two questions when using spring MVC or spring boot to develop interfaces
-
What types of interface parameters are supported? Are there any rules to follow?
-
Where do the values of interface parameters come from?
To tell you the truth, these two questions are very key. After understanding the principle, the development interface will be handy. Today I will take you to understand these two questions from the principle.
2. The general process of spring MVC processing requests
step1. Accept the request
Step 2. Find the controller method that can process the request according to the request information
step3. Parse the request and assemble the values of the parameters required by the controller method
step4. Call the method sent to the controller through reflection
step5, response results, etc
Let's focus on the step 3 parameter value assembly process.
3. Resolve the value of the processor method parameter
Spring MVC has a special interface for parsing the values required by parameters. This interface is: HandlerMethodArgumentResolver, which is called processor placement parameter resolver in Chinese. To put it bluntly, it is to parse the request to get the values of the parameters of the Controller method.
3.1. Processor method parameter resolver: HandlerMethodArgumentResolver interface
public interface HandlerMethodArgumentResolver { /** * Judge whether the current parser supports parsing parameter s * parameter: Method parameter information */ boolean supportsParameter(MethodParameter parameter); /** * Analyze the parameters to get the corresponding values of the parameters */ @Nullable Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer, NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory) throws Exception; }
3.1 process of analyzing parameter values
Multiple handlermethodargumentresolvers will be configured in spring MVC to form a list of handlermethodargumentresolvers. This list is used to parse the parameters to obtain the required values of the parameters, which is equivalent to 2 nested for loops. The simplified process is as follows:
//1. Get the controller parameter list List<MethodParameter> parameterList; //2. Parameter parser list List<HandlerMethodArgumentResolver> handlerMethodArgumentResolverList; //Controller method parameters Object[] handlerMethodArgs = new Object[parameterList.size()]; int paramIndex = 0; //Traversal parameter list for (MethodParameter parameter : parameterList) { //Traversing the parser list of processor method parameters for (HandlerMethodArgumentResolver resolver : handlerMethodArgumentResolverList) { if (resolver.supportsParameter(parameter)) { handlerMethodArgs[paramIndex++] = resolver.resolveArgument(parameter, webRequest, binderFactory); break; } } }
Location of parameter source code:
org.springframework.web.method.support.InvocableHandlerMethod#getMethodArgumentValues
4. Common HandlerMethodArgumentResolver
You can set breakpoints at InvocableHandlerMethod#getMethodArgumentValues to learn more about the parameter parsing process. In debug, we can see that so many parsers are registered in spring MVC by default, as shown in the following figure:
The following table lists some common parameters and the characteristics and types of parameters that can be parsed by these parameter parsers
Implementation class | Supported parameter types | Parameter value |
---|---|---|
RequestParamMethodArgumentResolver | The parameter needs to be marked with @ RequestParam, and the name attribute has a value. The parameter is usually of normal type and Map type; Or MultipartFile, Part type, or MultipartFile, Part collection and array | Request parameters |
RequestParamMapMethodArgumentResolver | The parameter needs to be marked with @ RequestParam, and the name attribute has no child, and the parameter is of Map type; The value of the parameter is taken from the parameter of the request. The key in the Map corresponds to the parameter name, and the value corresponds to the value of the parameter | Request parameters |
PathVariableMapMethodArgumentResolver | The parameter needs to be marked with @ PathVariable, and the parameter is usually of common type | Value from url |
RequestHeaderMethodArgumentResolver | Parameters need to be marked with @ RequestHeader. Parameters are usually Map, MultiValueMap and HttpHeaders | Request header |
ServletCookieValueMethodArgumentResolver | The parameter needs to be marked with @ Cookie value. The parameter is of normal type or Cookie type | cookie |
ModelMethodProcessor | The parameter is of Model type. The controller can call model.addAttribute to put data in the Model. Finally, these data will be copied to the request through request.setAttribute | From spring MVC container |
MapMethodProcessor | The parameter is of Map type, and the value is the same as ModelMethodProcessor | From spring MVC container |
ModelAttributeMethodProcessor | Parameters need to be annotated with @ ModelAttribute | Model.getAttribute |
ServletRequestMethodArgumentResolver | The parameter types are WebRequest, ServletRequest, MultipartRequest, HttpSession, Principal, InputStream, Reader, HttpMethod, Locale, TimeZone and ZoneId | request in Servlet container |
ServletResponseMethodArgumentResolver | The parameter types are ServletResponse, OutputStream and Writer | response in Servlet container |
ModelMethodProcessor | The parameter is of type org.springframework.ui.Model | From spring MVC container |
RequestAttributeMethodArgumentResolver | Parameter requires @ RequestAttribute | request.getAttribute |
SessionAttributeMethodArgumentResolver | Parameter requires @ SessionAttribute | session.getAttribute |
ExpressionValueMethodArgumentResolver | Parameters need to be annotated with @ Value | Take value from Spring configuration |
ServletModelAttributeMethodProcessor | It supports assigning values to our custom JavaBeans | - |
RequestResponseBodyMethodProcessor | Parameters need to be annotated with @ RequestBody | body in http request |
HttpEntityMethodProcessor | The parameter type is HttpEntity or RequestEntity. The parameters of these two types basically contain all the parameter information of the request | Complete information in http request |
There are many implementation classes, so I won't talk about them one by one. Here is a trick to teach you how to look at the source code of each parameter parser. After you master the source code, you can go through the source code of each implementation class and basically know how to use it. Here, take the source code of RequestParamMethodArgumentResolver as an example.
5. Interpretation of RequestParamMethodArgumentResolver source code
5.1. supportsParameter method: judge the supported parameter type
The source code is as follows. It's very simple. Please pay attention to the notes and understand it in seconds
public boolean supportsParameter(MethodParameter parameter) { //Judge whether there is @ RequestParam annotation on the parameter if (parameter.hasParameterAnnotation(RequestParam.class)) { //The parameter is of type Map if (Map.class.isAssignableFrom(parameter.nestedIfOptional().getNestedParameterType())) { //@RequestParam annotation name must have a value RequestParam requestParam = parameter.getParameterAnnotation(RequestParam.class); return (requestParam != null && StringUtils.hasText(requestParam.name())); } else { return true; } } else { //Judge whether there is @ RequestPart annotation on the parameter, and return false if there is if (parameter.hasParameterAnnotation(RequestPart.class)) { return false; } parameter = parameter.nestedIfOptional(); /** * Parameter whether wechat is of the following types. This type is usually used to accept parameters when uploading files * MultipartFile,Collection<MultipartFile>,List<MultipartFile>,MultipartFile[] * Part,Collection<Part>,List<Part>,Part[] */ if (MultipartResolutionDelegate.isMultipartArgument(parameter)) { return true; } else if (this.useDefaultResolution) { // Whether default resolution is enabled. useDefaultResolution is false by default return BeanUtils.isSimpleProperty(parameter.getNestedParameterType()); } else { return false; } } }
5.2 resolveArgument method
The resolveArgument method will eventually call the RequestParamMethodArgumentResolver#resolveName method. The code is as follows. If the file is uploaded, the MultipartFile object will be obtained. Otherwise, request.getParameterValues will be called to get values from the parameters
protected Object resolveName(String name, MethodParameter parameter, NativeWebRequest request) throws Exception { HttpServletRequest servletRequest = request.getNativeRequest(HttpServletRequest.class); Object arg = null; MultipartRequest multipartRequest = request.getNativeRequest(MultipartRequest.class); if (multipartRequest != null) { List<MultipartFile> files = multipartRequest.getFiles(name); if (!files.isEmpty()) { arg = (files.size() == 1 ? files.get(0) : files); } } if (arg == null) { String[] paramValues = request.getParameterValues(name); if (paramValues != null) { arg = (paramValues.length == 1 ? paramValues[0] : paramValues); } } return arg; }
5. @ RequestParam: get the parameters in the request
5.1 introduction
@We use a lot of requestparameter annotations. The parameters marked by this annotation will be taken from the request parameters of request. The parameter value is request.getparameter ("@ requestparameter annotation name value")
Let's focus on the source code of this class. As follows, you should learn to read the notes in the source code. The spring notes are particularly well written. Here's a praise for spring. The notes explain its usage in detail. Please pay attention to the Kuang Hong part below. Later, a case code will be used to let you understand several other common uses. The usage of this note is mastered, Other annotations are the same. If you look at the origin code and the corresponding parameter parser, you will understand.
@Target(ElementType.PARAMETER) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface RequestParam { /** * Corresponding parameter name in request */ @AliasFor("name") String value() default ""; /** * Same value */ @AliasFor("value") String name() default ""; /** * Must this parameter be present in the request */ boolean required() default true; /** * Default value */ String defaultValue() default ValueConstants.DEFAULT_NONE; }
5.2 cases
The case code is as follows. Pay attention to five parameters, which reflect all usage of @ RequestParam. Two parsers will be used for parameter resolution of this interface: RequestParamMethodArgumentResolver and RequestParamMapMethodArgumentResolver. You can set breakpoint debug.
Note that the type of the last parameter is MultiValueMap, which is equivalent to map < string, list < string > >
@RequestMapping("/test1") @ResponseBody public Map<String, Object> test1(@RequestParam("name") String name, @RequestParam("age") int age, @RequestParam("p1") String[] p1Map, @RequestParam Map<String, String> requestParams1, @RequestParam MultiValueMap requestParams2) { //MultiValueMap is equivalent to map < string, list < string > > Map<String, Object> result = new LinkedHashMap<>(); result.put("name", name); result.put("age", age); result.put("p1Map", p1Map); result.put("requestParams1", requestParams1); result.put("requestParams2", requestParams2); return result; }
Send request
http://localhost:8080/chat17/test1?name=ready&age=35&p1=1&p1=2&p1=3
Interface output
{ "name": "ready", "age": 35, "p1Map": [ "1", "2", "3" ], "requestParams1": { "name": "ready", "age": "35", "p1": "1" }, "requestParams2": { "name": [ "ready" ], "age": [ "35" ], "p1": [ "1", "2", "3" ] } }
7. Summary
This article introduces you to the function of the parameter parser HandlerMethodArgumentResolver. After mastering this, you will know how to write the parameters in the controller method. It is recommended that you go down and turn to the implementation class of this interface to master the various usages of common parameters. Only in this way can you quickly locate the problem and improve your ability to solve the problem quickly.
8. Code location and description
8.1. git address
https://gitee.com/javacode2018/springmvc-series