SpringBoot custom parameter parser HandlerMethodArgumentResolver application details

foreword

In the Controller layer of the three major frameworks of Spring, you can often see @RequestParam, @PathVariable, @RequestBody and other annotations to automatically encapsulate and enter parameters. These are realized through the parameter parser HandlerMethodArgumentResolver interface provided by the Spring MVC framework. This article will The application of the parameter parser HandlerMethodArgumentResolver will be introduced

HandlerMethodArgumentResolver parsing

Let's take a look at the HandlerMethodArgumentResolver source code:

public interface HandlerMethodArgumentResolver {
    
    

	/**
	 * Whether the given {@linkplain MethodParameter method parameter} is
	 * supported by this resolver.
	 * @param parameter the method parameter to check
	 * @return {@code true} if this resolver supports the supplied parameter;
	 * {@code false} otherwise
	 */
	boolean supportsParameter(MethodParameter parameter);

	/**
	 * Resolves a method parameter into an argument value from a given request.
	 * A {@link ModelAndViewContainer} provides access to the model for the
	 * request. A {@link WebDataBinderFactory} provides a way to create
	 * a {@link WebDataBinder} instance when needed for data binding and
	 * type conversion purposes.
	 * @param parameter the method parameter to resolve. This parameter must
	 * have previously been passed to {@link #supportsParameter} which must
	 * have returned {@code true}.
	 * @param mavContainer the ModelAndViewContainer for the current request
	 * @param webRequest the current request
	 * @param binderFactory a factory for creating {@link WebDataBinder} instances
	 * @return the resolved argument value, or {@code null} if not resolvable
	 * @throws Exception in case of errors with the preparation of argument values
	 */
	@Nullable
	Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer,
			NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory) throws Exception;

}

From the source code, you can see that HandlerMethodArgumentResolver provides two methods. The functions of these two methods are as follows:

  • supportsParameter: Determine whether the request parameter supports the parser (return true to indicate support, and the resolveArgument method will continue to be called, return false to indicate not support, and the parameter parsing ends)
  • resolveArgument: The specific implementation logic of parameter parsing, and finally returns the encapsulated object after the specific parameter parsing

Method parameter description:

  1. MethodParameter parameter: The method parameter object to be parsed, the properties of the parameter can be obtained from the object (such as the type of the parameter, the modified annotation, etc.)
  2. ModelAndViewContainer mavContainer: the currently requested ModelAndViewContainer container
  3. NativeWebRequest webRequest: the current request entity
  4. WebDataBinderFactory binderFactory: instance creation factory

Let's take a look at the existing specific implementations of HandlerMethodArgumentResolver:
insert image description hereinsert image description here
we can see that annotations including our commonly used @RequestParam have corresponding implementations of HandlerMethodArgumentResolver. Let's take RequestParamMapMethodArgumentResolver to analyze the specific implementation of
RequestParamMapMethodArgumentResolver source code:

public class RequestParamMapMethodArgumentResolver implements HandlerMethodArgumentResolver {
    
    

	@Override
	public boolean supportsParameter(MethodParameter parameter) {
    
    
		// 获取参数的@RequestParam注解
		RequestParam requestParam = parameter.getParameterAnnotation(RequestParam.class);
		// 判断参数是否被@RequestParam注解修饰并且映射类型是否为Map对象以及是否指定某个key值
		return (requestParam != null && Map.class.isAssignableFrom(parameter.getParameterType()) &&
				!StringUtils.hasText(requestParam.name()));
	}

	@Override
	public Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer,
			NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory) throws Exception {
    
    

		Class<?> paramType = parameter.getParameterType();

		Map<String, String[]> parameterMap = webRequest.getParameterMap();
		// 如何参数映射类型为MultiValueMap对象,则将映射到MultiValueMap对象中
		if (MultiValueMap.class.isAssignableFrom(paramType)) {
    
    
			MultiValueMap<String, String> result = new LinkedMultiValueMap<>(parameterMap.size());
			// 将参数设置到MultiValueMap对象中
			parameterMap.forEach((key, values) -> {
    
    
				for (String value : values) {
    
    
					result.add(key, value);
				}
			});
			return result;
		}
		// 如何参数映射类型不是MultiValueMap对象,则将映射到Map对象中
		else {
    
    
			Map<String, String> result = new LinkedHashMap<>(parameterMap.size());
			// 将参数设置到Map对象中
			parameterMap.forEach((key, values) -> {
    
    
				if (values.length > 0) {
    
    
					result.put(key, values[0]);
				}
			});
			return result;
		}
	}
}

HandlerMethodArgumentResolver application example

Above we introduced the implementation principle of HandlerMethodArgumentResolver. Sometimes we also need to customize a parameter resolver to parse a certain parameter. For example, in development, sometimes we need to obtain the user's login information verification permission. Every interface that needs to verify the permission is written A piece of logic for obtaining user information is somewhat redundant. At this time, we can customize a parameter parser to parse user login information and encapsulate it into an object that stores user information entities. Here is an example copied from other places as an
example 在这里插入代码片:

@Component
public class LoginUserHandlerMethodArgumentResolver implements HandlerMethodArgumentResolver {
    
    
    @Autowired
    private UserService userService;

    @Override
    public boolean supportsParameter(MethodParameter parameter) {
    
    
        return parameter.getParameterType().isAssignableFrom(UserEntity.class) && parameter.hasParameterAnnotation(LoginUser.class);
    }

    @Override
    public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer container,
                                  NativeWebRequest request, WebDataBinderFactory factory) throws Exception {
    
    
        //获取用户ID
        Object object = request.getAttribute(AuthorizationInterceptor.USER_KEY, RequestAttributes.SCOPE_REQUEST);
        if(object == null){
    
    
            return null;
        }
        //获取用户信息
        UserEntity user = userService.getById((Long)object);
        return user;
    }
}

This logic is not complicated, that is, the cookie is carried when sending the request, and the parameter parser parses the cookie to obtain user information and encapsulates it into a UserEntity object to return

HandlerMethodArgumentResolver, like the interceptor Interceptor, needs to be registered to the IOC container, which can be implemented through the addArgumentResolvers method provided by webMvcConfig. The implementation code is as follows:

@Configuration
public class WebMvcConfig implements WebMvcConfigurer {
    
    
    @Autowired
    private LoginUserHandlerMethodArgumentResolver loginUserHandlerMethodArgumentResolver;

    @Override
    public void addArgumentResolvers(List<HandlerMethodArgumentResolver> argumentResolvers) {
    
    
        argumentResolvers.add(loginUserHandlerMethodArgumentResolver);
    }
}

Guess you like

Origin blog.csdn.net/weixin_44947701/article/details/122355441