SpringMVC之RequestMappingHandlerMapping

一句话:这个类主要作用之一是将请求url和对应方法的映射关系缓存起来,方便后续快速路由请求到指定的method

原理

RequestMappingHandlerMapping注入时机

我们要用Springmvc,核心就是用它的DispatcherServlet;DispatcherServlet是一个servlet,它的生命周期受容器控制,即容器在servlet实例化后会调用对应的init方法进行初始化(实际是执行父类 org.springframework.web.servlet.HttpServletBean 的init方法)

在这里插入图片描述
方法会执行一系列的初始化动作
在这里插入图片描述
这里我们分析一下

org.springframework.web.servlet.DispatcherServlet#initHandlerMappings

这个方法,因为这个方法会注入我们今天的主角类;下面这段代码会将默认的策略类组合赋值给 DispatcherServlet 相应的属性
在这里插入图片描述

RequestMappingHandlerMapping分析
  • 首先来看下RequestMappingHandlerMapping的继承关系图

在这里插入图片描述
这里我最关心也是最重要的一点,它是一个 InitializingBean !!!
这很重要,这表明在类实例化后会自动执行它的 afterPropertiesSet 方法,调用链如下:

org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping#afterPropertiesSet

	@Override
	public void afterPropertiesSet() {
		this.config = new RequestMappingInfo.BuilderConfiguration();
		this.config.setUrlPathHelper(getUrlPathHelper());
		this.config.setPathMatcher(getPathMatcher());
		this.config.setSuffixPatternMatch(this.useSuffixPatternMatch);
		this.config.setTrailingSlashMatch(this.useTrailingSlashMatch);
		this.config.setRegisteredSuffixPatternMatch(this.useRegisteredSuffixPatternMatch);
		this.config.setContentNegotiationManager(getContentNegotiationManager());

// @A
		super.afterPropertiesSet();
	}
  • @A:调用父类方法即org.springframework.web.servlet.handler.AbstractHandlerMethodMapping#afterPropertiesSet
@Override
	public void afterPropertiesSet() {
//@B
		initHandlerMethods();
	}

	/**
	 * Scan beans in the ApplicationContext, detect and register handler methods.
	 * @see #isHandler(Class)
	 * @see #getMappingForMethod(Method, Class)
	 * @see #handlerMethodsInitialized(Map)
	 */
	protected void initHandlerMethods() {
		if (logger.isDebugEnabled()) {
			logger.debug("Looking for request mappings in application context: " + getApplicationContext());
		}
//@C
		String[] beanNames = (this.detectHandlerMethodsInAncestorContexts ?
				BeanFactoryUtils.beanNamesForTypeIncludingAncestors(obtainApplicationContext(), Object.class) :
				obtainApplicationContext().getBeanNamesForType(Object.class));
//@D
		for (String beanName : beanNames) {
			if (!beanName.startsWith(SCOPED_TARGET_NAME_PREFIX)) {
				Class<?> beanType = null;
				try {
					beanType = obtainApplicationContext().getType(beanName);
				}
				catch (Throwable ex) {
					// An unresolvable bean type, probably from a lazy bean - let's ignore it.
					if (logger.isDebugEnabled()) {
						logger.debug("Could not resolve target class for bean with name '" + beanName + "'", ex);
					}
				}
//@E
				if (beanType != null && isHandler(beanType)) {
					detectHandlerMethods(beanName);
				}
			}
		}
		handlerMethodsInitialized(getHandlerMethods());
	}
  • @B:调用 initHandlerMethods 方法

  • @C:根据是否搜索parent容器(false)获得容器中所有的类;(因为这是springmvc项目,只需要搜索当前springmvc容器定义的所有类,准确的说后续只需要接卸controller类)

  • @D:循环遍历beaname获得controller bean

  • @E:执行 isHandler 方法(被 @Controller注解或者被 @RequestMapping注解标注的类会返回true),根据返回值决定是否解析类里面的方法

  • 下面我们来到解析url和方法的具体方法 detectHandlerMethods

protected void detectHandlerMethods(final Object handler) {
//@F
		Class<?> handlerType = (handler instanceof String ?
				obtainApplicationContext().getType((String) handler) : handler.getClass());

		if (handlerType != null) {
			final Class<?> userType = ClassUtils.getUserClass(handlerType);
//@G
			Map<Method, T> methods = MethodIntrospector.selectMethods(userType,
					(MethodIntrospector.MetadataLookup<T>) method -> {
						try {
							return getMappingForMethod(method, userType);
						}
						catch (Throwable ex) {
							throw new IllegalStateException("Invalid mapping on handler class [" +
									userType.getName() + "]: " + method, ex);
						}
					});
			if (logger.isDebugEnabled()) {
				logger.debug(methods.size() + " request handler methods found on " + userType + ": " + methods);
			}
//@H
			methods.forEach((method, mapping) -> {
				Method invocableMethod = AopUtils.selectInvocableMethod(method, userType);
				registerHandlerMethod(handler, invocableMethod, mapping);
			});
		}
	}

	/**
	 * Register a handler method and its unique mapping. Invoked at startup for
	 * each detected handler method.
	 * @param handler the bean name of the handler or the handler instance
	 * @param method the method to register
	 * @param mapping the mapping conditions associated with the handler method
	 * @throws IllegalStateException if another method was already registered
	 * under the same mapping
	 */
	protected void registerHandlerMethod(Object handler, Method method, T mapping) {
	//@I
		this.mappingRegistry.register(mapping, handler, method);
	}
  • @F:获得controller类 Class

  • @G:解析controller获得一个Map<Method,RequestMappingInfo>的映射关系,具体封装RequestMappingInfo是子类 org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping#getMappingForMethod方法

  • @H:循环遍历找到的controller方法,缓存url和方法的关系

  • @I:mapping是RequestMappingInfo对象;handler是controller的类名称(小写开头);method是被@RequestMapping注解标记的方法

  • MappingRegistry是 AbstractHandlerMethodMapping的内部类,我们看看register方法

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

				if (logger.isInfoEnabled()) {
					logger.info("Mapped \"" + mapping + "\" onto " + handlerMethod);
				}
//@L
				this.mappingLookup.put(mapping, handlerMethod);
//@M
				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);
				}
//@N
				this.registry.put(mapping, new MappingRegistration<>(mapping, handlerMethod, directUrls, name));
			}
			finally {
				this.readWriteLock.writeLock().unlock();
			}
		}
  • @J:获得一个写锁
  • @K:根据类名和方法封装成一个HandlerMethod对象
  • @L:放入RequestMappingInfo为key的map中
  • @M:获得方法上声明的url集合,因为 @RequestMapping 注解可以指定多个url;然后缓存到url为key的LinkedMultiValueMap中
  • @N:缓存进以RequestMappingInfo为key的 HashMap 中,value是<RequestMappingInfo,方法,注解上标注的url集合,根据策略处理后的方法名>的封装对象
RequestMappingHandlerMapping方法路由
  • 一句话:根据J2EE规范处理http请求的方法是 DispatcherServlet的doService方法,核心方法是org.springframework.web.servlet.DispatcherServlet#doDispatch(这个方法是被父类的doService方法调起)
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);

//@A
				// Determine handler for the current request.
				mappedHandler = getHandler(processedRequest);
				if (mappedHandler == null) {
					noHandlerFound(processedRequest, response);
					return;
				}

				// Determine handler adapter for the current request.
				HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());

				// Process last-modified header, if supported by the handler.
				String method = request.getMethod();
				boolean isGet = "GET".equals(method);
				if (isGet || "HEAD".equals(method)) {
					long lastModified = ha.getLastModified(request, mappedHandler.getHandler());
					if (logger.isDebugEnabled()) {
						logger.debug("Last-Modified value for [" + getRequestUri(request) + "] is: " + lastModified);
					}
					if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) {
						return;
					}
				}
//@A1
				if (!mappedHandler.applyPreHandle(processedRequest, response)) {
					return;
				}
//@B
				// Actually invoke the handler.
				mv = ha.handle(processedRequest, response, mappedHandler.getHandler());

				if (asyncManager.isConcurrentHandlingStarted()) {
					return;
				}
//@C
				applyDefaultViewName(processedRequest, mv);
				mappedHandler.applyPostHandle(processedRequest, response, mv);
			}
			catch (Exception ex) {
				dispatchException = ex;
			}
			catch (Throwable err) {
				// As of 4.3, we're processing Errors thrown from handler methods as well,
				// making them available for @ExceptionHandler methods and other scenarios.
				dispatchException = new NestedServletException("Handler dispatch failed", err);
			}

//@D
			processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
		}
		catch (Exception ex) {
			triggerAfterCompletion(processedRequest, response, mappedHandler, ex);
		}
		catch (Throwable err) {
			triggerAfterCompletion(processedRequest, response, mappedHandler,
					new NestedServletException("Handler processing failed", err));
		}
		finally {
			if (asyncManager.isConcurrentHandlingStarted()) {
				// Instead of postHandle and afterCompletion
				if (mappedHandler != null) {
					mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);
				}
			}
			else {
				// Clean up any resources used by a multipart request.
				if (multipartRequestParsed) {
					cleanupMultipart(processedRequest);
				}
			}
		}
	}
  • @A:根据HttpServletRequest请求对象获得一个 HandlerExecutionChain对象(实际上是对匹配到的HandlerMethod的封装),具体逻辑在org.springframework.web.servlet.DispatcherServlet#getHandler方法,下面我们具体分析

  • @A1:执行拦截器逻辑,这里可见拦截器是在执行真正方法前被执行

  • @B:真正执行HandleMethod的方法,这里是委托给HandlerAdapter(真实实现类是:RequestMappingHandlerAdapter)来执行的,这里会返回一个 ModelAndView

  • @C:设置view的名称,执行一些后置处理

  • @D:处理结果,渲染界面(渲染界面逻辑下一篇文章详细揭秘)

  • 接上面@A里分析org.springframework.web.servlet.DispatcherServlet#getHandler方法如何获得HandlerExecutionChain对象

	@Nullable
	protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
		if (this.handlerMappings != null) {
			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;
	}
  • 这个方法逻辑是:循环遍历 handlerMappings,然后调用getHandler方法,如果不为空的话就返回;根据前面的内容我们知道 handlerMappings是2个对象(怎么少了一个?)
    +在这里插入图片描述
    这里其实起作用的就是上面框架注入的 RequestMappingHandlerMapping,getHandler方法是在它的父类org.springframework.web.servlet.handler.AbstractHandlerMapping#getHandler里实现的;

具体调用栈如下:

org.springframework.web.servlet.handler.AbstractHandlerMapping#getHandler
|
org.springframework.web.servlet.handler.AbstractHandlerMethodMapping#getHandlerInternal(根据url找找到之前缓存的元数据信息并封装成 HandlerMethod 对象)
|
org.springframework.web.servlet.handler.AbstractHandlerMapping#getHandlerExecutionChain(将HandlerMethod封装成HandlerExecutionChain对象并返回)
|

最终返回一个 HandlerExecutionChain 对象,传给@B逻辑

  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
RequestMappingHandlerMappingSpringMVC框架中的一个处理器映射器,它是HandlerMapping接口的一个实现类。它的主要作用是根据请求来获取对应的处理器(Handler),它会根据请求的路径(path)来找到一个最匹配的HandlerMethod来处理请求。RequestMappingHandlerMapping会与DispatcherServlet一起工作,在DispatcherServlet中会调用其getHandler方法来获取HandlerExecutionChain对象,然后将该对象返回给DispatcherServlet,以供后续的处理。RequestMappingHandlerMapping的命名来源于它专门用于处理使用@RequestMapping注解的方法。总之,RequestMappingHandlerMappingSpringMVC中起到了寻找并映射请求处理器的重要作用。123 #### 引用[.reference_title] - *1* [04-RequestMappingHandlerMapping](https://blog.csdn.net/my_momo_csdn/article/details/102923401)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v92^chatsearchT3_1"}} ] [.reference_item] - *2* [探索SpringMVC-HandlerMapping之RequestMappingHandlerMapping](https://blog.csdn.net/Evan_L/article/details/128356004)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v92^chatsearchT3_1"}} ] [.reference_item] - *3* [RequestMappingHandlerMapping详解](https://blog.csdn.net/m0_51945027/article/details/119772969)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v92^chatsearchT3_1"}} ] [.reference_item] [ .reference_list ]

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值