Background Some time ago, I developed an interface, because my colleague who called my interface had a very good temper, so I was not rude either. I sent the source code directly to him as the interface definition.
Unexpectedly, my colleague saw my code asking either get a,b,c or post [a,b,c].Can this be automatically parsed?They have been converting themselves into list s.
I'm sure I can, but I'm used to it. I don't know the underlying mechanism. In fact, this annotation RequestParam can't be omitted. Normal string parameters can be bound automatically, and internal conversion can't be required.
Parameter Binding Principle
Spring's parameter resolution is done using components of the HandlerMethodArgmentResolver type.Different types are resolved using different ArgumentResolver s.Refer specifically to the source code of the RequestMappingHandlerAdapter class.There is a good way to explain this:
// Get Default HandlerMethodArgumentResolver private List<HandlerMethodArgumentResolver> getDefaultArgumentResolvers() { List<HandlerMethodArgumentResolver> resolvers = new ArrayList<HandlerMethodArgumentResolver>(); // 1.Annotation-based parameter resolution <-- Parsed data sources are mainly HttpServletRequest | ModelAndViewContainer // Annotation-based argument resolution // Resolve Annotated @RequestParam, @RequestPart Modified parameters, Data acquisition via HttpServletRequest.getParameterValues resolvers.add(new RequestParamMethodArgumentResolver(getBeanFactory(), false)); // Resolve Annotated @RequestParam Modification, And the type is Map Parameters, Data acquisition via HttpServletRequest.getParameterMap resolvers.add(new RequestParamMapMethodArgumentResolver()); // Resolve Annotated @PathVariable Modification, Data acquisition via uriTemplateVars, and uriTemplateVars But through RequestMappingInfoHandlerMapping.handleMatch generate, In fact, it is uri Mapped in key <-> value resolvers.add(new PathVariableMethodArgumentResolver()); // Resolve Annotated @PathVariable Modified and data type is Map, Data acquisition via uriTemplateVars, and uriTemplateVars But through RequestMappingInfoHandlerMapping.handleMatch generate, In fact, it is uri Mapped in key <-> value resolvers.add(new PathVariableMapMethodArgumentResolver()); // Resolve Annotated @MatrixVariable Modification, Data acquisition via URI Extracted;Post-stored uri template Variable Value resolvers.add(new MatrixVariableMethodArgumentResolver()); // Resolve Annotated @MatrixVariable Modified and data type is Map, Data acquisition via URI Extracted;Post-stored uri template Variable Value resolvers.add(new MatrixVariableMapMethodArgumentResolver()); // Resolve Annotated @ModelAttribute Modification, And the type is Map Parameters, Data acquisition via ModelAndViewContainer Obtain, adopt DataBinder Make a binding resolvers.add(new ServletModelAttributeMethodProcessor(false)); // Resolve Annotated @RequestBody Modified parameters, And by@ResponseBody Modified Return Value, Data acquisition via HttpServletRequest Obtain, according to MediaType adopt HttpMessageConverter Convert to corresponding format, The return value is also handled by MediaType Choose the right one HttpMessageConverter, Convert Format, And output resolvers.add(new RequestResponseBodyMethodProcessor(getMessageConverters(), this.requestResponseBodyAdvice)); // Resolve Annotated @RequestPart Modification, Data acquisition via HttpServletRequest.getParts() resolvers.add(new RequestPartMethodArgumentResolver(getMessageConverters(), this.requestResponseBodyAdvice)); // Resolve Annotated @RequestHeader Modification, Data acquisition via HttpServletRequest.getHeaderValues() resolvers.add(new RequestHeaderMethodArgumentResolver(getBeanFactory())); // Resolve Annotated @RequestHeader Modified and parameter type is Map, Data acquisition via HttpServletRequest.getHeaderValues() resolvers.add(new RequestHeaderMapMethodArgumentResolver()); // Resolve Annotated @CookieValue Modification, Data acquisition via HttpServletRequest.getCookies() resolvers.add(new ServletCookieValueMethodArgumentResolver(getBeanFactory())); // Resolve Annotated @Value Modification, Data is not parsed here resolvers.add(new ExpressionValueMethodArgumentResolver(getBeanFactory())); // Resolve Annotated @SessionAttribute Modification, Data acquisition via HttpServletRequest.getAttribute(name, RequestAttributes.SCOPE_SESSION) resolvers.add(new SessionAttributeMethodArgumentResolver()); // Resolve Annotated @RequestAttribute Modification, Data acquisition via HttpServletRequest.getAttribute(name, RequestAttributes.SCOPE_REQUEST) resolvers.add(new RequestAttributeMethodArgumentResolver()); // 2.Type-based parameter parser // Type-based argument resolution // Resolving fixed type parameters(such as: ServletRequest, HttpSession, InputStream etc.), Is the parameter's data obtained or passed HttpServletRequest resolvers.add(new ServletRequestMethodArgumentResolver()); // Resolving fixed type parameters(such as: ServletResponse, OutputStream etc.), Is the parameter's data obtained or passed HttpServletResponse resolvers.add(new ServletResponseMethodArgumentResolver()); // Resolving fixed type parameters(such as: HttpEntity, RequestEntity etc.), Is the parameter's data obtained or passed HttpServletRequest resolvers.add(new HttpEntityMethodProcessor(getMessageConverters(), this.requestResponseBodyAdvice)); // Resolving fixed type parameters(such as: RedirectAttributes), Is the parameter's data obtained or passed HttpServletResponse resolvers.add(new RedirectAttributesMethodArgumentResolver()); // Resolving fixed type parameters(such as: Model etc.), Parameter data acquisition via ModelAndViewContainer resolvers.add(new ModelMethodProcessor()); // Resolving fixed type parameters(such as: Model etc.), Parameter data acquisition via ModelAndViewContainer resolvers.add(new MapMethodProcessor()); // Resolving fixed type parameters(such as: Errors), Parameter data acquisition via ModelAndViewContainer resolvers.add(new ErrorsMethodArgumentResolver()); // Resolving fixed type parameters(such as: SessionStatus), Parameter data acquisition via ModelAndViewContainer resolvers.add(new SessionStatusMethodArgumentResolver()); // Resolving fixed type parameters(such as: UriComponentsBuilder), Parameter data acquisition via HttpServletRequest resolvers.add(new UriComponentsBuilderMethodArgumentResolver()); // 3.Custom parameter parser // Custom arguments if (getCustomArgumentResolvers() != null) { resolvers.addAll(getCustomArgumentResolvers()); } // Catch-all //These two parsers can parse all types of parameters resolvers.add(new RequestParamMethodArgumentResolver(getBeanFactory(), true)); resolvers.add(new ServletModelAttributeMethodProcessor(true)); return resolvers; }
After tracking the source code one step at a time, eventually there is a private method for createDefaultEditors in the PropertyEditorRegistrySupport class.There are various types of conversion defined:
private void createDefaultEditors() { this.defaultEditors = new HashMap(64); this.defaultEditors.put(Charset.class, new CharsetEditor()); this.defaultEditors.put(Class.class, new ClassEditor()); this.defaultEditors.put(Class[].class, new ClassArrayEditor()); this.defaultEditors.put(Currency.class, new CurrencyEditor()); this.defaultEditors.put(File.class, new FileEditor()); this.defaultEditors.put(InputStream.class, new InputStreamEditor()); this.defaultEditors.put(InputSource.class, new InputSourceEditor()); this.defaultEditors.put(Locale.class, new LocaleEditor()); if(pathClass != null) { this.defaultEditors.put(pathClass, new PathEditor()); } this.defaultEditors.put(Pattern.class, new PatternEditor()); this.defaultEditors.put(Properties.class, new PropertiesEditor()); this.defaultEditors.put(Reader.class, new ReaderEditor()); this.defaultEditors.put(Resource[].class, new ResourceArrayPropertyEditor()); this.defaultEditors.put(TimeZone.class, new TimeZoneEditor()); this.defaultEditors.put(URI.class, new URIEditor()); this.defaultEditors.put(URL.class, new URLEditor()); this.defaultEditors.put(UUID.class, new UUIDEditor()); if(zoneIdClass != null) { this.defaultEditors.put(zoneIdClass, new ZoneIdEditor()); } this.defaultEditors.put(Collection.class, new CustomCollectionEditor(Collection.class)); this.defaultEditors.put(Set.class, new CustomCollectionEditor(Set.class)); this.defaultEditors.put(SortedSet.class, new CustomCollectionEditor(SortedSet.class)); this.defaultEditors.put(List.class, new CustomCollectionEditor(List.class)); this.defaultEditors.put(SortedMap.class, new CustomMapEditor(SortedMap.class)); this.defaultEditors.put(byte[].class, new ByteArrayPropertyEditor()); this.defaultEditors.put(char[].class, new CharArrayPropertyEditor()); this.defaultEditors.put(Character.TYPE, new CharacterEditor(false)); this.defaultEditors.put(Character.class, new CharacterEditor(true)); this.defaultEditors.put(Boolean.TYPE, new CustomBooleanEditor(false)); this.defaultEditors.put(Boolean.class, new CustomBooleanEditor(true)); this.defaultEditors.put(Byte.TYPE, new CustomNumberEditor(Byte.class, false)); this.defaultEditors.put(Byte.class, new CustomNumberEditor(Byte.class, true)); this.defaultEditors.put(Short.TYPE, new CustomNumberEditor(Short.class, false)); this.defaultEditors.put(Short.class, new CustomNumberEditor(Short.class, true)); this.defaultEditors.put(Integer.TYPE, new CustomNumberEditor(Integer.class, false)); this.defaultEditors.put(Integer.class, new CustomNumberEditor(Integer.class, true)); this.defaultEditors.put(Long.TYPE, new CustomNumberEditor(Long.class, false)); this.defaultEditors.put(Long.class, new CustomNumberEditor(Long.class, true)); this.defaultEditors.put(Float.TYPE, new CustomNumberEditor(Float.class, false)); this.defaultEditors.put(Float.class, new CustomNumberEditor(Float.class, true)); this.defaultEditors.put(Double.TYPE, new CustomNumberEditor(Double.class, false)); this.defaultEditors.put(Double.class, new CustomNumberEditor(Double.class, true)); this.defaultEditors.put(BigDecimal.class, new CustomNumberEditor(BigDecimal.class, true)); this.defaultEditors.put(BigInteger.class, new CustomNumberEditor(BigInteger.class, true)); if(this.configValueEditorsActive) { StringArrayPropertyEditor sae = new StringArrayPropertyEditor(); this.defaultEditors.put(String[].class, sae); this.defaultEditors.put(short[].class, sae); this.defaultEditors.put(int[].class, sae); this.defaultEditors.put(long[].class, sae); } }
From the above method, you can see which types of automatic replacements are supported by default.The source code for the intermediate process is not identical.
Summarize the process of parsing bindings for parameters
1. When SpringMVC is initialized, the RequestMappingHanderAdapter class adds some default parameter parsers to argumentResolvers.When SpringMVC receives the request, it first looks for the corresponding HandlerMethod based on the url.
2. Traverse the MethodParameter array of the HandlerMethod.
3. Find out which HandlerMethodArgumentResolver to confirm using based on the type of MethodParameter.
4. Parse the parameters, parse the parameters corresponding to MethodParameter from the request, and the results are strings.
5. Conversion parameters. PropertyEditorRegistrySupport converts a String to the type required for a specific method when using DataBinder, which includes basic types, objects, lists, and so on.