栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

SpringMVC源码系列 HandlerMapping

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

SpringMVC源码系列 HandlerMapping

一、HandlerMapping 1.概述

HandlerMapping映射器主要的作用就是将请求和方法映射起来,那么主要考虑:

  1. 映射器有几种,如何初始化的?
  2. 既然有了映射器,那么如何将请求和逻辑方法映射起来?

上面的问题简单来说就是请求来了,需要找到处理方法,需要映射器来负责,那么映射器有哪些,它怎么帮我找到方法,那么带着上面的问题,然后有目的性的看源码

2.调用流程

SpringMVC的核心类是 DispatcherServlet,其中的核心方式是 doDispatch()方法,在这个方法里面会获取到处理该请求的映射器,那么是怎么被调用到的呢?

我们请求会找到 doGet()和doPost()方法,但是DispatcherServlet类中没有,那么请求首先会到达父类的 frameworkServlet的doGet() 或 doPost() 方法,

   // frameworkServlet类 
    @Override
	protected final void doGet(HttpServletRequest request, HttpServletResponse response)
			throws ServletException, IOException {

		processRequest(request, response);
	}
	@Override
	protected final void doPost(HttpServletRequest request, HttpServletResponse response)
			throws ServletException, IOException {

		processRequest(request, response);
	}

我们发现父类 frameworkServlet的 doXXX 类似的方法都回调用 processRequest() 方法,我们查看此方法

protected final void processRequest(HttpServletRequest request, HttpServletResponse response)
			throws ServletException, IOException {

		long startTime = System.currentTimeMillis();
		Throwable failureCause = null;

		LocaleContext previousLocaleContext = LocaleContextHolder.getLocaleContext();
		LocaleContext localeContext = buildLocaleContext(request);

		RequestAttributes previousAttributes = RequestContextHolder.getRequestAttributes();
		ServletRequestAttributes requestAttributes = buildRequestAttributes(request, response, previousAttributes);

		WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
		asyncManager.registerCallableInterceptor(frameworkServlet.class.getName(), new RequestBindingInterceptor());

		initContextHolders(request, localeContext, requestAttributes);

		try {
             // 调用核心方法
			doService(request, response);
		}
		catch (ServletException | IOException ex) {
			failureCause = ex;
			throw ex;
		}
		catch (Throwable ex) {
			failureCause = ex;
			throw new NestedServletException("Request processing failed", ex);
		}

		finally {
			resetContextHolders(request, previousLocaleContext, previousAttributes);
			if (requestAttributes != null) {
				requestAttributes.requestCompleted();
			}
			logResult(request, response, failureCause, asyncManager);
			publishRequestHandledEvent(request, response, startTime, failureCause);
		}
	}

我们发现其中最重要的方法 doService()方法,查看该方法

protected abstract void doService(HttpServletRequest request, HttpServletResponse response)
			throws Exception;

发现这是个抽象方法,于是查看其子类 DispatcherServlet类 重写了该方法

@Override
	protected void doService(HttpServletRequest request, HttpServletResponse response) throws Exception {
		logRequest(request);

		// Keep a snapshot of the request attributes in case of an include,
		// to be able to restore the original attributes after the include.
		Map attributesSnapshot = null;
		if (WebUtils.isIncludeRequest(request)) {
			attributesSnapshot = new HashMap<>();
			Enumeration attrNames = request.getAttributeNames();
			while (attrNames.hasMoreElements()) {
				String attrName = (String) attrNames.nextElement();
				if (this.cleanupAfterInclude || attrName.startsWith(DEFAULT_STRATEGIES_PREFIX)) {
					attributesSnapshot.put(attrName, request.getAttribute(attrName));
				}
			}
		}

		// Make framework objects available to handlers and view objects.
		request.setAttribute(WEB_APPLICATION_CONTEXT_ATTRIBUTE, getWebApplicationContext());
		request.setAttribute(LOCALE_RESOLVER_ATTRIBUTE, this.localeResolver);
		request.setAttribute(THEME_RESOLVER_ATTRIBUTE, this.themeResolver);
		request.setAttribute(THEME_SOURCE_ATTRIBUTE, getThemeSource());

		if (this.flashMapManager != null) {
			FlashMap inputFlashMap = this.flashMapManager.retrieveAndUpdate(request, response);
			if (inputFlashMap != null) {
				request.setAttribute(INPUT_FLASH_MAP_ATTRIBUTE, Collections.unmodifiableMap(inputFlashMap));
			}
			request.setAttribute(OUTPUT_FLASH_MAP_ATTRIBUTE, new FlashMap());
			request.setAttribute(FLASH_MAP_MANAGER_ATTRIBUTE, this.flashMapManager);
		}

		RequestPath previousRequestPath = null;
		if (this.parseRequestPath) {
			previousRequestPath = (RequestPath) request.getAttribute(ServletRequestPathUtils.PATH_ATTRIBUTE);
			ServletRequestPathUtils.parseAndCache(request);
		}

		try {
             // 核心调用
			doDispatch(request, response);
		}
		finally {
			if (!WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted()) {
				// Restore the original attribute snapshot, in case of an include.
				if (attributesSnapshot != null) {
					restoreAttributesAfterInclude(request, attributesSnapshot);
				}
			}
			if (this.parseRequestPath) {
				ServletRequestPathUtils.setParsedRequestPath(previousRequestPath, request);
			}
		}
	}

子类 DispatcherServlet重写了该方法,并且在方法中调用了最最最最重要的方法 doDispatch

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) {
					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 = HttpMethod.GET.matches(method);
				if (isGet || HttpMethod.HEAD.matches(method)) {
					long lastModified = ha.getLastModified(request, mappedHandler.getHandler());
					if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) {
						return;
					}
				}

				if (!mappedHandler.applyPreHandle(processedRequest, response)) {
					return;
				}

				// Actually invoke the handler.
				mv = ha.handle(processedRequest, response, mappedHandler.getHandler());

				if (asyncManager.isConcurrentHandlingStarted()) {
					return;
				}

				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);
			}
			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);
				}
			}
		}
	}

我们在doDispatch()方法里面看到将请求和handle映射起来的方法getHandler(processedRequest) 这也是本篇文章的重点

@Nullable
	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;
	}

这个方法里面主要有2点,需要我们详细讲述,

  1. 我们在匹配的时候发现这个 handlerMappings里面是有值得,那么这个值是如何初始化的?
  2. 这个 handlerMappings存在多个,代表映射器有不同的类型,那么通过 getHandler()进行匹配的时候,不同的映射器,匹配方式也不同,那么如何匹配?
二、HandlerMapping 初始化

上面我们看到了 handlerMappings里面存在多个值 ,代表多个映射器类型,因为我们接受请求的方式可能不同,所以肯定有多种映射器,那么接下来介绍接受请求的方式,以及HandlerMapping接口以及子类,并且这些是如何初始化的

1、编写接口的方法 1)实现HttpRequestHandler接口
@Component("/handler")
public class HandleController implements HttpRequestHandler {
    @Override
    public void handleRequest(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) throws ServletException, IOException {
        System.out.println("实现HttpRequestHandler接口方式");
    }
}
2)实现Controller接口
@Component("/name")
public class BeanNameController implements Controller {
    @Override
    public ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response) throws Exception {
        System.out.println("实现Controller接口方式");
        return null;
    }
}
3)注解方法
@RestController
@RequestMapping("/controller")
public class HelloController {

    @Autowired(required = false)
    private UserMapper userMapper;

    @GetMapping(value = "/test")
    public Object test(RequestEntity requestEntity) {
        System.out.println("test");
        return "哈哈哈";
    }
}
2、HandlerMapping 接口以及子类

HandlerMapping接口中包含多个字段和1个最重要的方法 getHandler(HttpServletRequest request) 这个方法就是将请求和Handler进行映射

public interface HandlerMapping {
	@Nullable
	HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception;
}
子类

HandlerMapping映射器主要有3个常见的子类:

BeanNameUrlHandlerMapping

RequestMappingHandlerMapping

RouterFunctionMapping

类关系图

3、初始化

在DispatcherServlet类中 我们进行初始化,Handler会调用initHandlerMappings(ApplicationContext context) 方法,那么这个类的调用流程是:

org.springframework.web.servlet.HttpServletBean#init()
--> org.springframework.web.servlet.frameworkServlet#initServletBean()
--> org.springframework.web.servlet.frameworkServlet#initWebApplicationContext()
--> org.springframework.web.servlet.DispatcherServlet#onRefresh()
--> org.springframework.web.servlet.DispatcherServlet#initStrategies()
--> org.springframework.web.servlet.DispatcherServlet#initHandlerMappings()

通过上面的调用流程,我们会调用到这个初始化方法,那么我们来看一下这个方法

private void initHandlerMappings(ApplicationContext context) {
		this.handlerMappings = null;
		
		if (this.detectAllHandlerMappings) {
			// Find all HandlerMappings in the ApplicationContext, including ancestor contexts.
             // 从spring的容器中取,如果这儿加了@EnableWebMvc注解,这儿取出来的就是三个
			Map 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 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.
         // 如果上面从Spring容器中获取不到映射器,那么就调用方法,从配置文件取
		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");
			}
		}

		for (HandlerMapping mapping : this.handlerMappings) {
			if (mapping.usesPathPatterns()) {
				this.parseRequestPath = true;
				break;
			}
		}
	}

上面的代码就是如果加了@EnableWebMvc注解的话,就直接能从spring容器中取出对应的三个HandlerMapping,如果没有加,则调用 getDefaultStrategies() 方法,

protected  List getDefaultStrategies(ApplicationContext context, Class strategyInterface) {
		if (defaultStrategies == null) {
			try {
				// Load default strategy implementations from properties file.
				// This is currently strictly internal and not meant to be customized
				// by application developers.
                  // 从默认的配置中 ‘DispatcherServlet.properties’ 取值 
				ClassPathResource resource = new ClassPathResource(DEFAULT_STRATEGIES_PATH, DispatcherServlet.class);
                  // 加载属性
				defaultStrategies = PropertiesLoaderUtils.loadProperties(resource);
			}
			catch (IOException ex) {
				throw new IllegalStateException("Could not load '" + DEFAULT_STRATEGIES_PATH + "': " + ex.getMessage());
			}
		}
         // 遍历默认的映射器注入
		String key = strategyInterface.getName();
		String value = defaultStrategies.getProperty(key);
		if (value != null) {
			String[] classNames = StringUtils.commaDelimitedListToStringArray(value);
			List strategies = new ArrayList<>(classNames.length);
			for (String className : classNames) {
				try {
					Class clazz = ClassUtils.forName(className, DispatcherServlet.class.getClassLoader());
					Object strategy = createDefaultStrategy(context, clazz);
					strategies.add((T) strategy);
				}
				catch (ClassNotFoundException ex) {
					throw new BeanInitializationException(
							"Could not find DispatcherServlet's default strategy class [" + className +
							"] for interface [" + key + "]", ex);
				}
				catch (linkageError err) {
					throw new BeanInitializationException(
							"Unresolvable class definition for DispatcherServlet's default strategy class [" +
							className + "] for interface [" + key + "]", err);
				}
			}
			return strategies;
		}
		else {
			return Collections.emptyList();
		}
	}

上面的代码说如果spring容器中取不到,那么会从默认的配置文件中取,这个配置在通过IDEA中查看jar包 spring-webmvc-5.3.9.jar,打开可以看到其中的一段配置

org.springframework.web.servlet.HandlerMapping=org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping,
	org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping,
	org.springframework.web.servlet.function.support.RouterFunctionMapping

也就是如果从Spring容器中加载映射器,如果没有,则加载默认的映射器,这样做是为了我们可能会扩展映射器,于是先从Spring中加载,上面两种方式的区别就在于:如果加了@EnableWebMvc注解的话,这三个类就直接受spring的管理了,同时这些Bean会进行一个完整的生命周期。而那种从配置文件中加载的,就没有经历一个完整的生命周期

三、getHandler() 映射方法

上面我们介绍了HanlerMapping的分类及初始化,也就是有了映射器了,那么接下来我们介绍如何通过 getHandler()方法将请求和方法映射起来,我们需要分别介绍,不同的映射器方法不同

1、BeanNameUrlHandlerMapping 请求流程

第一个是BeanNameUrlHandlerMapping,我们先来看看这个类中的getHandler方法的代码,

注意: HandlerMapping有很多子类,我们可以通过上面的类关系图, 看到 BeanNameUrlHandlerMapping 并没有重写getHandler()方法,于是往上查,发现AbstractDetectingUrlHandlerMapping类 和 AbstractUrlHandlerMapping类 都没有重写,在AbstractHandlerMapping类里面重写了

AbstractUrlHandlerMapping类
    @Override
	@Nullable
	public final HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
		//获取指定的handler
         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);
		}

		// Ensure presence of cached lookupPath for interceptors and others
		if (!ServletRequestPathUtils.hasCachedPath(request)) {
			initLookupPath(request);
		}

		HandlerExecutionChain executionChain = getHandlerExecutionChain(handler, request);

		if (logger.isTraceEnabled()) {
			logger.trace("Mapped to " + handler);
		}
		else if (logger.isDebugEnabled() && !DispatcherType.ASYNC.equals(request.getDispatcherType())) {
			logger.debug("Mapped to " + executionChain.getHandler());
		}

		if (hasCorsConfigurationSource(handler) || CorsUtils.isPreFlightRequest(request)) {
			CorsConfiguration config = getCorsConfiguration(handler, request);
			if (getCorsConfigurationSource() != null) {
				CorsConfiguration globalConfig = getCorsConfigurationSource().getCorsConfiguration(request);
				config = (globalConfig != null ? globalConfig.combine(config) : config);
			}
			if (config != null) {
				config.validateAllowCredentials();
			}
			executionChain = getCorsHandlerExecutionChain(request, executionChain, config);
		}

		return executionChain;
	}

上面的代码通过调用getHandlerInternal(request)方法通过request来获取这个Handler,那么这个方法干了什么具体的代码如下:

这getHandlerInternal() 也有多个子类重写了,我们需要找到其父类 AbstractUrlHandlerMapping中重写的

    @Override
	@Nullable
	protected Object getHandlerInternal(HttpServletRequest request) throws Exception {
         // 获取请求url地址
		String lookupPath = initLookupPath(request);
		Object handler;
		if (usesPathPatterns()) {
			RequestPath path = ServletRequestPathUtils.getParsedRequestPath(request);
			handler = lookupHandler(path, lookupPath, request);
		}
		else {
             // 获取handler
			handler = lookupHandler(lookupPath, request);
		}
		if (handler == null) {
			// We need to care for the default handler directly, since we need to
			// expose the PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE for it as well.
			Object rawHandler = null;
			if (StringUtils.matchesCharacter(lookupPath, '/')) {
				rawHandler = getRootHandler();
			}
			if (rawHandler == null) {
				rawHandler = getDefaultHandler();
			}
			if (rawHandler != null) {
				// Bean name or resolved handler?
				if (rawHandler instanceof String) {
					String handlerName = (String) rawHandler;
					rawHandler = obtainApplicationContext().getBean(handlerName);
				}
				validateHandler(rawHandler, request);
				handler = buildPathExposingHandler(rawHandler, lookupPath, lookupPath, null);
			}
		}
		return handler;
	}

上面的代码是通过lookupHandler()方法来查找Handler,那么具体的代码如下:

@Nullable
	protected Object lookupHandler(String lookupPath, HttpServletRequest request) throws Exception {
         // 查找
		Object handler = getDirectMatch(lookupPath, request);
         // 如果找到了直接返回
		if (handler != null) {
			return handler;
		}

		// Pattern match?
		List matchingPatterns = new ArrayList<>();
		for (String registeredPattern : this.handlerMap.keySet()) {
			if (getPathMatcher().match(registeredPattern, lookupPath)) {
				matchingPatterns.add(registeredPattern);
			}
			else if (useTrailingSlashMatch()) {
				if (!registeredPattern.endsWith("/") && getPathMatcher().match(registeredPattern + "/", lookupPath)) {
					matchingPatterns.add(registeredPattern + "/");
				}
			}
		}
        // Pattern match?
        //正则匹配
        //省略一部分代码

        // No handler found...
        // 没有找到对应的Handler
        return null;
}

上面的方法会进行查找,如果找到,则直接返回,代表精确匹配,如果没找到,会进行正则匹配,因为我们的请求允许携带 ” @Override @Nullable protected HandlerMethod getHandlerInternal(HttpServletRequest request) throws Exception { // 获取请求的url String lookupPath = initLookupPath(request); // 加锁 this.mappingRegistry.acquireReadLock(); try { HandlerMethod handlerMethod = lookupHandlerMethod(lookupPath, request); return (handlerMethod != null ? handlerMethod.createWithResolvedBean() : null); } finally { // 解锁 this.mappingRegistry.releaseReadLock(); } }

上面的代码和BeanNameUrlHandlerMapping的代码很相似,上面的核心的方法就是lookupHandlerMethod(lookupPath, request);具体的代码如下:

	@Nullable
	protected HandlerMethod lookupHandlerMethod(String lookupPath, HttpServletRequest request) throws Exception {
         // 创建集合
		List matches = new ArrayList<>();
         // 这里是重点,直接从集合 mappingRegistry 中取值
		List directPathMatches = this.mappingRegistry.getMappingsByDirectPath(lookupPath);
		if (directPathMatches != null) {
			addMatchingMappings(directPathMatches, matches, request);
		}
		if (matches.isEmpty()) {
			addMatchingMappings(this.mappingRegistry.getRegistrations().keySet(), matches, request);
		}
		if (!matches.isEmpty()) {
			Match bestMatch = matches.get(0);
			if (matches.size() > 1) {
				Comparator comparator = new MatchComparator(getMappingComparator(request));
				matches.sort(comparator);
				bestMatch = matches.get(0);
				if (logger.isTraceEnabled()) {
					logger.trace(matches.size() + " matching mappings: " + matches);
				}
				if (CorsUtils.isPreFlightRequest(request)) {
					for (Match match : matches) {
						if (match.hasCorsConfig()) {
							return PREFLIGHT_AMBIGUOUS_MATCH;
						}
					}
				}
				else {
					Match secondBestMatch = matches.get(1);
					if (comparator.compare(bestMatch, secondBestMatch) == 0) {
						Method m1 = bestMatch.getHandlerMethod().getMethod();
						Method m2 = secondBestMatch.getHandlerMethod().getMethod();
						String uri = request.getRequestURI();
						throw new IllegalStateException(
								"Ambiguous handler methods mapped for '" + uri + "': {" + m1 + ", " + m2 + "}");
					}
				}
			}
			request.setAttribute(BEST_MATCHING_HANDLER_ATTRIBUTE, bestMatch.getHandlerMethod());
			handleMatch(bestMatch.mapping, lookupPath, request);
			return bestMatch.getHandlerMethod();
		}
		else {
			return handleNoMatch(this.mappingRegistry.getRegistrations().keySet(), lookupPath, request);
		}
	}

其中调用了一个 getMappingsByDirectPath() 方法,该方法直接从一个Map中取值

		@Nullable
		public List getMappingsByDirectPath(String urlPath) {
			return this.pathLookup.get(urlPath);
		}

我们发现这个 mappingRegistry 内部类,其中有很多Map 字段,我们的内容就是从这里取值的

class MappingRegistry {

		private final Map> registry = new HashMap<>();

		private final MultiValueMap pathLookup = new linkedMultiValueMap<>();

		private final Map> nameLookup = new ConcurrentHashMap<>();

		private final Map corsLookup = new ConcurrentHashMap<>();

		private final ReentrantReadWriteLock readWriteLock = new ReentrantReadWriteLock();
    
        // .... 省略其它
}

我们看到 MultiValueMap 不是我们常用的Map,其实它就是继承了Map,然后自己封装了一些方法

public interface MultiValueMap extends Map> {
	// 省略代码
}
初始化

上面也是从一个Map里面取到对应的handler,那么这个Map是如何初始化的呢?我们从RequestMappingHandlerMapping类中看到了 afterPropertiesSet()方法

	@Override
	@SuppressWarnings("deprecation")
	public void afterPropertiesSet() {

		this.config = new RequestMappingInfo.BuilderConfiguration();
		this.config.setTrailingSlashMatch(useTrailingSlashMatch());
		this.config.setContentNegotiationManager(getContentNegotiationManager());

		if (getPatternParser() != null) {
			this.config.setPatternParser(getPatternParser());
			Assert.isTrue(!this.useSuffixPatternMatch && !this.useRegisteredSuffixPatternMatch,
					"Suffix pattern matching not supported with PathPatternParser.");
		}
		else {
			this.config.setSuffixPatternMatch(useSuffixPatternMatch());
			this.config.setRegisteredSuffixPatternMatch(useRegisteredSuffixPatternMatch());
			this.config.setPathMatcher(getPathMatcher());
		}
		// 调用父类的
		super.afterPropertiesSet();
	}

上面的方法调用了父类 AbstractHandlerMethodMapping 的afterPropertiesSet()方法

	@Override
	public void afterPropertiesSet() {
		initHandlerMethods();
	}

那么afterPropertiesSet()这个方法何时被调用,我们再来看下RequestMappingHandlerMapping类的继承的关系,具体的如下图:

可以发现RequestMappingHandlerMappering的父类AbstractHandlerMethodMapping实现InitializeBean的接口

// 凡是继承该接口的类,在初始化的时候都会执行 afterPropertiesSet() 
public interface InitializingBean {
    void afterPropertiesSet() throws Exception;
}

所以就会调用到上面的afterPropertiesSet()方法的,在afterPropertiesSet()方法中调用了initHandlerMethods()方法,具体的代码如下:

	protected void initHandlerMethods() {
         //遍历所有的BeanName
		for (String beanName : getCandidateBeanNames()) {
             //如果beanName的开头不是scopedTarget.
			if (!beanName.startsWith(SCOPED_TARGET_NAME_PREFIX)) {
                  // 处理候选的bean
				processCandidateBean(beanName);
			}
		}
		handlerMethodsInitialized(getHandlerMethods());
	}

上面的代码用到了getCandidateBeanNames() 方法,该方法会获取到所有的bean对象

	protected String[] getCandidateBeanNames() {
        // 直接从Spring容器中取出所有的Object类型的对象,就代表直接取所有的bean
		return (this.detectHandlerMethodsInAncestorContexts ?
				BeanFactoryUtils.beanNamesForTypeIncludingAncestors(obtainApplicationContext(), Object.class) :
				obtainApplicationContext().getBeanNamesForType(Object.class));
	}

然后代码会遍历所有的beanName,然后只要开头不是scopedTarget.,就会执行processCandidateBean(beanName),这个方法主要是进行判断,然后处理,具体的代码如下:

	protected void processCandidateBean(String beanName) {
		Class beanType = null;
		try {
             // 获取Class类型
			beanType = obtainApplicationContext().getType(beanName);
		}
		catch (Throwable ex) {
			// An unresolvable bean type, probably from a lazy bean - let's ignore it.
			if (logger.isTraceEnabled()) {
				logger.trace("Could not resolve type for bean '" + beanName + "'", ex);
			}
		}
         //判断这个类上面是否加了@Controller注解或者是@RequestMapping注解,加了就执行下面的代码
		if (beanType != null && isHandler(beanType)) {
			detectHandlerMethods(beanName);
		}
	}

这里面会通过isHandler()方法判断类上面是否加了@Controller注解或者是@RequestMapping注解

	@Override
	protected boolean isHandler(Class beanType) {
		return (AnnotatedElementUtils.hasAnnotation(beanType, Controller.class) ||
				AnnotatedElementUtils.hasAnnotation(beanType, RequestMapping.class));
	}

找到加了@Controller或者@RequestMapping注解的类,然后执行 detectHandlerMethods(beanName) 方法

	protected void detectHandlerMethods(Object handler) {
        // 获取类型
		Class handlerType = (handler instanceof String ?
				obtainApplicationContext().getType((String) handler) : handler.getClass());

		if (handlerType != null) {
             // 获取原始类型,这个类可能是代理类
			Class userType = ClassUtils.getUserClass(handlerType);
             // 这里面通过 selectMethods() 来匹配方法
			Map methods = MethodIntrospector.selectMethods(userType,
					(MethodIntrospector.metadataLookup) method -> {
						try {
                              // 通过 getMappingForMethod() 方法创建RequestMappingInfo 添加到结果集中
							return getMappingForMethod(method, userType);
						}
						catch (Throwable ex) {
							throw new IllegalStateException("Invalid mapping on handler class [" +
									userType.getName() + "]: " + method, ex);
						}
					});
			if (logger.isTraceEnabled()) {
				logger.trace(formatMappings(userType, methods));
			}
			else if (mappingsLogger.isDebugEnabled()) {
				mappingsLogger.debug(formatMappings(userType, methods));
			}
			methods.forEach((method, mapping) -> {
				Method invocableMethod = AopUtils.selectInvocableMethod(method, userType);
				registerHandlerMethod(handler, invocableMethod, mapping);
			});
		}
	}	

上面的代码通过selectMethods()方法来查找匹配的方法,具体的代码如下:

	public static  Map selectMethods(Class targetType, final metadataLookup metadataLookup) {
         // 创建结果集
		final Map methodMap = new linkedHashMap<>();
		Set> handlerTypes = new linkedHashSet<>();
		Class specificHandlerType = null;
		//如果这个类不是jdk动态代理的类,再次判断这个类是不是cglib的类,反正获取的就是原始的类
		if (!Proxy.isProxyClass(targetType)) {
             // 获取到全部的方法
			specificHandlerType = ClassUtils.getUserClass(targetType);
             // 添加到集合
			handlerTypes.add(specificHandlerType);
		}
         //将这个类的所有的接口添加到集合中去,一般的时候都是空的
		handlerTypes.addAll(ClassUtils.getAllInterfacesForClassAsSet(targetType));
		// 遍历
		for (Class currentHandlerType : handlerTypes) {
              // 获取目标类
			final Class targetClass = (specificHandlerType != null ? specificHandlerType : currentHandlerType);
			// 查找给定的lambda表达式匹配的方法
			ReflectionUtils.doWithMethods(currentHandlerType, method -> {
                 // 获取具体的方法
				Method specificMethod = ClassUtils.getMostSpecificMethod(method, targetClass);
                 // 回调原来写好的lambda表达式中的方法
				T result = metadataLookup.inspect(specificMethod);
				if (result != null) {
                      // 查找对应的桥接方法,一般情况下不是桥接方法
					Method bridgedMethod = BridgeMethodResolver.findBridgedMethod(specificMethod);
                      // 不是桥接方法,就直接添加到methodMap中取
					if (bridgedMethod == specificMethod || metadataLookup.inspect(bridgedMethod) == null) {
						methodMap.put(specificMethod, result);
					}
				}
			}, ReflectionUtils.USER_DECLARED_METHODS);
		}

		return methodMap;
	}

上面经过一系列的操作,最终还是会调用对应的getMappingForMethod()方法,来创建RequestMappingInfo对象,最终添加到methodMap中去。具体的代码如下:

	@Override
	@Nullable
	protected RequestMappingInfo getMappingForMethod(Method method, Class handlerType) {
         // 根据方法创建RequestMapping对象,主要是解析@RequestMapping注解上的数据
		RequestMappingInfo info = createRequestMappingInfo(method);
		if (info != null) {
             //根据类型创建RequestMapping对象,主要是解析@RequestMapping注解上的数据
			RequestMappingInfo typeInfo = createRequestMappingInfo(handlerType);
			if (typeInfo != null) {
                  //将两个合起来
				info = typeInfo.combine(info);
			}
			String prefix = getPathPrefix(handlerType);
			if (prefix != null) {
				info = RequestMappingInfo.paths(prefix).options(this.config).build().combine(info);
			}
		}
		return info;
	}

getMappingForMethod()这个方法就是解析@RequestMapping注解的数据,然后根据这个@RequestMapping这个注解创建RequestMappingInfo对象返回,至此就方法和请求的地址映射起来,同时存到methodMap中去,最后返回。回到之前的 detectHandlerMethods() 方法

	protected void detectHandlerMethods(Object handler) {
        // 获取类型
		Class handlerType = (handler instanceof String ?
				obtainApplicationContext().getType((String) handler) : handler.getClass());

		if (handlerType != null) {
             // 获取原始类型,这个类可能是代理类
			Class userType = ClassUtils.getUserClass(handlerType);
             // 这里面通过 selectMethods() 来匹配方法
			Map methods = MethodIntrospector.selectMethods(userType,
					(MethodIntrospector.metadataLookup) method -> {
						try {
                              // 通过 getMappingForMethod() 方法创建RequestMappingInfo 添加到结果集中
							return getMappingForMethod(method, userType);
						}
						catch (Throwable ex) {
							throw new IllegalStateException("Invalid mapping on handler class [" +
									userType.getName() + "]: " + method, ex);
						}
					});
			if (logger.isTraceEnabled()) {
				logger.trace(formatMappings(userType, methods));
			}
			else if (mappingsLogger.isDebugEnabled()) {
				mappingsLogger.debug(formatMappings(userType, methods));
			}
             // 遍历刚才的返回结果
			methods.forEach((method, mapping) -> {
                  // 选择可调用的方法
				Method invocableMethod = AopUtils.selectInvocableMethod(method, userType);
                  // 完成映射,就是将这些的对象包装起来存到mappingRegistry
				registerHandlerMethod(handler, invocableMethod, mapping);
			});
		}
	}	

我们继续看最后一步,完成映射的代码,具体就是调用registerHandlerMethod(handler, invocableMethod, mapping);具体的代码如下:

	protected void registerHandlerMethod(Object handler, Method method, T mapping) {
		this.mappingRegistry.register(mapping, handler, method);
	}

继续调用 register() 方法注册,具体的代码如下:

	public void register(T mapping, Object handler, Method method) {
        	 // 加写锁
			this.readWriteLock.writeLock().lock();
			try {
                 // 将方法包装成HandlerMethod对象
				HandlerMethod handlerMethod = createHandlerMethod(handler, method);
                 // 校验
				validateMethodMapping(handlerMethod, mapping);
				// 获取Url
				Set directPaths = AbstractHandlerMethodMapping.this.getDirectPaths(mapping);
				for (String path : directPaths) {
                      // 将Url和mapping关联起来
					this.pathLookup.add(path, mapping);
				}

				String name = null; 
                 // 名字的生成策略,这里用默认
				if (getNamingStrategy() != null) {
					name = getNamingStrategy().getName(handlerMethod, mapping);
					addMappingName(name, handlerMethod);
				}
				// 跨域的配置
				CorsConfiguration corsConfig = initCorsConfiguration(handler, method, mapping);
				if (corsConfig != null) {
					corsConfig.validateAllowCredentials();
					this.corsLookup.put(handlerMethod, corsConfig);
				}
                 // 完成最终的映射
				this.registry.put(mapping,
						new MappingRegistration<>(mapping, handlerMethod, directPaths, name, corsConfig != null));
			}
			finally {
                 // 关锁
				this.readWriteLock.writeLock().unlock();
			}
		}

上面的代码就是根据提供的方法创建HandlerMethod对象,最后将一些变量put到registry中去。最终就就完成Url和方法的映射。

简单而说,RequestMappingHandlerMapping初始化,也是把请求地址和Handler提前存到1个Map里面,然后请求的时候遍历查找,
那么这和BeanNameUrlHandlerMapping有啥区别呢,都是存到Map里面,
BeanNameUrlHandlerMapping是需要类实现HttpRequestServlet或者Controller接口,那么这样就1个类
只能实现1个接口,同时这个类又注入到了Spring容器中,就相当于我们直接从所有的bean对象中找到对象名带“/”的,就相当于找到了这个类
RequestMappingHandlerMapping是通过@Controller和@RequestMapping注解来实现,那么一个类中可能存在多个方法,这时候我们就需要先通过
Spring容器遍历出有这两个注解的类,然后通过反射获取方法进行判断,然后注入

流程图

3、RouterFunctionMapping

有了前面的例子,笔者还是带大家看下RouterFunctionMapping这个类的继承结构,看看有没有实现什么Spring的接口,具体的图如下:

请求流程
protected Object getHandlerInternal(HttpServletRequest servletRequest) throws Exception {
    //获取请求的Url
	String lookupPath = getUrlPathHelper().getLookupPathForRequest(servletRequest);
    //设置好属性
	servletRequest.setAttribute(LOOKUP_PATH, lookupPath);
    //前面初始化routerFunction不为空
	if (this.routerFunction != null) {
        //根据消息转换器和request创建成ServerRequest
		ServerRequest request = ServerRequest.create(servletRequest, this.messageConverters);
        //添加对应的属性
		servletRequest.setAttribute(RouterFunctions.REQUEST_ATTRIBUTE, request);
        //跳转指定的路由,如果没有指定的路由就返回null
		return this.routerFunction.route(request).orElse(null);
	}
	else {
		return null;
	}
}

初始化

这个类实现了InitializingBean并且写了afterPropertiesSet()方法,Spring在创建完成Bean后会调用这个方法,这个方法的代码如下:

public void afterPropertiesSet() throws Exception {
    //如果这个roterFunction为空,就执行initRouterFunction()
	if (this.routerFunction == null) {
		initRouterFunction();
	}
    //默认的时候为空
	if (CollectionUtils.isEmpty(this.messageConverters)) {
        //初始化消息转换器
		initMessageConverters();
	}
}

这个时候如果roterFunction为空,就会执行initRouterFunction()具体的代码如下:

private void initRouterFunction() {
    //先获取容器的环境
	ApplicationContext applicationContext = obtainApplicationContext();
    //获取Bean类型是RouterFunction
	Map beans =
			(this.detectHandlerFunctionsInAncestorContexts ?
					BeanFactoryUtils.beansOfTypeIncludingAncestors(applicationContext, RouterFunction.class) :
					applicationContext.getBeansOfType(RouterFunction.class));

	List routerFunctions = new ArrayList<>(beans.values());
    //打印这些路由规则
	if (!CollectionUtils.isEmpty(routerFunctions) && logger.isInfoEnabled()) {
		routerFunctions.forEach(routerFunction -> logger.info("Mapped " + routerFunction));
	}
    //将这个routerFunction收集起来
	this.routerFunction = routerFunctions.stream()
			.reduce(RouterFunction::andOther)
			.orElse(null);
}

上面的代码就是将实现RouterFunction接口的Bean给封装到routerFunction属性中去。最后还是要调用初始化消息转换器。具体的代码如下:

private void initMessageConverters() {
	List> messageConverters = new ArrayList<>(4);
	messageConverters.add(new ByteArrayHttpMessageConverter());
	messageConverters.add(new StringHttpMessageConverter());

	try {
		messageConverters.add(new SourceHttpMessageConverter<>());
	}
	catch (Error err) {
		// Ignore when no TransformerFactory implementation is available
	}
	messageConverters.add(new AllEncompassingFormHttpMessageConverter());

	this.messageConverters = messageConverters;
}

上面添加了四个消息转换器,主要是ByteArrayHttpMessageConverter、StringHttpMessageConverter、SourceHttpMessageConverter、AllEncompassingFormHttpMessageConverter这四个消息转换器。这个时候初始化的流程完成。

流程图

ion.class) :
applicationContext.getBeansOfType(RouterFunction.class));

List routerFunctions = new ArrayList<>(beans.values());
//打印这些路由规则
if (!CollectionUtils.isEmpty(routerFunctions) && logger.isInfoEnabled()) {
	routerFunctions.forEach(routerFunction -> logger.info("Mapped " + routerFunction));
}
//将这个routerFunction收集起来
this.routerFunction = routerFunctions.stream()
		.reduce(RouterFunction::andOther)
		.orElse(null);

}

上面的代码就是将实现`RouterFunction`接口的`Bean`给封装到`routerFunction`属性中去。最后还是要调用初始化消息转换器。具体的代码如下:

```java
private void initMessageConverters() {
	List> messageConverters = new ArrayList<>(4);
	messageConverters.add(new ByteArrayHttpMessageConverter());
	messageConverters.add(new StringHttpMessageConverter());

	try {
		messageConverters.add(new SourceHttpMessageConverter<>());
	}
	catch (Error err) {
		// Ignore when no TransformerFactory implementation is available
	}
	messageConverters.add(new AllEncompassingFormHttpMessageConverter());

	this.messageConverters = messageConverters;
}

上面添加了四个消息转换器,主要是ByteArrayHttpMessageConverter、StringHttpMessageConverter、SourceHttpMessageConverter、AllEncompassingFormHttpMessageConverter这四个消息转换器。这个时候初始化的流程完成。

流程图

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/322254.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号