文档章节

spring mvc AnnotationMethodHandlerAdapter 关键方法

 陆大侠
发布于 2015/12/24 14:32
字数 914
阅读 29
收藏 0
点赞 0
评论 0

spring mvc 会对request 参数以及response的输出作处理,这个很神奇,也很便捷。

HttpMessageConverter 是具体处理的相关接口,有若干实现类,如json,xml, text等等。

而HandlerAdapter 是SPRING MVC 最核心的对象,它的代码负责控制了这个转换的规则,具体来说,就是

1 根据具体 controller方法的注解@requestBody, @headerParam @pathParam等等,以及request的content-type, 选择合适的HttpMessageConverter,做参数的填充。

2 根据request的accept列表里面的参数,选择合适的HttpMessageConverter,做输出。


具体情况贴点AnnotationMethodHandlerAdapter的代码吧(流程为@RequestBody注解,其他类似): 

入口是DispatcherServlet.doDispatch,调用了HandlerAdapter的handle方法. 。。。。然后到了下面这段代码。

public class AnnotationMethodHandlerAdapter extends WebContentGenerator
		implements HandlerAdapter, Ordered, BeanFactoryAware {
。。。。省略代码   有个内部私有类,继承了HandlerMethodInvoker 
private class ServletHandlerMethodInvoker extends HandlerMethodInvoker {

在request进来的时候,调用controller的具体方法执行之前,会调用一下HandlerMethodInvoker的invokeHandlerMethod代码:

public ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler)
			throws Exception {
。。。省略
		// Execute invokeHandlerMethod in synchronized block if required.
		if (this.synchronizeOnSession) { //这里可以配置基于session的锁。
			HttpSession session = request.getSession(false);
			if (session != null) {
				Object mutex = WebUtils.getSessionMutex(session);
				synchronized (mutex) {
					return invokeHandlerMethod(request, response, handler);
				}
			}
		}

		return invokeHandlerMethod(request, response, handler);
	}

protected ModelAndView invokeHandlerMethod(HttpServletRequest request, HttpServletResponse response, Object handler)
			throws Exception {

		ServletHandlerMethodResolver methodResolver = getMethodResolver(handler);
		Method handlerMethod = methodResolver.resolveHandlerMethod(request);
		ServletHandlerMethodInvoker methodInvoker = new ServletHandlerMethodInvoker(methodResolver);
		ServletWebRequest webRequest = new ServletWebRequest(request, response);
		ExtendedModelMap implicitModel = new BindingAwareModelMap();
                //这里会调用到controller 的方法,之前会解析并填充参数。
		Object result = methodInvoker.invokeHandlerMethod(handlerMethod, handler, webRequest, implicitModel);
		//这个填充了ModelAndView对象,并对response做处理,比如@responseBody
		ModelAndView mav =
				methodInvoker.getModelAndView(handlerMethod, handler.getClass(), result, implicitModel, webRequest);
		methodInvoker.updateModelAttributes(handler, (mav != null ? mav.getModel() : null), implicitModel, webRequest);
		return mav;
	}
public final Object invokeHandlerMethod(Method handlerMethod, Object handler,
			NativeWebRequest webRequest, ExtendedModelMap implicitModel){
                //这个地方,找到了实际要执行的controller的那个方法,会通过反射调用
		Method handlerMethodToInvoke = BridgeMethodResolver.findBridgedMethod(handlerMethod);
		try {
			。。。省略了一些代码
			//这里会填充方法的参数
			Object[] args = resolveHandlerArguments(handlerMethodToInvoke, handler, webRequest, implicitModel);
			if (debug) {
				logger.debug("Invoking request handler method: " + handlerMethodToInvoke);
			}
			ReflectionUtils.makeAccessible(handlerMethodToInvoke);
			return handlerMethodToInvoke.invoke(handler, args); //这里实际调用controller
		}
}	
//resolveHandlerArguments这个方法有点长, 就不贴了。这个方法,遍历了controller方法的参数,然后检查每个参数上的注解,根据注解的不同不去调用了readWithMessageConverters 这个方法。

readWithMessageConverters方法 大致是根据ContentType和messageConverters的配置,去把webReqeust里面拿到requestBody这个流对象,然后转换成 方法参数
private Object readWithMessageConverters(MethodParameter methodParam, HttpInputMessage inputMessage, Class paramType)
			throws Exception {
                //获得request的ContentType, 如果为空的话,会报错.
		MediaType contentType = inputMessage.getHeaders().getContentType();
		if (contentType == null) {
			StringBuilder builder = new StringBuilder(ClassUtils.getShortName(methodParam.getParameterType()));
			String paramName = methodParam.getParameterName();
			if (paramName != null) {
				builder.append(' ');
				builder.append(paramName);
			}
			throw new HttpMediaTypeNotSupportedException(
					"Cannot extract parameter (" + builder.toString() + "): no Content-Type found");
		}
                //下面这段代码找到,相关的messageConverter,进行转换。
		List<MediaType> allSupportedMediaTypes = new ArrayList<MediaType>();
		if (this.messageConverters != null) {
			for (HttpMessageConverter<?> messageConverter : this.messageConverters) {
				allSupportedMediaTypes.addAll(messageConverter.getSupportedMediaTypes());
				if (messageConverter.canRead(paramType, contentType)) {
					if (logger.isDebugEnabled()) {
						logger.debug("Reading [" + paramType.getName() + "] as \"" + contentType
								+"\" using [" + messageConverter + "]");
					}
					return messageConverter.read(paramType, inputMessage);
				}
			}
		} //如果,我们不能处理这个ContentType,也需要报错。
		throw new HttpMediaTypeNotSupportedException(contentType, allSupportedMediaTypes);
}

AnnotationMethodHandlerAdapter如何处理@ResponseBody:

这是HandlerMethodInvoker的方法,返回对象是ModelAndView,如果controller的returnValue是httpEntity或则controller方法的签名是有@ResponseBody注解,则返回为空。如果为空,上层的ViewResolver就不处理了。
public ModelAndView (Method handlerMethodClass handlerTypeObject returnValue )
如果是@ResponseBody,则会调用下面的方法:
private void handleResponseBody(Object returnValue, ServletWebRequest webRequest)
      throws Exception {
   if (returnValue == null) {
      return;
   }
   HttpInputMessage inputMessage = createHttpInputMessage(webRequest);
   HttpOutputMessage outputMessage = createHttpOutputMessage(webRequest);
   writeWithMessageConverters(returnValue, inputMessage, outputMessage);
}
这个和上面的那个read类似,是拿到Accept参数。去找到相应的messageConverter 做输出转换。
private void writeWithMessageConverters(Object returnValue,
         HttpInputMessage inputMessage, HttpOutputMessage outputMessage)
         throws IOException, HttpMediaTypeNotAcceptableException {
      List<MediaType> acceptedMediaTypes = inputMessage.getHeaders().getAccept();
      if (acceptedMediaTypes.isEmpty()) {
         acceptedMediaTypes = Collections.singletonList(MediaType.ALL);
      }
      MediaType.sortByQualityValue(acceptedMediaTypes);
      Class<?> returnValueType = returnValue.getClass();
      List<MediaType> allSupportedMediaTypes = new ArrayList<MediaType>();
      if (getMessageConverters() != null) {
         for (MediaType acceptedMediaType : acceptedMediaTypes) {
            for (HttpMessageConverter messageConverter : getMessageConverters()) {
               if (messageConverter.canWrite(returnValueType, acceptedMediaType)) {
                  messageConverter.write(returnValue, acceptedMediaType, outputMessage);
                  if (logger.isDebugEnabled()) {
                     MediaType contentType = outputMessage.getHeaders().getContentType();
                     if (contentType == null) {
                        contentType = acceptedMediaType;
                     }
                     logger.debug("Written [" + returnValue + "] as \"" + contentType +
                           "\" using [" + messageConverter + "]");
                  }
                  this.responseArgumentUsed = true;
                  return;
               }
            }
         }
         for (HttpMessageConverter messageConverter : messageConverters) {
            allSupportedMediaTypes.addAll(messageConverter.getSupportedMediaTypes());
         }
      }
      throw new HttpMediaTypeNotAcceptableException(allSupportedMediaTypes);
   }
}


© 著作权归作者所有

共有 人打赏支持
粉丝 2
博文 52
码字总数 18787
作品 0
浦东
【2】springmvc 入门

springmvc都是通过其中央控制器DispatcherServlet来进行转发的,所以在web.xml文件里面需要增加相应的配置 <servlet><servlet-name>springServlet</servlet-name><servlet-class>org.spring......

dkz ⋅ 2014/04/05 ⋅ 0

Spring AOP 对Spring MVC的Controller切面拦截不起作用

当使用Spring AOP对Controller层的Controller类的方法进行切面拦截,不起作用。AOP配置没有任何问题。 Spring AOP配置没有任何问题;【正常】 断点调试:Spring源码断点调试,在调用Control...

陶邦仁 ⋅ 2015/11/02 ⋅ 2

<mvc:annotation-driven >和context:component-scan的区别 为啥要说这两个注解的区别,因为<mvc:annotation-driven >和context:component-scan放置的位置出错,可能就会导致项目出现404的错误......

小车车 ⋅ 2016/07/07 ⋅ 0

SpringMVC配置全局日期转换器,处理日期转换异常

spring3.0配置日期转换可以通过配置自定义实现WebBingingInitializer接口的一个日期转换类来实现,方法如下 转换类: public class DateConverter implements WebBindingInitializer { publ...

Zero零_度 ⋅ 2016/03/14 ⋅ 0

SpringMVC - 注解的适配器和映射器配置

注解的映射器 在spring 3.1 之前使用DefaultAnnotationHandlerMapping, 在spring 3.1 之后使用org.springframework.web.servlet.mvc.method.annotation包下的RequestMappingHandlerMapping ......

中柠檬 ⋅ 2016/11/18 ⋅ 0

【spring MVC学习二】springMVC简单使用

springmvc都是通过其中央控制器DispatcherServlet来进行转发的,所以在web.xml文件里面需要增加相应的配置 如果没有配置contextConfigLocation,Spring mvc默认配置文件为“/WEB-INF/[servl...

挨踢人生 ⋅ 2014/10/15 ⋅ 0

spring vmc3.1.1 上,通过AnnotationMethodHandlerAdap...

spring vmc3.1.1 下,通过AnnotationMethodHandlerAdapter配置webBindingInitializer失效解决方案 问题: spring vmc3.1.1 下,通过AnnotationMethodHandlerAdapter配置webBindingInitializ......

爪哇小贩 ⋅ 2013/03/23 ⋅ 2

SpringMVC工作原理

标签: it 上面的是springMVC的工作原理图: 1、客户端发出一个http请求给web服务器,web服务器对http请求进行解析,如果匹配DispatcherServlet的请求映射路径(在web.xml中指定),web容器将...

LYQ1990 ⋅ 2016/05/31 ⋅ 0

SpringMvc数据绑定

到目前为止,请求已经能交给我们的处理器进行处理了,接下来的事情是要进行收集数据啦,接下来我们看看我们能从请求中收集到哪些数据,如图6-11: 图6-11 1、@RequestParam绑定单个请求参数值...

游宵 ⋅ 2014/04/21 ⋅ 0

Spring MVC

Spring MVC一、什么是 Spring MVC Spring MVC 属于 SpringFrameWork 的后续产品,已经融合在 Spring Web Flow 里面,是一个强大灵活的 Web 框架。Spring MVC 提供了一个 DispatcherServlet ...

任任任任师艳 ⋅ 2017/11/20 ⋅ 0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

LVM

LVM: 硬盘划分分区成物理卷->物理卷组成卷组->卷组划分逻辑分区。 1.磁盘分区: fdisk /dev/sdb 划分几个主分区 输入t更改每个分区类型为8e(LVM) 使用partprobe生成分区的文件:如/dev/sd...

ZHENG-JY ⋅ 39分钟前 ⋅ 0

彻底删除Microsoft Office的方法

参照此链接彻底删除Office https://support.office.com/zh-cn/article/%e4%bb%8e-pc-%e5%8d%b8%e8%bd%bd-office-9dd49b83-264a-477a-8fcc-2fdf5dbf61d8?ui=zh-CN&rs=zh-CN&ad=CN......

Kampfer ⋅ 54分钟前 ⋅ 0

大盘与个股之间关系

大盘走多:积极出手 顺势加码 大盘走空: 少量出手 退场观望 大盘做头:逆势减码 少量操作 大盘做底 : 小量建仓 小量试单

guozenhua ⋅ 56分钟前 ⋅ 0

Day16 LVM(逻辑卷管理)与磁盘故障小案例

lvm详解 简述 LVM的产生是因为传统的分区一旦分区好后就无法在线扩充空间,也存在一些工具能实现在线扩充空间但是还是会面临数据损坏的风险;传统的分区当分区空间不足时,一般的解决办法是再...

杉下 ⋅ 今天 ⋅ 0

rsync实现多台linux服务器的文件同步

一、首先安装rsync,怎样安装都行,rpm,yum,还是你用源码安装都可以。因为我用的是阿里云的ESC,yum install rsync就ok了。 二、配置rsync服务 1.先建立个同步数据的帐号 123 groupadd r...

在下头真的很硬 ⋅ 今天 ⋅ 0

前端基础(三):函数

字数:1685 阅读时间:5分钟 函数定义 在最新的ES规范中,声明函数有4中方法: -函数声明 -函数表达式 -构造函数Function -生成器函数 1.函数声明 语法: function name([param[, param2 [....

老司机带你撸代码 ⋅ 今天 ⋅ 0

Java虚拟机的Heap监狱

在Java虚拟机中,我是一个位高权重的大管家,他们都很怕我,尤其是那些Java 对象,我把他们圈到一个叫做Heap的“监狱”里,严格管理,生杀大权尽在掌握。 中国人把Stack翻译成“栈”,把Hea...

java高级架构牛人 ⋅ 今天 ⋅ 0

Spring MVC基本概念

只写Controller

颖伙虫 ⋅ 今天 ⋅ 0

微软重金收购GitHub的背后逻辑原来是这样的

全球最大的开发者社区GitHub网站花落谁家的问题已经敲定,微软最终以75亿美元迎娶了这位在外界看来无比“神秘”的小家碧玉。尽管此事已过去一些时日,但整个开发者世界,包括全球各地的开源社...

linux-tao ⋅ 今天 ⋅ 0

磁盘管理—逻辑卷lvm

4.10-4.12 lvm 操作流程: 磁盘分区-->创建物理卷-->划分为卷组-->划分成逻辑卷-->格式化、挂载-->扩容。 磁盘分区 注: 创建分区时需要更改其文件类型为lvm(代码8e) 分区 3 已设置为 Linu...

弓正 ⋅ 今天 ⋅ 0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

返回顶部
顶部