springmvc 如何通过请求url找到具体的controller方法

springmvc 如何通过请求url找到具体的controller方法

1.前言

第一次写博客,有点小紧张,本博客主要用来记录平时在工作中遇到的各种问题和解决办法,同时也记录自己学习的点点滴滴,内容如果有瑕疵,请在评论区告知,本人会尽快修改,谢谢。

2.实例代码

  先上实例代码:
	@RestController
public class DomeController {

	@RequestMapping("/hello")
	public String hello(){
		return "hello";
	}
}

直接在浏览器输入http://127.0.0.1:8080/hello 就可得到我们预期的界面。

在这里插入图片描述

示例程序虽然很简单,但不禁有疑问,Springmvc到底使用的哪种魔法,能根据简单的 "/hello"请求就能确定我们的具体的处理器,同时也能定位调用哪个具体的方法?带着这个疑问,我们继续往下看。

3.根据url获取具体的处理方法

Springmvc使用中央控制器DispatcherServlet进行请求的处理,当用户在浏览器发送请求时,会被DispatcherServlet拦截,会调用父类的doGet()或者doPost()方法进行请求的处理,但这两个方法经过层层跳转,都会到达DispatcherServlet的doDispatch()方法,所有我们的分析中doDispatch()方法开始。

	protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
		//保存原始请求
		HttpServletRequest processedRequest = request;
		HandlerExecutionChain mappedHandler = null;
		boolean multipartRequestParsed = false;
		try {
			ModelAndView mv = null;
			try {
				// Determine handler for the current request.
				// 标记1.0 根据request寻找handle 
				mappedHandler = getHandler(processedRequest);
				//根据handle寻找HandlerAdapter
				//所有这里使用了适配器模式,对调用方法的接收参数和响应参数进行处理
				HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
				//应用preHandle方法
				if (!mappedHandler.applyPreHandle(processedRequest, response)) {
					return;
				}
				// Actually invoke the handler.
				//调用真正的handle
				mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
				//应用视图解析器
				applyDefaultViewName(processedRequest, mv);
				//应用postHandle方法
				mappedHandler.applyPostHandle(processedRequest, response, mv);
			}
	}

本次分析的主要代码就在标记1.0处,getHandler()会根据请求request的url来获取一个处理器执行链HandlerExecutionChain,HandlerExecutionChain内部包含HandlerMethod对象和一系列的拦截器,要知晓获取HandlerExecutionChain的具体过程,我们必然要进入DispatcherServlet的getHandler()内部看看。


	protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
		if (this.handlerMappings != null) {
			//request 包括请求路径
			//handlerMappings 包括url 和controller&method的对应关系
			for (HandlerMapping hm : this.handlerMappings) {
				//获取处理器执行链
				HandlerExecutionChain handler = hm.getHandler(request);
				if (handler != null) {
					return handler;
				}
			}
		}
		return null;
	}

可以看到getHandler()方法通过遍历this.handlerMappings对象,依次调用HandlerMapping 的getHandler()方法尝试获取处理器执行链,如果遍历完HandlerMapping 还没有获取到处理器链则返回null。

但HandlerMapping 对象具体有什么用?如何通过HandlerMapping 的getHandler()方法获取处理器执行链?
在这里插入图片描述
Springmvc默认会为我们加载5个HandlerMapping ,但项目中Controller一般会和@RequestMapping注解代码使用,这里直接进入AbstractHandlerMapping类所在的getHandler()方法内部查看。

HandlerMapping接口的抽象类AbstractHandlerMapping类的getHandler()方法源码

public final HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
		//标记2. 获取处理器
		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 = obtainApplicationContext().getBean(handlerName);
		}

		//用HandlerExecutionChain 封装handler
		HandlerExecutionChain executionChain = getHandlerExecutionChain(handler, request);
		
		return executionChain;
	}

这里先说明一下HandlerMethod对象,这个对象主要封装@requestMapping标记处理器方法,也就是我们具体的请求方法。
逻辑很清晰,getHandlerInternal()方法获取一个HandlerMethod,使用getHandlerExecutionChain()方法对请求和HandlerMethod进行封装,返回处理器执行链,这里我们只简单分析获取处理器的函数getHandlerInternal()。

protected HandlerMethod getHandlerInternal(HttpServletRequest request) throws Exception {
		//获取处理url
		String lookupPath = getUrlPathHelper().getLookupPathForRequest(request);
		
		this.mappingRegistry.acquireReadLock();
		try {
			//获取处理方法 controller&method
			HandlerMethod handlerMethod = lookupHandlerMethod(lookupPath, request);
			//获取具体的处理器方法,handlerMethod包含一个处理器
			return (handlerMethod != null ? handlerMethod.createWithResolvedBean() : null);
		}
		finally {
			this.mappingRegistry.releaseReadLock();
		}
	}

首先获取request请求的url,然后使用url去获取HandlerMethod
lookupHandlerMethod()方法是HandlerMethod的关键

protected HandlerMethod lookupHandlerMethod(String lookupPath, HttpServletRequest request) throws Exception {
		List<Match> matches = new ArrayList<>();
		//根据请求url获取候选处理方法 可能存在多个处理方法,应该有get/ post /put ... 从urlLookup获取
		List<T> directPathMatches = this.mappingRegistry.getMappingsByUrl(lookupPath);
		if (directPathMatches != null) {
			//获取准确的处理方法 将获取到的处理器方法和处理器包装起来放入matches中
			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));
			//对处理器进行排序
			matches.sort(comparator);
			//获取第一个处理器&method
			Match bestMatch = matches.get(0);
			bestMatch.handlerMethod);
			handleMatch(bestMatch.mapping, lookupPath, request);
			return bestMatch.handlerMethod;
		}
		else {
			return handleNoMatch(this.mappingRegistry.getMappings().keySet(), lookupPath, request);
		}
	}

在这里插入图片描述
可以通过这里发现,springmvc在底层缓存了url和HandlerMethod之间的对应关系,该映射关系被mappingRegistry所保存,当有请求来时,直接通过url获取HandlerMethod对象。
那url和HandlerMethod之间的映射关系是何时建立的喃?springmvc是如何解析@requestMapping注解的,等候下次分析吧。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值