文档章节

【原创】遨游springmvc之HandlerMapping

开源中国首席脑科主任
 开源中国首席脑科主任
发布于 2016/07/24 17:46
字数 1076
阅读 161
收藏 2

1.前言

之前我们springmvc原理篇里已经介绍过,从springmvc核心处理器DispatcherServlet派遣请求的时候,首先会将请求包装,这就是我们这边介绍的HandlerMapping

在springmvc源码介绍中我们知道,HandlerMapping是一个处理对象映射和映射对应的处理程序的一个中间体,它也可以由开发者自己去实现,但是我们一般没有这个必要,因为springmvc框架已经给我们提供了足够的映射处理器给我们,通常在初始化我们的web项目的时候,springmvc会初始化2个处理器:org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping、org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping,其中RequestMappingHandlerMapping便可以处理我们在Controller中带@RequestMapping的方法,HandlerMapping可以直接实现拦截器,但是不会这么做,通常HandlerMapping都会用一个叫HandlerExecutionChain的东西给包装起来,而HandlerExecutionChain则引入了HandlerInterceptor(拦截器),在包装类中,springmvc已经帮我们处理好了映射对应的handler(处理方法)和拦截器

 

2.源代码解刨

2.1依赖关系

2.2 源码解刨

DispatcherServlet中首先给当前请求指定对应的HandlerExecutionChain

源码2.2.1

	protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
		HttpServletRequest processedRequest = request;
		HandlerExecutionChain mappedHandler = null;
		boolean multipartRequestParsed = false;

		WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);

		try {
			ModelAndView mv = null;
			Exception dispatchException = null;

			try {
				processedRequest = checkMultipart(request);
				multipartRequestParsed = (processedRequest != request);

				// Determine handler for the current request.(给当前请求指派对应的处理程序)
				mappedHandler = getHandler(processedRequest);
				if (mappedHandler == null || mappedHandler.getHandler() == null) {
					noHandlerFound(processedRequest, response);
					return;
				}

				//省略代码
		}
	}

DispatcherServlet提供循环HandlerMapping的方法,将请求包装

源码2.2.2

	protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
		for (HandlerMapping hm : this.handlerMappings) {
			if (logger.isTraceEnabled()) {
				logger.trace(
						"Testing handler map [" + hm + "] in DispatcherServlet with name '" + getServletName() + "'");
			}
			HandlerExecutionChain handler = hm.getHandler(request);
			if (handler != null) {
				return handler;
			}
		}
		return null;
	}

在AbstractHandlerMapping(抽象HandlerMapping)中处理了HandlerMapping对应的处理程序,加入拦截器

源码2.2.3

	@Override
	public final HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
        //处理映射对应的处理程序(所说的控制器)
		Object handler = getHandlerInternal(request);
		if (handler == null) {
			handler = getDefaultHandler();
		}
		if (handler == null) {
			return null;
		}
		// Bean name or resolved handler?
		if (handler instanceof String) {
			String handlerName = (String) handler;
			handler = getApplicationContext().getBean(handlerName);
		}
        //包装拦截器
		HandlerExecutionChain executionChain = getHandlerExecutionChain(handler, request);
		if (CorsUtils.isCorsRequest(request)) {
			CorsConfiguration globalConfig = this.corsConfigSource.getCorsConfiguration(request);
			CorsConfiguration handlerConfig = getCorsConfiguration(handler, request);
			CorsConfiguration config = (globalConfig != null ? globalConfig.combine(handlerConfig) : handlerConfig);
			executionChain = getCorsHandlerExecutionChain(request, executionChain, config);
		}
		return executionChain;
	}

那么问题来了?HandlerMapping如何处理带@RequestMapping的方法?

springmvc很机制,直接将两个类的字母连接起来形成一个叫RequestMappingHandlerMapping,RequestMappingHandlerMapping依赖RequestMappingInfoHandlerMapping、RequestMappingInfoHandlerMapping依赖AbstractHandlerMethodMapping(看依赖图),在AbstractHandlerMethodMapping中的一个内部类MappingRegistry中,通过注册方法,直接将

@RequestMappng中包含的信息RequestMappingInfo和HandlerMethod通过LinkedHashMap(key-value)方式连接,也就是说有了RequestMapping(RequestMappingInfo)就能得到HandlerMethod(看源码2.2.7)HandlerMethod包装了执行类、执行方法、参数、桥接处理器 

源码2.2.4

	@Override
	protected HandlerMethod getHandlerInternal(HttpServletRequest request) throws Exception {
        //获取url
		String lookupPath = getUrlPathHelper().getLookupPathForRequest(request);
		if (logger.isDebugEnabled()) {
			logger.debug("Looking up handler method for path " + lookupPath);
		}
		this.mappingRegistry.acquireReadLock();
		try {
            //根据url获取HandlerMethod
			HandlerMethod handlerMethod = lookupHandlerMethod(lookupPath, request);
			if (logger.isDebugEnabled()) {
				if (handlerMethod != null) {
					logger.debug("Returning handler method [" + handlerMethod + "]");
				}
				else {
					logger.debug("Did not find handler method for [" + lookupPath + "]");
				}
			}
			return (handlerMethod != null ? handlerMethod.createWithResolvedBean() : null);
		}
		finally {
			this.mappingRegistry.releaseReadLock();
		}
	}

源码2.2.5

//查找当前请求的最优处理方法。如果找到多个匹配,选择最佳匹配。	
protected HandlerMethod lookupHandlerMethod(String lookupPath, HttpServletRequest request) throws Exception {
		List<Match> matches = new ArrayList<Match>();
        //mappingRegistry注册器
		List<T> directPathMatches = this.mappingRegistry.getMappingsByUrl(lookupPath);
		if (directPathMatches != null) {
			addMatchingMappings(directPathMatches, matches, request);
		}
		if (matches.isEmpty()) {
			// No choice but to go through all mappings...
			addMatchingMappings(this.mappingRegistry.getMappings().keySet(), matches, request);
		}

		if (!matches.isEmpty()) {
			Comparator<Match> comparator = new MatchComparator(getMappingComparator(request));
			Collections.sort(matches, comparator);
			if (logger.isTraceEnabled()) {
				logger.trace("Found " + matches.size() + " matching mapping(s) for [" +
						lookupPath + "] : " + matches);
			}
			Match bestMatch = matches.get(0);
			if (matches.size() > 1) {
				if (CorsUtils.isPreFlightRequest(request)) {
					return PREFLIGHT_AMBIGUOUS_MATCH;
				}
				Match secondBestMatch = matches.get(1);
				if (comparator.compare(bestMatch, secondBestMatch) == 0) {
					Method m1 = bestMatch.handlerMethod.getMethod();
					Method m2 = secondBestMatch.handlerMethod.getMethod();
					throw new IllegalStateException("Ambiguous handler methods mapped for HTTP path '" +
							request.getRequestURL() + "': {" + m1 + ", " + m2 + "}");
				}
			}
			handleMatch(bestMatch.mapping, lookupPath, request);
			return bestMatch.handlerMethod;
		}
		else {
			return handleNoMatch(this.mappingRegistry.getMappings().keySet(), lookupPath, request);
		}
	}

源码2.2.6

注册器注册了映射和HandlerMethod的关系,并且使得后续适配器中得到适配

源码2.2.7

		public void register(T mapping, Object handler, Method method) {
			this.readWriteLock.writeLock().lock();
			try {
				HandlerMethod handlerMethod = createHandlerMethod(handler, method);
				assertUniqueMethodMapping(handlerMethod, mapping);

				if (logger.isInfoEnabled()) {
					logger.info("Mapped \"" + mapping + "\" onto " + handlerMethod);
				}
                //将requestMappingInfo和handlerMethod绑定
				this.mappingLookup.put(mapping, handlerMethod);

				List<String> directUrls = getDirectUrls(mapping);
				for (String url : directUrls) {
					this.urlLookup.add(url, mapping);
				}

				String name = null;
				if (getNamingStrategy() != null) {
					name = getNamingStrategy().getName(handlerMethod, mapping);
					addMappingName(name, handlerMethod);
				}

				CorsConfiguration corsConfig = initCorsConfiguration(handler, method, mapping);
				if (corsConfig != null) {
					this.corsLookup.put(handlerMethod, corsConfig);
				}

				this.registry.put(mapping, new MappingRegistration<T>(mapping, handlerMethod, directUrls, name));
			}
			finally {
				this.readWriteLock.writeLock().unlock();
			}
		}

 

3.小结

到此为止,springmvc已经将请求包装处理好,也就是对应第一张原理图中的如下标记是2、3的部分

期待下一章HandlerAdapter

 

 

发现一个机智的导航😳

© 著作权归作者所有

开源中国首席脑科主任
粉丝 61
博文 21
码字总数 18226
作品 0
宁波
后端工程师
私信 提问
【原创】遨游springmvc之原理篇

1.Springmvc是什么 spring web mvc是一种基于java实现的请求驱动(请求-响应模型)的web层轻量级框架,spring web mvc采用了MVC(模型-视图-控制器)框架设计,将web层进行职责解耦,围绕核心处理...

开源中国首席脑科主任
2016/07/23
484
0
【原创】遨游springmvc之DispatcherServlet

1.机制 Dispatcher是springmvc前端控制器模式的实现,它提供一个集中的请求处理机制,所有的请求都将由一个单一的处理程序处理,Dispatcher负责请求的派遣,它与spring ioc完美继承,从而可以...

开源中国首席脑科主任
2016/07/23
125
0
【原创】遨游springmvc之HandlerAdapter

1.前言 前一篇我们讲述了HandlerMapping,它返回了请求对应的controller实例和对应的处理方法,很多读者认为就完事了,其实不然。所以我们需要这一片来讲述一下HandlerAdapter的作用。Handl...

开源中国首席脑科主任
2016/07/31
209
0
Spring MVC 执行过程详解

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。 https://blog.csdn.net/Rsx/article/details/96309840 文章目录 Spring MVC简述 大部分Java应...

不专业得Cook
07/17
0
0
Spring mvc HandlerMapping 实现机制

概述 当DispatcherServlet接受到客户端的请求后,SpringMVC 通过 HandlerMapping 找到请求的Controller。 HandlerMapping 在这里起到路由的作用,负责找到请求的Controller。 Spring MVC 默认...

轨迹_
2018/06/23
94
0

没有更多内容

加载失败,请刷新页面

加载更多

堆”和“栈

C++作为一款C语言的升级版本,具有非常强大的功能。它不但能够支持各种程序设计风格,而且还具有C语言的所有功能。我们在这里为大家介绍的是其中一个比较重要的内容,C++内存区域的基本介绍。...

SibylY
25分钟前
2
0
总结:Https

一、介绍 简单理解,https即在http协议的基础上,增加了SSL协议,保障数据传输的安全性。 它由以前的http—–>tcp,改为http——>SSL—–>tcp;https采用了共享密钥加密+公开密钥加密的方式 ...

浮躁的码农
27分钟前
3
0
数据库表与表之间的一对一、一对多、多对多关系

表1 foreign key 表2 多对一:表 1 的多条记录对应表 2 的一条记录 利用foreign key的原理我们可以制作两张表的多对多,一对一关系 多对多: 表1的多条记录可以对应表2的一条记录 表2的多条记...

Garphy
58分钟前
7
0
MySQL 表崩溃修复

MySQL日志报错 2019-10-19 13:41:51 19916 [ERROR] /usr/local/mysql/bin/mysqld: Table './initread_hss/user_info' is marked as crashed and should be repaired2019-10-19 13:41:51 1......

雁南飞丶
今天
6
0
Error和Exception

1.Error类和Exception类都是继承Throwable类 2.Error(错误)是系统中的错误,程序员是不能改变的和处理的,是在程序编译时出现的错误,只能通过修改程序才能修正。一般是指与虚拟机相关的问...

大瑞清_liurq
今天
4
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部