The Spring container has many built-in parameter parsers. These parameter parsers are mentioned in the HandlerAdapter source code parsing. If you are interested, you can take a look. Today we’ll talk about how a parameter parser works.

Step 1: Initialize the defined parameters

When defining a Controller, we write some parameters to receive the parameters. Remember that a HandlerExecutionChain is returned in the DispatchServlet, where the HandlerMethod encapsulates the Handler information we defined (including the parameter information).

private HandlerMethod(HandlerMethod handlerMethod, Object handler) {
    this.bean = handler;
    this.beanFactory = handlerMethod.beanFactory;
    this.beanType = handlerMethod.beanType;
    this.method = handlerMethod.method;
    this.bridgedMethod = handlerMethod.bridgedMethod;
    // private final MethodParameter[] parameters;
    this.parameters = handlerMethod.parameters;
    this.responseStatus = handlerMethod.responseStatus;
    this.responseStatusReason = handlerMethod.responseStatusReason;
    this.resolvedFromHandlerMethod = handlerMethod;
    this.description = handlerMethod.description;
}
Copy the code

As you can see from the above, SpringMVC creates a HandlerMethod where this.parameters encapsulates our parameter information.

Step 2: Parse the parameters

From the handlerapdater.handle () method of the DispatchServlet, you can trace the following code to indicate that the parameter binding starts when the handleMethod method is executed.

public void invokeAndHandle(ServletWebRequest webRequest, ModelAndViewContainer mavContainer, Object... providedArgs) throws Exception {
   Object returnValue = invokeForRequest(webRequest, mavContainer, providedArgs);
}
Copy the code
// InvocableHandlerMethod.java
protected Object[] getMethodArgumentValues(NativeWebRequest request, @Nullable ModelAndViewContainer mavContainer, Object... providedArgs) throws Exception {
    // Get all the initialization parameters in the first step
    MethodParameter[] parameters = this.getMethodParameters();
    Object[] args = new Object[parameters.length];
    for(int i = 0; i < parameters.length; ++i) {
      // Iterate over and parse each argument
      args[i] = this.resolvers.resolveArgument(parameter, mavContainer, request, this.dataBinderFactory);
      }
    return args;
}
Copy the code

When we have all the parameters, we need to parse them. Because the type of each parameter is different, the required parameter parser is different, so we traverse each parameter and select the appropriate parameter parser for parameter parsing.

@Nullable
public Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer, NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory) throws Exception {
    // Select the appropriate parameter parser based on the current parameters. Eg: There is a specific parameter parser for @requestParam
    HandlerMethodArgumentResolver resolver = this.getArgumentResolver(parameter);
    if (resolver == null) {
        throw new IllegalArgumentException("Unsupported parameter type [" + parameter.getParameterType().getName() + "]. supportsParameter should be called first.");
    } else {
        // Perform parameter parsing according to the selected parameter parser
        returnresolver.resolveArgument(parameter, mavContainer, webRequest, binderFactory); }}Copy the code
public final Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer, NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory) throws Exception {
    // The following three lines get the parameter information to get the parameter name
    AbstractNamedValueMethodArgumentResolver.NamedValueInfo namedValueInfo = this.getNamedValueInfo(parameter);
    MethodParameter nestedParameter = parameter.nestedIfOptional();
    Object resolvedName = this.resolveEmbeddedValuesAndExpressions(namedValueInfo.name);
    // Get the parameter value from the request based on the parameter name.
    Object arg = this.resolveName(resolvedName.toString(), nestedParameter, webRequest);
    // Create a Web data binder
    WebDataBinder binder = binderFactory.createBinder(webRequest, (Object)null, namedValueInfo.name);
    // If necessary, parameters can be converted. A conversion that converts a string argument to the type the server really needs
    arg = binder.convertIfNecessary(arg, parameter.getParameterType(), parameter);
    // Empty implementation, subclasses can be implemented, for customization
    this.handleResolvedValue(arg, namedValueInfo.name, parameter, mavContainer, webRequest);
    return arg;
}
Copy the code

Gets the parameter value from the request based on the parameter name. The underlying use or traditional development mode of the request. The getParameterValues (” argName “). The core processing code involves the parameter parser and the DataBinder (DataBinder) sequencing the parameters. Notice that SpringMVC is WebDataBinder.

Step 3: Consider scaling

Spring has a number of parameter parsers and data binders built in, but if you encounter parameters that aren’t built in, you can add an appropriate parameter parser and DataBinder (DataBinder) to handle specific data based on the actual parameter type. Custom parser needs to realize HandlerMethodArgumentResolver interface parameters, then the implementation class appended to the container.

@Configuration
public class WebMvcConfig implements WebMvcConfigurer {
    
    @Autowired
    private CusHandlerMethodArgumentResolver cusHandlerMethodArgumentResolver;
    // Add a custom parameter parser
    @Override
    public void addArgumentResolvers(List<HandlerMethodArgumentResolver> resolvers) { resolvers.add(cusHandlerMethodArgumentResolver); }}Copy the code

Data binding, can not only complete data type conversion, but also for parameter verification. Developers can customize the DataBinder by implementing the Converter

interface and append implementation classes to the container.
,>