In the project, LocalDateTime series was used as the data type of the time in DTO, but SpringMVC always reported an error after receiving the parameter. In order to configure the global time type conversion, it tried the following processing method.

Note: This article is based on the SpringBoot2.x test. If it does not take effect, it may be due to the earlier spring version. If you don’t include any annotations (RequestParam, PathVariable, etc.) for LocalDate parameters in your Controller, this will also cause an error, because by default, this parameter is parsed using RequestParamModelAttributeMethodProcessorThe LocalDate class does not have a constructor, so it cannot reflect the instantiation of an object.

Meet the target

  • Get, POST (content-type=application/json)
  • Returns data converted to the specified date-time format character for type Date
  • Support for Java8 date apis such as:LocalTime,localDateLocalDateTime

GET request and POST form date-time string format conversion

This situation is treated differently from time as a Json string because the underlying Json serialization tool Jackson (HttpMessgeConverter) is used to convert front-end Json to back-end POJOs. When the time string is passed in as a normal request parameter, the Converter is used for conversion, which is handled differently.

Using A Custom Parameter Converter (Converter)

Implement org. Springframework. Core. The convert. Converter. The converter, converter, custom parameters are as follows:

@Configuration
public class DateConverterConfig {
    @Bean
    public Converter<String, LocalDate> localDateConverter(a) {
      	return new Converter<String, LocalDate>() {
            @Override
            public LocalDate convert(String source) {
                return LocalDate.parse(source, DateTimeFormatter.ofPattern("yyyy-MM-dd")); }}; }@Bean
    public Converter<String, LocalDateTime> localDateTimeConverter(a) {
        return new Converter<String, LocalDateTime>() {
            @Override
            public LocalDateTime convert(String source) {
                return LocalDateTime.parse(source, DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")); }}; }}Copy the code

Comments: the above two beans will be injected into the parameters of the spring MVC parser (like called ParameterConversionService), when the incoming string to be converted to LocalDateTime class, the spring will call the Converter of the transformed into the refs.

Note: I ran into a bug with the custom argument Converter Converter, which I wrote in detail here. My original idea was to simplify the above anonymous inner class into a lambda expression:

    @Bean
    @ConditionalOnBean(name = "requestMappingHandlerAdapter")
    public Converter<String, LocalDate> localDateConverter(a) {
        return source -> LocalDate.parse(source, DateTimeFormatter.ofPattern(DEFAULT_DATE_FORMAT));
    }
Copy the code

When I started the project again, there was an exception:

Caused by: java.lang.IllegalArgumentException: Unable to determine source type <S> and target type <T> for your Converter [com.example.demo126.config.MappingConverterAdapter$$LambdaThe $522/ 817994751]; does the class parameterize those types?Copy the code

Puzzled, in the reference to the data that one or two:

The web project start registered requestMappingHandlerAdapter will initialize WebBindingInitializer

adapter.setWebBindingInitializer(getConfigurableWebBindingInitializer());
Copy the code

But ConfigurableWebBindingInitializer need FormattingConversionService, but FormattingConversionService will add all of the Converter can come in, To add, you need to get generic information:

@Override
public void addFormatters(FormatterRegistry registry) {
    for(Converter<? ,? > converter : getBeansOfType(Converter.class)) { registry.addConverter(converter); }for (GenericConverter converter : getBeansOfType(GenericConverter.class)) {
      	registry.addConverter(converter);
    }
    for (Formatter<?> formatter : getBeansOfType(Formatter.class)) {
      	registry.addFormatter(formatter);
    }
}
Copy the code

Adding Converter. Class is generally done by getting the concrete types of two generics through the interface

public ResolvableType as(Class
        type) {
    if (this == NONE) {
      returnNONE; } Class<? > resolved = resolve();if (resolved == null || resolved == type) {
      return this;
    }
    for (ResolvableType interfaceType : getInterfaces()) {
      ResolvableType interfaceAsType = interfaceType.as(type);
      if(interfaceAsType ! = NONE) {returninterfaceAsType; }}return getSuperType().as(type);
}
Copy the code

The interface to Lambda expressions is Converter, so we can’t get the specific type. After snooping the source code of SpringMVC, we know that this is the case. Now that we know the reason, the solution is as follows:

  • The easiest thing to do is not to apply Lambda expressions, and just use anonymous inner classes, so you don’t have these problems

  • Or is requestMappingHandlerAdapterbean registration completed then add your own converter will not register in the FormattingConversionService

    @Bean
    @ConditionalOnBean(name = "requestMappingHandlerAdapter")
    public Converter<String, LocalDateTime> localDateTimeConverter(a) {
      return source -> LocalDateTime.parse(source, DateTimeUtils.DEFAULT_FORMATTER);
    }
    Copy the code

You can also match the string passed by the front end, for example, YYYY-MM-DD HH: MM :ss, YYYY-MM-DD, and HH: MM :ss. Adapt to a variety of scenarios.

@Component
public class DateConverter implements Converter<String.Date> {
    @Override
    public Date convert(String value) {
        /** * Select * from hutool; /** * select * from hutool; Made wheels will not repeat here * cn hutool. Core. Date. DateUtil *@param value
         * @return* /
        returnDateUtil.parse(value.trim()); }}Copy the code

* * note: A lazy * * here I am, in the match Date Date format directly using the hutool write good analytical tools, for we have here make the wheels will not repeat, the following method to use the same tools, want to use this tool in your project class is also very simple, in the project in the pom file introducing hutool dependence is ok, As follows:

<! -- Hu Tool -->
<dependency>
  <groupId>cn.hutool</groupId>
  <artifactId>hutool-all</artifactId>
  <version>5.1.3</version>
</dependency>
Copy the code

Using Spring annotations

Use the spring annotation @dateTimeformat (pattern = “YYYY-MM-DD “) as follows:

@DateTimeFormat(pattern = "yyyy-MM-dd")
private Date startDate;
Copy the code

If a custom parameter converter is used, Spring will preferentially process it, meaning that Spring annotations do not take effect.

Use ControllerAdvice with initBinder

@ControllerAdvice
public class GlobalExceptionHandler {

    @InitBinder
    protected void initBinder(WebDataBinder binder) {
        binder.registerCustomEditor(LocalDate.class, new PropertyEditorSupport() {
            @Override
            public void setAsText(String text) throws IllegalArgumentException {
                setValue(LocalDate.parse(text, DateTimeFormatter.ofPattern("yyyy-MM-dd"))); }}); binder.registerCustomEditor(LocalDateTime.class,new PropertyEditorSupport() {
            @Override
            public void setAsText(String text) throws IllegalArgumentException {
                setValue(LocalDateTime.parse(text, DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"))); }}); binder.registerCustomEditor(LocalTime.class,new PropertyEditorSupport() {
            @Override
            public void setAsText(String text) throws IllegalArgumentException {
                setValue(LocalTime.parse(text, DateTimeFormatter.ofPattern("HH:mm:ss"))); }}); }}Copy the code

As the name suggests, this is doing the loop cutting on the Controller (which also allows global exception catching), converting the parameter before it goes to handler; Convert to our corresponding object.

JSON input parameters and return values are handled globally

The request type is POST, content-Type = Application /json. The @requestBody is used to receive the request. The default format for receiving and returning the request is YYYY-MM-DD HH: MM: SS

Modify the application.yml file

Add the following to the application.propertities file:

spring:
	jackson:
		date-format: yyyy-MM-dd HH:mm:ss
		time-zone: GMT+8
Copy the code
  • The format of the request (Content-type =application/json) isyyyy-MM-dd HH:mm:ssString, used in the background@RequestBodyReceives, and returns the value date toyyyy-MM-dd HH:mm:ssThe format string.
  • The string of the type YYYY-MM-DD in the request (Content-Type =application/json) cannot be converted to date.
  • Java8 date API not supported;

Use Jackson’s JSON serialization and deserialization

@Configuration
public class JacksonConfig {

    /** The default date and time format */
    public static final String DEFAULT_DATE_TIME_FORMAT = "yyyy-MM-dd HH:mm:ss";
    /** Default date format */
    public static final String DEFAULT_DATE_FORMAT = "yyyy-MM-dd";
    /** Default time format */
    public static final String DEFAULT_TIME_FORMAT = "HH:mm:ss";

    @Bean
    public MappingJackson2HttpMessageConverter mappingJackson2HttpMessageConverter(a) {
        MappingJackson2HttpMessageConverter converter = new MappingJackson2HttpMessageConverter();
        ObjectMapper objectMapper = new ObjectMapper();

        // Ignore other attributes in the JSON string
        objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
        // Ignore objects that cannot be converted
        objectMapper.configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false);
        // PrettyPrinter formats output
        objectMapper.configure(SerializationFeature.INDENT_OUTPUT, true);
        // NULL does not participate in serialization
        objectMapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);

        // Specify the time zone
        objectMapper.setTimeZone(TimeZone.getTimeZone("GMT+8:00"));
        // Date type string processing
        objectMapper.setDateFormat(new SimpleDateFormat(DEFAULT_DATE_TIME_FORMAT));

        Java8 date date processing
        JavaTimeModule javaTimeModule = new JavaTimeModule();
        javaTimeModule.addSerializer(LocalDateTime.class, new LocalDateTimeSerializer(DateTimeFormatter.ofPattern(DEFAULT_DATE_TIME_FORMAT)));
        javaTimeModule.addSerializer(LocalDate.class, new LocalDateSerializer(DateTimeFormatter.ofPattern(DEFAULT_DATE_FORMAT)));
        javaTimeModule.addSerializer(LocalTime.class, new LocalTimeSerializer(DateTimeFormatter.ofPattern(DEFAULT_TIME_FORMAT)));
        javaTimeModule.addDeserializer(LocalDateTime.class, new LocalDateTimeDeserializer(DateTimeFormatter.ofPattern(DEFAULT_DATE_TIME_FORMAT)));
        javaTimeModule.addDeserializer(LocalDate.class, new LocalDateDeserializer(DateTimeFormatter.ofPattern(DEFAULT_DATE_FORMAT)));
        javaTimeModule.addDeserializer(LocalTime.class, new LocalTimeDeserializer(DateTimeFormatter.ofPattern(DEFAULT_TIME_FORMAT)));
        objectMapper.registerModule(javaTimeModule);

        converter.setObjectMapper(objectMapper);
        returnconverter; }}Copy the code

Conclusion:

  • The format of the request (Content-type =application/json) isyyyy-MM-dd HH:mm:ssString, used in the background@RequestBodyReceives, and returns the value Date toyyyy-MM-dd HH:mm:ssThe format String.
  • Support for java8 date apis;
  • The content-type=application/json request is not supportedyyyy-MM-ddA string of the same type is converted to Date;

The above two methods are for global processing of JSON input parameters, and the second method is recommended, especially suitable for global setting in the basic package of large projects.

JSON input parameters and return values are locally differentiated

Scenario: Suppose that the global date and time processing format is YYYY-MM-DD HH: MM: SS, but a field requires the date YYYY-MM-DD to be received or returned.

Methods a

Use the springBoot annotation @jsonFormat (pattern = “YYYY-MM-DD “) as follows:

@JsonFormat(pattern = "yyyy-MM-dd", timezone="GMT+8")
private Date releaseDate;
Copy the code

Comments: SpringBoot is provided by default. It is powerful and can be used in common scenarios. You can specify a time zone.

Way 2

Custom date serialization and deserialization, as shown below:

/** * date serialization */
public class DateJsonSerializer extends JsonSerializer<Date> {
    @Override
    public void serialize(Date date, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) throws IOException {
        SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd"); jsonGenerator.writeString(dateFormat.format(date)); }}/** * date deserialization */
public class DateJsonDeserializer extends JsonDeserializer<Date> {
    @Override
    public Date deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) throws IOException {
        try {
            SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd");
            return dateFormat.parse(jsonParser.getText());
        } catch (ParseException e) {
            throw newRuntimeException(e); }}}/** ** Use mode */
@JsonSerialize(using = DateJsonSerializer.class)
@JsonDeserialize(using = DateJsonDeserializer.class)
private Date releaseDate;
Copy the code

Date and time formatting is fully configured

@Configuration
public class DateHandlerConfig {

    /** The default date and time format */
    public static final String DEFAULT_DATE_TIME_FORMAT = "yyyy-MM-dd HH:mm:ss";
    /** Default date format */
    public static final String DEFAULT_DATE_FORMAT = "yyyy-MM-dd";
    /** Default time format */
    public static final String DEFAULT_TIME_FORMAT = "HH:mm:ss";

    /** * LocalDate converter, used to convert RequestParam and PathVariable arguments * '@ConditionalOnBean(name = "requestMappingHandlerAdapter")`: Such as requestMappingHandlerAdapter bean registration completed * add their own ` converter ` wouldn't have to register ` FormattingConversionService ` * /
    @Bean
    @ConditionalOnBean(name = "requestMappingHandlerAdapter")
    public Converter<String, LocalDate> localDateConverter(a) {
        return source -> LocalDate.parse(source, DateTimeFormatter.ofPattern(DEFAULT_DATE_FORMAT));
    }

    /** * LocalDateTime converter to convert RequestParam and PathVariable arguments */
    @Bean
    @ConditionalOnBean(name = "requestMappingHandlerAdapter")
    public Converter<String, LocalDateTime> localDateTimeConverter(a) {
        return source -> LocalDateTime.parse(source, DateTimeFormatter.ofPattern(DEFAULT_DATE_TIME_FORMAT));
    }

    /** * LocalTime converter to convert RequestParam and PathVariable arguments */
    @Bean
    @ConditionalOnBean(name = "requestMappingHandlerAdapter")
    public Converter<String, LocalTime> localTimeConverter(a) {
        return source -> LocalTime.parse(source, DateTimeFormatter.ofPattern(DEFAULT_TIME_FORMAT));
    }

    /** * Date converter, used to convert RequestParam and PathVariable arguments ** Hutool's Date parser class */
    @Bean
    public Converter<String, Date> dateConverter(a) {
        return new Converter<String, Date>() {
            @Override
            public Date convert(String source) {
                returnDateUtil.parse(source.trim()); }}; }/** * Json serialization and deserialization converters for converting THE Json in the BODY of the Post request and serializing our object to the Json that returns the response */
    @Bean
    public ObjectMapper objectMapper(a){
        ObjectMapper objectMapper = new ObjectMapper();
        objectMapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
        objectMapper.disable(DeserializationFeature.ADJUST_DATES_TO_CONTEXT_TIME_ZONE);

        //LocalDateTime serialization and deserialization modules, inherited from JSR310, where we modified the date format
        JavaTimeModule javaTimeModule = new JavaTimeModule();
        javaTimeModule.addSerializer(LocalDateTime.class,new LocalDateTimeSerializer(DateTimeFormatter.ofPattern(DEFAULT_DATE_TIME_FORMAT)));
        javaTimeModule.addSerializer(LocalDate.class,new LocalDateSerializer(DateTimeFormatter.ofPattern(DEFAULT_DATE_FORMAT)));
        javaTimeModule.addSerializer(LocalTime.class,new LocalTimeSerializer(DateTimeFormatter.ofPattern(DEFAULT_TIME_FORMAT)));
        javaTimeModule.addDeserializer(LocalDateTime.class,new LocalDateTimeDeserializer(DateTimeFormatter.ofPattern(DEFAULT_DATE_TIME_FORMAT)));
        javaTimeModule.addDeserializer(LocalDate.class,new LocalDateDeserializer(DateTimeFormatter.ofPattern(DEFAULT_DATE_FORMAT)));
        javaTimeModule.addDeserializer(LocalTime.class,new LocalTimeDeserializer(DateTimeFormatter.ofPattern(DEFAULT_TIME_FORMAT)));


        //Date serialization and deserialization
        javaTimeModule.addSerializer(Date.class, new JsonSerializer<>() {
            @Override
            public void serialize(Date date, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) throws IOException {
                SimpleDateFormat formatter = newSimpleDateFormat(DEFAULT_DATE_TIME_FORMAT); String formattedDate = formatter.format(date); jsonGenerator.writeString(formattedDate); }}); javaTimeModule.addDeserializer(Date.class,new JsonDeserializer<>() {
            @Override
            public Date deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) throws IOException, JsonProcessingException {
                SimpleDateFormat format = new SimpleDateFormat(DEFAULT_DATE_TIME_FORMAT);
                String date = jsonParser.getText();
                try {
                    return format.parse(date);
                } catch (ParseException e) {
                    throw newRuntimeException(e); }}}); objectMapper.registerModule(javaTimeModule);returnobjectMapper; }}Copy the code

Delve into the SpringMVC data binding process

Let’s go into debug mode and see how MVC binds our request parameters to our Controller layer method inputs.

Write a simple controller and set a breakpoint to see the method call stack:

    @GetMapping("/getDate")
    public LocalDateTime getDate(@RequestParam LocalDate date, @RequestParam LocalDateTime dateTime, @RequestParam Date originalDate) {
        System.out.println(date);
        System.out.println(dateTime);
        System.out.println(originalDate);
        return LocalDateTime.now();
    }
Copy the code

With the interface called, let’s look at some key methods in the method call stack:

/ / into the DispatcherServlet
doService:942, DispatcherServlet
// Process the request
doDispatch:1038, DispatcherServlet
// Generate call chain (preprocessing, actual calling method, postprocessing)
handle:87, AbstractHandlerMethodAdapter
// Reflection gets the actual calling method and is ready to start calling
invokeHandlerMethod:895, RequestMappingHandlerAdapter
invokeAndHandle:102, ServletInvocableHandlerMethod
// Here is the key, the parameters are obtained from here
invokeForRequest:142, InvocableHandlerMethod
doInvoke:215, InvocableHandlerMethod
// This is a Java Reflect call, so the argument must have been retrieved before that
invoke:566, Method
Copy the code

InvokeForRequest :142, InvocableHandlerMethod invokeForRequest:142, InvocableHandlerMethod

    @Nullable
    public Object invokeForRequest(NativeWebRequest request, @Nullable ModelAndViewContainer mavContainer, Object... providedArgs) throws Exception {
        // This method is used to get parameters, the next break here
        Object[] args = getMethodArgumentValues(request, mavContainer, providedArgs);
        if (logger.isTraceEnabled()) {
            logger.trace("Arguments: " + Arrays.toString(args));
        }
        // start calling the method here
        return doInvoke(args);
    }
Copy the code

Enter this method to see what the operation is:

protected Object[] getMethodArgumentValues(NativeWebRequest request, @Nullable ModelAndViewContainer mavContainer, Object... providedArgs) throws Exception {
    // Get an array of method parameters, containing input information such as type, generics, and so on
    MethodParameter[] parameters = getMethodParameters();
    // This is used to store the parameters converted from the request parameter
    Object[] args = new Object[parameters.length];
    for (int i = 0; i < parameters.length; i++) {
      MethodParameter parameter = parameters[i];
      parameter.initParameterNameDiscovery(this.parameterNameDiscoverer);
      // It looks like there is no egg here (providedArgs is empty)
      args[i] = resolveProvidedArgument(parameter, providedArgs);
      // Here we begin to get the parameters of the actual method call, step
      if (this.argumentResolvers.supportsParameter(parameter)) {
        // The parameter parser parses parameters
        args[i] = this.argumentResolvers.resolveArgument(parameter, mavContainer, request, this.dataBinderFactory);
        continue; }}return args;
}
Copy the code

Enter resolveArgument to see:

public Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer, NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory) throws Exception {
  // Get the corresponding parser according to the method input parameter
  HandlerMethodArgumentResolver resolver = getArgumentResolver(parameter);
  // Start parsing the parameters (convert the parameter in the request to the method input)
  return resolver.resolveArgument(parameter, mavContainer, webRequest, binderFactory);
}
Copy the code

Here’s how to get the corresponding parameter parser based on the parameter:

// Walk through, call the supportParameter method, and follow up
for (HandlerMethodArgumentResolver methodArgumentResolver : this.argumentResolvers) {
  if (methodArgumentResolver.supportsParameter(parameter)) {
    result = methodArgumentResolver;
    this.argumentResolverCache.put(parameter, result);
    break; }}Copy the code

Here, walk through the parameter parsers to see if there are any suitable parsers! So, what parameter parsers are there (26 at the time OF my testing)? Let me list a few important ones to see if they look familiar!!

{RequestParamMethodArgumentResolver@7686} 
{PathVariableMethodArgumentResolver@8359} 
{RequestResponseBodyMethodProcessor@8366} 
{RequestPartMethodArgumentResolver@8367} 
Copy the code

We went to one of the most common parsers to look at its supportsParameter method and found that the corresponding parser is obtained through parameter annotations.

    public boolean supportsParameter(MethodParameter parameter) {
        // If the parameter has the @requestParam annotation, go to this branch.
        if (parameter.hasParameterAnnotation(RequestParam.class)) {
            // This seems to handle arguments of type Optional
            if (Map.class.isAssignableFrom(parameter.nestedIfOptional().getNestedParameterType())) {
                RequestParam requestParam = parameter.getParameterAnnotation(RequestParam.class);
                return(requestParam ! =null && StringUtils.hasText(requestParam.name()));
            }
            else {
                return true; }}/ /...
    }
Copy the code

That is, SpringMVC uses a different parameter parser for data binding for @RequestParam and @RequestBody as well as @PathVariable annotations! So what Converter parameters do these three parsers use? Let’s take a look at three parsers: First look at the RequestParamMethodArgumentResolver found internal use WebDataBinder for data binding, the underlying use ConversionService (also is the place where our Converter injection)

WebDataBinder binder = binderFactory.createBinder(webRequest, null, namedValueInfo.name);
// Data binding via DataBinder
arg = binder.convertIfNecessary(arg, parameter.getParameterType(), parameter);
Copy the code
/ / follow up convertIfNecessary ()
public <T> T convertIfNecessary(@Nullable Object value, @Nullable Class
       
         requiredType, @Nullable MethodParameter methodParam)
        throws TypeMismatchException {

  return getTypeConverter().convertIfNecessary(value, requiredType, methodParam);
}
Copy the code
// Continue to follow up, see
ConversionService conversionService = this.propertyEditorRegistry.getConversionService();
if (editor == null&& conversionService ! =null&& newValue ! =null&& typeDescriptor ! =null) {
  TypeDescriptor sourceTypeDesc = TypeDescriptor.forObject(newValue);
  if (conversionService.canConvert(sourceTypeDesc, typeDescriptor)) {
    try {
      return (T) conversionService.convert(newValue, sourceTypeDesc, typeDescriptor);
    }
    catch (ConversionFailedException ex) {
      // fallback to default conversion logic belowconversionAttemptEx = ex; }}}Copy the code

Then look at theRequestResponseBodyMethodProcessorFound that the converter used is of type HttpMessageConverter:

// the resolveArgument method is called internally
Object arg = readWithMessageConverters(webRequest, parameter, parameter.getNestedGenericParameterType());

/ / step into readWithMessageConverters (), we see the Converter is HttpMessageConverter
for(HttpMessageConverter<? > converter :this.messageConverters) { Class<HttpMessageConverter<? >> converterType = (Class<HttpMessageConverter<? >>) converter.getClass(); GenericHttpMessageConverter<? > genericConverter = (converterinstanceofGenericHttpMessageConverter ? (GenericHttpMessageConverter<? >) converter :null);
  if(genericConverter ! =null? genericConverter.canRead(targetType, contextClass, contentType) : (targetClass ! =null && converter.canRead(targetClass, contentType))) {
    if(message.hasBody()) { HttpInputMessage msgToUse = getAdvice().beforeBodyRead(message, parameter, targetType, converterType); body = (genericConverter ! =null ? genericConverter.read(targetType, contextClass, msgToUse) :
              ((HttpMessageConverter<T>) converter).read(targetClass, msgToUse));
      body = getAdvice().afterBodyRead(body, msgToUse, parameter, targetType, converterType);
    }
    else {
      body = getAdvice().handleEmptyBody(null, message, parameter, targetType, converterType);
    }
    break; }}Copy the code

The last to seePathVariableMethodArgumentResolverWalk the execution path of consistent discovery and RequestParam (both are inherited from AbstractNamedValueMethodArgumentResolver parser), so the code is not posted.

conclusion

If we want to convert a parameter from request to the type we specify, we can distinguish it by the input annotation:

  • If it’s a RequestBody, configure ObjectMapper (which is injected into Jackson’s HttpMessagConverter, i.eMappingJackson2HttpMessageConverterIn) to realize the serialization and deserialization of Json format data;
  • If the parameter is of type RequestParam or PathVariable, convert the parameter by configuring Converter (which is injected into ConversionService).