驽马十驾 驽马十驾

驽马十驾,功在不舍

目录
SpringMVC中的参数解析-HandlerMehtodArgumentResolver
/  

SpringMVC中的参数解析-HandlerMehtodArgumentResolver

前言

本文主要介绍SpringMVC对Controller中对外方法的参数的解析接口HandlerMethodArgumentResolver,包括如下说明:

  • 接口方法说明
  • 核心实现类
  • 作用于何处
  • 代码实战

接口方法说明

该接口在:org.springframework.web.method.support.HandlerMethodArgumentResolver中,有2个核心方法:

public interface HandlerMethodArgumentResolver {

	/**
	 * true:表示支持,调用下面的:resolveArgument() 方法。
	 */
	boolean supportsParameter(MethodParameter parameter);

	/**
	 * 如何处理 这个方法参数。
	 * 重点:该返回值为Controller层中对外方法对外形参的值。[结合后续]
	 */
	@Nullable
	Object resolveArgument(MethodParameter parameter, 
                           @Nullable ModelAndViewContainer mavContainer,
							NativeWebRequest webRequest, 
                           @Nullable WebDataBinderFactory binderFactory) throws Exception;

这2个参数如何理解?

我们可以结合官方实现来看看。

官方实现

我们先来看一个需求,当前Controller层需要获取到某个cookie的值,如何做?

@Controller
public class HelloWorldController {
    
    /**
	 *  通过注解:@CookieValue(value="JSESSIONID") 就可以获取到!
	 *  但是你思考过为什么没有?
	 */
	@RequestMapping("/mic/cookie")
	public void cookieTest(@CookieValue(value="JSESSIONID")String sessionId) {
		System.out.println("通过@CookieValue获得JSESSIONID:"+sessionId);
	}

Spring MVC在处理的时候,会将Controller层中每一个对外的方法的形参进行审核(姑且这么形容),然后赋值.

其利用的就是HandlerMethodArgumentResolver这个处理类,接下来我们看看官方对于@CookieValue的实现。

public abstract class AbstractCookieValueMethodArgumentResolver extends AbstractNamedValueMethodArgumentResolver {

    /**
	 *  第一个方法,参数匹配,如果为true表示可以执行后续
	 */
	@Override
	public boolean supportsParameter(MethodParameter parameter) {
		return parameter.hasParameterAnnotation(CookieValue.class);
	}
}

这个resolveArgument在其父类中,方法比较长,一个一个来看

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

		NamedValueInfo namedValueInfo = getNamedValueInfo(parameter);
		MethodParameter nestedParameter = parameter.nestedIfOptional();

        // 获取 cookie 对应的名称 jsessionId
		Object resolvedName = resolveStringValue(namedValueInfo.name);
		.....
        // 获取jessionId对应的值,如果获取到,通常该流程就返回了
		Object arg = resolveName(resolvedName.toString(), nestedParameter, webRequest);
		if (arg == null) {
			if (namedValueInfo.defaultValue != null) {
                // 解析为默认值
				arg = resolveStringValue(namedValueInfo.defaultValue);
			}
			else if (namedValueInfo.required && !nestedParameter.isOptional()) {
                // 解析没有默认值,那么解析为 MissingValue
				handleMissingValue(namedValueInfo.name, nestedParameter, webRequest);
			}
            // 最后进行nullValue处理。
			arg = handleNullValue(namedValueInfo.name, arg, nestedParameter.getNestedParameterType());
		}
		else if ("".equals(arg) && namedValueInfo.defaultValue != null) {
			arg = resolveStringValue(namedValueInfo.defaultValue);
		}
		return arg;
	}

作用于何处

该处理器同名称一样,处理的是Argument所以我们很容易猜到,当我们需要在Controller对外的方法中注入某个形参的时候,可以通过该接口进行处理。

比如很典型的例子:解析HTTP请求中的CookieId,通过Id从缓存中获取用户信息。

传统的方法可以从HttpServletRequest中获取并解析,但是如果很多地方都这么去写不利于代码维护,所以可以进行统一处理。

实战

首先定义一个类MicUserArgumentResolver来实现接口HandlerMethodArgumentResovler

@Component
public class MicUserArgumentResolver implements HandlerMethodArgumentResolver {
    /**
     * 通过返回值判定是否支持参数.
     * 虽然没有查看源码,但是应该是将每个参数获取后进行处理的。
     */
    @Override
    public boolean supportsParameter(MethodParameter methodParameter) {
        Class<?> parameterType = methodParameter.getParameterType();
        return parameterType == MicUser.class;
    }

    
    @Override
    public Object resolveArgument(MethodParameter methodParameter,
                                  ModelAndViewContainer modelAndViewContainer, 
                                  NativeWebRequest nativeWebRequest, 
                                  WebDataBinderFactory webDataBinderFactory) {
        //获取HttpServletRequest
        HttpServletRequest request = 
            nativeWebRequest.getNativeRequest(HttpServletRequest.class);
        //获取HttpServletResponse
        HttpServletResponse response = 
            nativeWebRequest.getNativeResponse(HttpServletResponse.class);
        String paramToken = request.getParameter("jessionId");
	    ......
        return new MicUser();
    }

然后将这个参数解析器注入上下文,需要利用WebMvcConfigurer这个类

@Configuration
public class CustomWebConfigure implements WebMvcConfigurer {
    @Autowired
    private MicUserArgumentResolver micUserArgumentResolver;

    /**
     * 参数处理:可以通过实现 HandlerMethodArgumentResolver 来解析"通用"的参数<br>
     * 比如典型的案例就是通过参数中的cookie获取到用户对象
     *
     * @param resolvers
     */
    @Override
    public void addArgumentResolvers(List<HandlerMethodArgumentResolver> resolvers) {
        resolvers.add(micUserArgumentResolver);
    }
}

然后在Controller的形参中注入试用

@RestController
public class TestController {

    @GetMapping("/mic/user")
    public MicUser micUser(MicUser micUser, String xx) {
        return micUser;
    }
}

大功告成!!!

总结

其实后续几篇文章都会结合:WebMvcConfigurer里面的接口方法进行讲解,欢迎收藏!

骐骥一跃,不能十步。驽马十驾,功在不舍。