1.1 spring-webmvc之HandlerAdapter

1 spring-webmvc概览

    回到DispatcherServlet的doDispatch中,在获取

mappedHandler = getHandler(processedRequest);

是一个清晰的逻辑概念,即从一个request的路径及其它属性获取到某个controller+method为一个handler主体+拦截器列表,该逻辑放在后期介绍。大家会有一个一个疑问,handler(HandlerMethod)是谁负责调用,以及谁去解析@RequestBody,@PathVariable,@RequestParam这些注解,那就开始吧。

    spring-mvc围绕着责任链模式做请求的一步步处理,先来看spring-mvc最最核心的接口HandlerAdapter,其中定义了三个方法:

1:boolean supports(Object handler);
2:ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception;
3:long getLastModified(HttpServletRequest request, Object handler);

其中3不做展开,前两个方法的意思就是:我是否支持,我支持则我处理。我们都围绕着99%用的@Controller展开,对应的handler为HandlerMethod(对应controller#method),该handerle的适配器为:RequestMappingHandlerAdapter,该类的父类+也比较简单,无核心功能。其中方法1也无需展开,大概意思就是:handler为HandlerMethod,一看代码便知:

public final boolean supports(Object handler) {
   return (handler instanceof HandlerMethod && supportsInternal((HandlerMethod) handler));
}

其中handler没有任何继承及接口,构造方法接收某controller的method:

public HandlerMethod(Object bean, Method method) {

    最核心的方法handle放在最后说明,因handler明确为HandlerMethod,因此该方法在抽象父类中做了转换后新定义了一个方法:

protected abstract ModelAndView handleInternal(HttpServletRequest request,
      HttpServletResponse response, HandlerMethod handlerMethod)

handleInternal中处理了是否相同session同步请求及响应头Cache-Control的处理,这个从来没有用到过该逻辑,所以也不做展开,该方法中调用了该核心方法并返回ModelAndView:
mav = invokeHandlerMethod(request, response, handlerMethod);

    处理逻辑都在该方法中:invokeHandlerMethod

ServletInvocableHandlerMethod createInvocableHandlerMethod(HandlerMethod handlerMethod)

该类继承HandlerMethod,设置参数处理器argumentResolvers(HandlerMethodArgumentResolverComposite),设置返回值处理器returnValueHandlers(HandlerMethodReturnValueHandlerComposite)。所以到这里,也基本了解了一个整体的过程,该方法参数处理器和方法返回值处理器都来自RequestMappingHandlerAdapter

    整体的处理过程已经有个轮廓了吧,现在再继续往下介绍细节,先说方法参数处理器吧,适配器中的argumentResolvers是什么时候初始化的,初始化了哪些参数处理器,如果我要增加参数处理器我应该怎么处理?spring-mvc在spring容器中运行,直接看改方法就一目了然:

@Override
public void afterPropertiesSet() {
   // Do this first, it may add ResponseBody advice beans
   initControllerAdviceCache();

   if (this.argumentResolvers == null) {
      List<HandlerMethodArgumentResolver> resolvers = getDefaultArgumentResolvers();
      this.argumentResolvers = new HandlerMethodArgumentResolverComposite().addResolvers(resolvers);
   }
   if (this.initBinderArgumentResolvers == null) {
      List<HandlerMethodArgumentResolver> resolvers = getDefaultInitBinderArgumentResolvers();
      this.initBinderArgumentResolvers = new HandlerMethodArgumentResolverComposite().addResolvers(resolvers);
   }
   if (this.returnValueHandlers == null) {
      List<HandlerMethodReturnValueHandler> handlers = getDefaultReturnValueHandlers();
      this.returnValueHandlers = new HandlerMethodReturnValueHandlerComposite().addHandlers(handlers);
   }
}

在getDefaultArgumentResolvers,他都定义好了近20个内置的处理器,其中getCustomArgumentResolvers一看便知,是框架给开发预留的自定义处理器。其实这么多实现不可能一个个去了解,我们只需要看他接口是怎么定义的,以及我们常用的@RequestBody,@PathVariable,@RequestParam,及不带注解的基本类型加对应类型怎么处理,就已经足够掌握了。先看接口定义:

public interface HandlerMethodArgumentResolver {
	boolean supportsParameter(MethodParameter parameter);
	Object resolveArgument(
        MethodParameter parameter, 
        ModelAndViewContainer mavContainer,
		NativeWebRequest webRequest, 
        WebDataBinderFactory binderFactory);

}
    其中supportsParameter是不是该我处理,其中parameter也一看便知:参数,只是spring将参数的class+注解都预先组织好了。resolveArgument接收参数,将request中的String值取到转换成真正的参数值返回。当前rest方式横行,大家都习惯了用@RequestBody去接收参数,那就先介绍这个参数值怎么处理的吧。文档写到这里,其实我也不知道是哪个参数处理器处理的,那就去找吧:
private List<HandlerMethodArgumentResolver> getDefaultArgumentResolvers() {
		List<HandlerMethodArgumentResolver> resolvers = new ArrayList<>(30);

		// Annotation-based argument resolution
		resolvers.add(new RequestParamMethodArgumentResolver(getBeanFactory(), false));
		resolvers.add(new RequestParamMapMethodArgumentResolver());
		resolvers.add(new PathVariableMethodArgumentResolver());
		resolvers.add(new PathVariableMapMethodArgumentResolver());
		resolvers.add(new MatrixVariableMethodArgumentResolver());
		resolvers.add(new MatrixVariableMapMethodArgumentResolver());
		resolvers.add(new ServletModelAttributeMethodProcessor(false));
		resolvers.add(new RequestResponseBodyMethodProcessor(getMessageConverters(), this.requestResponseBodyAdvice));
		resolvers.add(new RequestPartMethodArgumentResolver(getMessageConverters(), this.requestResponseBodyAdvice));
		resolvers.add(new RequestHeaderMethodArgumentResolver(getBeanFactory()));
		resolvers.add(new RequestHeaderMapMethodArgumentResolver());
		resolvers.add(new ServletCookieValueMethodArgumentResolver(getBeanFactory()));
		resolvers.add(new ExpressionValueMethodArgumentResolver(getBeanFactory()));
		resolvers.add(new SessionAttributeMethodArgumentResolver());
		resolvers.add(new RequestAttributeMethodArgumentResolver());

		// Type-based argument resolution
		resolvers.add(new ServletRequestMethodArgumentResolver());
		resolvers.add(new ServletResponseMethodArgumentResolver());
		resolvers.add(new HttpEntityMethodProcessor(getMessageConverters(), this.requestResponseBodyAdvice));
		resolvers.add(new RedirectAttributesMethodArgumentResolver());
		resolvers.add(new ModelMethodProcessor());
		resolvers.add(new MapMethodProcessor());
		resolvers.add(new ErrorsMethodArgumentResolver());
		resolvers.add(new SessionStatusMethodArgumentResolver());
		resolvers.add(new UriComponentsBuilderMethodArgumentResolver());

		// Custom arguments
		if (getCustomArgumentResolvers() != null) {
			resolvers.addAll(getCustomArgumentResolvers());
		}

		// Catch-all
		resolvers.add(new RequestParamMethodArgumentResolver(getBeanFactory(), true));
		resolvers.add(new ServletModelAttributeMethodProcessor(true));

		return resolvers;
	}
是不是这个比较像啊:RequestResponseBodyMethodProcessor。那点进去看看
	public boolean supportsParameter(MethodParameter parameter) {
		return parameter.hasParameterAnnotation(RequestBody.class);
	}

	public boolean supportsReturnType(MethodParameter returnType) {
		return (AnnotatedElementUtils.hasAnnotation(returnType.getContainingClass(), ResponseBody.class) ||
				returnType.hasMethodAnnotation(ResponseBody.class));
	}
找到了吧,是否包含这个注解:parameter.hasParameterAnnotation(RequestBody.class),顺便也把这个也顺带出来了:supportsReturnType,一看便知,这个类充当了两个角色,那就看看这个类的继承结构:它吧响应的处理逻辑也一直做了吧,大概是同一个人写的不想写两次吧,以及这个请求参数和响应处理它都用到了json转换。

    言归正传,这里还只是说参数是怎么处理的:

resolveArgument,这之中它调用了readWithMessageConverters,其中将servlet规范定义的httpServletRequets转成成了ServletServerHttpRequest,该类定义了获取请求流的方法:
public InputStream getBody()
再调用父类中的AbstractMessageConverterMethodArgumentResolver方法:
protected <T> Object readWithMessageConverters(HttpInputMessage inputMessage, MethodParameter parameter,Type targetType)
看名字是一样的,不过接收的参数不同,这个里面的处理逻辑有点长,但凭想象也知道,是从HttpInputMessage获取字符串,该字符串肯定是一个json吧,通过HttpMessageConverter将json字符串转换成对象返回。

    其中messageConverters在抽像类中定义的原因也知道了,因为它同时还要处理@ResponseBody的json转换,因此也实现HandlerMethodReturnValueHandler。其中转换器在哪定义的呢,往上翻去找RequestResponseBodyMethodProcessor的定义,是在RequestMappingHandlerAdapter顶级类型定义的:

resolvers.add(new RequestResponseBodyMethodProcessor(getMessageConverters(), this.requestResponseBodyAdvice));

其中getMessageConverters()的默认值在中定义:WebMvcConfigurationSupport

#addDefaultHttpMessageConverters
其中用哪个json转换器,看该静态代码块便知,记得用这个命令查找mvn dependency:tree依赖
	static {
		ClassLoader classLoader = WebMvcConfigurationSupport.class.getClassLoader();
		romePresent = ClassUtils.isPresent("com.rometools.rome.feed.WireFeed", classLoader);
		jaxb2Present = ClassUtils.isPresent("javax.xml.bind.Binder", classLoader);
		jackson2Present = ClassUtils.isPresent("com.fasterxml.jackson.databind.ObjectMapper", classLoader) &&
				ClassUtils.isPresent("com.fasterxml.jackson.core.JsonGenerator", classLoader);
		jackson2XmlPresent = ClassUtils.isPresent("com.fasterxml.jackson.dataformat.xml.XmlMapper", classLoader);
		jackson2SmilePresent = ClassUtils.isPresent("com.fasterxml.jackson.dataformat.smile.SmileFactory", classLoader);
		jackson2CborPresent = ClassUtils.isPresent("com.fasterxml.jackson.dataformat.cbor.CBORFactory", classLoader);
		gsonPresent = ClassUtils.isPresent("com.google.gson.Gson", classLoader);
		jsonbPresent = ClassUtils.isPresent("javax.json.bind.Jsonb", classLoader);
	}

依赖中默认带入的是:

messageConverters.add(new MappingJackson2HttpMessageConverter(builder.build()));

看该类的构造方法也知道了这个处理器是怎么处理的的了吧:
 

public MappingJackson2HttpMessageConverter() {
   this(Jackson2ObjectMapperBuilder.json().build());
}
public MappingJackson2HttpMessageConverter(ObjectMapper objectMapper) {
   super(objectMapper, MediaType.APPLICATION_JSON, new MediaType("application", "*+json"));
}

 所以记得发送请求的时候,在请求头中设置:

Content-Type:application/json

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值