SpringMVC-详解HandlerMapping的前世今生之诞生

本文基于spring 5.5.2.release

springmvc接收到请求后,第一步要做的事情就是查找Handler,以确定是否可以处理该请求,Handler可以简单的理解为Controller。这个查找的过程是由HandlerMapping完成的。
HandlerMapping是一个接口,实现类必须实现如下方法:

//该方法的返回值HandlerExecutionChain是对Handler的封装,它内部还封装了拦截器
//在执行Handler之前和之后,都会调用拦截器
HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception;

springmvc提供了多种不同的HandlerMapping实现类,每个实现类都可以根据请求参数查找对应的Handler。这些实现类实例化对象后都放到DispatcherServlet的属性handlerMappings中,以供后续遍历时查找访问。那这些对象是如何放到属性handlerMappings中的?

一、初始化属性handlerMappings

下面是DispatcherServlet的属性handlerMappings的定义:

	/** List of HandlerMappings used by this servlet. */
	@Nullable
	private List<HandlerMapping> handlerMappings;

下面来看一下这个属性是如何初始化的。
DispatcherServlet实现了Servlet接口,因此当第一次外部请求时,会触发该类的init()方法,init()方法最终调用到initHandlerMappings():

	//入参context是web环境下的ApplicationContext对象,
	//默认实现类是AnnotationConfigServletWebServerApplicationContext
	private void initHandlerMappings(ApplicationContext context) {
		this.handlerMappings = null;
		//detectAllHandlerMappings默认是true,
		//表示是自动检测,还是只从容器中查找名字为“handlerMapping”的对象
		if (this.detectAllHandlerMappings) {
			//下面开始自动查找HandlerMapping对象
			//下面这一行代码用于从容器中查找类型为HandlerMapping的对象,
			//包括父容器也会一并查找,scope=prototype类型的也会搜索到
			Map<String, HandlerMapping> matchingBeans =
					BeanFactoryUtils.beansOfTypeIncludingAncestors(context, HandlerMapping.class, true, false);
					
			if (!matchingBeans.isEmpty()) {
				this.handlerMappings = new ArrayList<>(matchingBeans.values());
				// We keep HandlerMappings in sorted order.
				AnnotationAwareOrderComparator.sort(this.handlerMappings);//排序
			}
		}
		else {
			try {
				//下面是从容器中查找名字为“handlerMapping”且类型为HandlerMapping的对象
				HandlerMapping hm = context.getBean(HANDLER_MAPPING_BEAN_NAME, HandlerMapping.class);
				this.handlerMappings = Collections.singletonList(hm);
			}
			catch (NoSuchBeanDefinitionException ex) {
				// Ignore, we'll add a default HandlerMapping later.
			}
		}

		// Ensure we have at least one HandlerMapping, by registering
		// a default HandlerMapping if no other mappings are found.
		//如果上述过程没有找到HandlerMapping,那么读取文件DispatcherServlet.properties,
		//从该文件中加载HandlerMapping对象
		if (this.handlerMappings == null) {
			this.handlerMappings = getDefaultStrategies(context, HandlerMapping.class);
			if (logger.isTraceEnabled()) {
				logger.trace("No HandlerMappings declared for servlet '" + getServletName() +
						"': using default strategies from DispatcherServlet.properties");
			}
		}
	}

从上面代码可以看到springmvc直接从容器中获取HandlerMapping对象添加到属性handlerMappings中。那么容器中的对象从哪来?答案是WebMvcConfigurationSupport。WebMvcConfigurationSupport提供了requestMappingHandlerMapping()、viewControllerHandlerMapping()等创建HandlerMapping的方法。该类一共创建四个,分别是:

  • RequestMappingHandlerMapping
  • BeanNameUrlHandlerMapping
  • RouterFunctionMapping
  • resourceHandlerMapping

除了上面四个,还有一个是在EnableWebMvcConfiguration中创建的:

  • WelcomePageHandlerMapping

有的HandlerMapping对象,指定了order属性,这个属性用于排序使用,可以看到initHandlerMappings()里面也对这些对象做了排序,排序结果如下:
在这里插入图片描述
DispatcherServlet遍历HandlerMapping便是以上图的顺序进行,当从其中一个HandlerMapping中找到合适的Handler,那么后面的便不再遍历,下面是遍历的代码,DispatcherServlet便是调用下面这个方法找到合适的Handler:

	protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
		if (this.handlerMappings != null) {
			for (HandlerMapping mapping : this.handlerMappings) {
				HandlerExecutionChain handler = mapping.getHandler(request);
				if (handler != null) {
					return handler;//找到后直接返回
				}
			}
		}
		return null;
	}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值