SpringMVC源码系列(三)HandlerMapping

1.写在前面

今天开始,笔者就带着大家来看springMVC的源码了,由于我们之前在SpringMVC源码系列(一)手动模拟SpringMVC中笔者已经模拟了springMVC的源码了。有了这篇博客的基础,读起springMVC的源码,就比较简单了。由于springMVC源码的体系比较庞大,笔者一篇博客肯定讲不完,所以要分几次来讲。

2.三大HandlerMapping

因为我们都知道SpringMVC的核心类就是DispatcherServlet,所以我们需要看下这个DispatcherServlet类的构造方法,同时看下静态的代码块,具体的代码如下:

static {
	// Load default strategy implementations from properties file.
	// This is currently strictly internal and not meant to be customized
	// by application developers.
	try {
        //加载DispatcherServlet的配置文件
		ClassPathResource resource = new ClassPathResource(DEFAULT_STRATEGIES_PATH, DispatcherServlet.class);
        //设置到对应变量中去,方便后面的使用www
		defaultStrategies = PropertiesLoaderUtils.loadProperties(resource);
	}
	catch (IOException ex) {
		throw new IllegalStateException("Could not load '" + DEFAULT_STRATEGIES_PATH + "': " + ex.getMessage());
	}
}
public DispatcherServlet(WebApplicationContext webApplicationContext) {
	super(webApplicationContext);
	setDispatchOptionsRequest(true);
}

我们发现这个类,加载了DispatcherServlet的配置文件,同时调用了父类的构造函数,然后发现这个类中没有doGetdoPost的方法,所以请求进来的时候是先调用父类FrameworkServlet中的doGet或者是doPost方法,我们可以先看下DispatchServlet类的继承关系,具体的如下图:

在这里插入图片描述

既然FrameworkServlet中有doGet方法和doPost方法,所以我们要看springMVC的源码,走来先看的就是这两个方法,因为拦截的请求,走来就会执行这两个方法中的其中的一个方法。具体的doGet和doPost方法如下:

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

可以发现这两个方法都是调用processRequest(request, response);方法,于是笔者在这个方法打了一个断点,准备跟进。具体的的代码如下:

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(request, response);,具体的代码如下:

@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<String, Object> 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);
		}

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

上面的代码也是设置一些值,然后核心的方法是doDispatch(request, response);,这个方法的代码如下:

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 = "GET".equals(method);
				if (isGet || "HEAD".equals(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);
				}
			}
		}
	}

上面做将请求的地址和方法映射的方法就是getHandler(processedRequest);,也是我们这篇博客的重点,具体的代码如下:

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

这个时候通过断点调试发现这个handlerMappings的长度不为空而是三个,那么是什么时候添加进去的呢?还记得我们之前读取的配置文件吗?你会发现这儿的handlerMappings就是配置文件中配置的。那就简单了,那么是什么时候加进去的呢?就有笔者告诉你吧,具体的调用链如下:

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()

配置文件中的数据是在这个方法中加载的,具体的代码如下:

protected void initStrategies(ApplicationContext context) {
	initMultipartResolver(context);
	initLocaleResolver(context);
	initThemeResolver(context);
	initHandlerMappings(context);
	initHandlerAdapters(context);
	initHandlerExceptionResolvers(context);
	initRequestToViewNameTranslator(context);
	initViewResolvers(context);
	initFlashMapManager(context);
}

这儿会调用initHandlerMappings(context);方法来完成往handlerMappings的map中添加三个变量,具体的代码如下:

private void initHandlerMappings(ApplicationContext context) {
	this.handlerMappings = null;

	if (this.detectAllHandlerMappings) {
		// Find all HandlerMappings in the ApplicationContext, including ancestor contexts.
        // 从spring的容器中取,如果这儿加了@EnableWebMvc注解,这儿取出来的就是三个
		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 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
	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");
		}
	}
}

上面的代码就是如果加了@EnableWebMvc注解的话,就直接能从spring容器中取出对应的三个HandlerMapping,如果没有加,就直接加载对应的配置文件中的HandlerMapping,具体的配置的内容如下:

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源码系列(九)MapperScan注解的原理(一),这儿是通过Import类,然后这个类是一个配置类,同时它的父类中添加的许多的@Bean的注解的方法,于是就将这些HandlerMapping的类扫描成BeanDefinition然后添加到spring容器中去了。我们可以看下@EnableWebMvc的注解的源码,具体的源码如下:

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
@Import(DelegatingWebMvcConfiguration.class)
public @interface EnableWebMvc {
}

上面两种方式的区别就在于:如果加了@EnableWebMvc注解的话,这三个类就直接受spring的管理了,同时这些Bean会进行一个完整的生命周期。而那种从配置文件中加载的,就没有经历一个完整的生命周期。

上篇博客遗留了一个问题,我们先看如下springMVC启动调用的代码:

package com.ys.config;

import org.springframework.web.WebApplicationInitializer;
import org.springframework.web.context.support.AnnotationConfigWebApplicationContext;
import org.springframework.web.servlet.DispatcherServlet;

import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.ServletRegistration;

public class MyWebApplicationInitializer implements WebApplicationInitializer {
    @Override
    public void onStartup(ServletContext servletContext) throws ServletException {
        System.out.println("Tomcat启动的时候调用了");
        //加载spring web的环境
        AnnotationConfigWebApplicationContext context = new AnnotationConfigWebApplicationContext();
        context.register(AppConfig.class);

        // 注册DispatcherServlet
        DispatcherServlet servlet = new DispatcherServlet(context);
        ServletRegistration.Dynamic registration = servletContext.addServlet("app", servlet);
        registration.setLoadOnStartup(1);
        registration.addMapping("*.do");
    }
}

你会发现这儿没有调用refresh()方法,那么spring的容器在什么地方初始化的呢?这个时候就需要看下对应的调用链了

org.springframework.web.servlet.HttpServletBean#init
-->org.springframework.web.servlet.FrameworkServlet#initServletBean
-->org.springframework.web.servlet.FrameworkServlet#initWebApplicationContext
-->org.springframework.web.servlet.FrameworkServlet#configureAndRefreshWebApplicationContext
-->org.springframework.context.support.AbstractApplicationContext#refresh

servlet的生命周期大家都是知道的,所有这儿调用的FrameworkServlet的父类的HttpServletBeaninit方法,然后经过一系列的处理调用到refresh方法,这样spring容器就初始化完成了。

3.getHandler方法

上小节,笔者已经讲到怎么调用到getHandler方法,同时不管有没有加@EnableWebMvc的注解,这个时候都会有三个HandlerMapping,本节笔者主要讲getHandler方法,具体的代码如下:

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

可以发现调用的就是这三大HandlerMappinggetHandler方法,如果有一个HandlerMapping中的getHandler的方法返回值不是null,就直接返回,我们这儿按照对应的顺序,分别看看这个三个HandlerMapping中的getHandler方法中干了什么。

3.1BeanNameUrlHandlerMapping

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

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

	HandlerExecutionChain executionChain = getHandlerExecutionChain(handler, request);

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

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

	return executionChain;
}

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

protected Object getHandlerInternal(HttpServletRequest request) throws Exception {
	String lookupPath = getUrlPathHelper().getLookupPathForRequest(request);
	request.setAttribute(LOOKUP_PATH, lookupPath);
    //调用下面的方法查找Handler
	Object 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,那么具体的代码如下:

protected Object lookupHandler(String urlPath, HttpServletRequest request) throws Exception {
	// Direct match?
    //直接匹配
	Object handler = this.handlerMap.get(urlPath);
	if (handler != null) {
		// Bean name or resolved handler?
		if (handler instanceof String) {
			String handlerName = (String) handler;
			handler = obtainApplicationContext().getBean(handlerName);
		}
		validateHandler(handler, request);
		return buildPathExposingHandler(handler, urlPath, urlPath, null);
	}

	// Pattern match?
    //正则匹配
    //省略一部分代码
		
	// No handler found...
    // 没有找到对应的Handler
	return null;
}

这儿笔者只打算讲直接匹配,上面handlerMap在什么时候有值呢?或者是什么时候往其中put值呢?于是笔者使用idea的工具进行查找,终于找到如下的代码,然后给对应的地方打上断点,然后进行调试,具体的如下:

protected void registerHandler(String urlPath, Object handler) throws BeansException, IllegalStateException {
	Assert.notNull(urlPath, "URL path must not be null");
	Assert.notNull(handler, "Handler object must not be null");
	Object resolvedHandler = handler;

	// Eagerly resolve handler if referencing singleton via name.
	if (!this.lazyInitHandlers && handler instanceof String) {
		String handlerName = (String) handler;
		ApplicationContext applicationContext = obtainApplicationContext();
		if (applicationContext.isSingleton(handlerName)) {
			resolvedHandler = applicationContext.getBean(handlerName);
		}
	}

	Object mappedHandler = this.handlerMap.get(urlPath);
	if (mappedHandler != null) {
		if (mappedHandler != resolvedHandler) {
			throw new IllegalStateException(
					"Cannot map " + getHandlerDescription(handler) + " to URL path [" + urlPath +
					"]: There is already " + getHandlerDescription(mappedHandler) + " mapped.");
		}
	}
	else {
		if (urlPath.equals("/")) {
			if (logger.isTraceEnabled()) {
				logger.trace("Root mapping to " + getHandlerDescription(handler));
			}
			setRootHandler(resolvedHandler);
		}
		else if (urlPath.equals("/*")) {
			if (logger.isTraceEnabled()) {
				logger.trace("Default mapping to " + getHandlerDescription(handler));
			}
			setDefaultHandler(resolvedHandler);
		}
		else {
			this.handlerMap.put(urlPath, resolvedHandler);
			if (logger.isTraceEnabled()) {
				logger.trace("Mapped [" + urlPath + "] onto " + getHandlerDescription(handler));
			}
		}
	}
}

那么这个方法在什么时候调用的呢?我们可以具体看下调用栈和类的继承图,具体的如下:

在这里插入图片描述
在这里插入图片描述

发现这个三个HandlerMapping都是实现ApplicationContextAware的接口,所有会调用其中的setApplicationContext()方法,在这个方法中调用了initApplicationContext()方法,这个方法会一直重写下去,所以调用的是子类的方法,由于RouterFunctionMappingRequestMappingHandlerMapping这两个类没有重写initApplicationContext()方法,所以调用的父类AbstractHandlerMapping中的initApplicationContext()方法,BeanNameUrlHandlerMapping中也没有重写initApplicationContext()方法,但是调用了父类AbstractDetectingUrlHandlerMapping中的initApplicationContext()方法,具体的内容如下:

@Override
public void initApplicationContext() throws ApplicationContextException {
    //调用父类AbstractHandlerMapping中的initApplicationContext()方法
	super.initApplicationContext();
    //添加匹配的Bean到handlerMap
	detectHandlers();
}

这个时候调用的父类initApplicationContext()方法和另外两个HandlerMapping的调用的父类的内容是一样的。具体的内容如下:主要的内容就是初始化拦截器

@Override
protected void initApplicationContext() throws BeansException {
	extendInterceptors(this.interceptors);
	detectMappedInterceptors(this.adaptedInterceptors);
	initInterceptors();
}

既然已经搞清楚了怎么调用registerHandler(),那么我们继续开始分析,先从detectHandlers()方法说起,具体的代码如下:

protected void detectHandlers() throws BeansException {
    //获取容器环境
	ApplicationContext applicationContext = obtainApplicationContext();
    //根据类型获取所有的beanName
   	//detectHandlersInAncestorContexts 如果为true,就要从父容器中获取,这儿默认是false
	String[] beanNames = (this.detectHandlersInAncestorContexts ?
			BeanFactoryUtils.beanNamesForTypeIncludingAncestors(applicationContext, Object.class) :
			applicationContext.getBeanNamesForType(Object.class));

	// Take any bean name that we can determine URLs for.
	for (String beanName : beanNames) {
        //调用对应的方法进行匹配筛选
		String[] urls = determineUrlsForHandler(beanName);
		if (!ObjectUtils.isEmpty(urls)) {
			// URL paths found: Let's consider it a handler.
            //注册对应的Url,简单的说就是添加到handlerMap
			registerHandler(urls, beanName);
		}
	}

	if ((logger.isDebugEnabled() && !getHandlerMap().isEmpty()) || logger.isTraceEnabled()) {
		logger.debug("Detected " + getHandlerMap().size() + " mappings in " + formatMappingName());
	}
}

上面的方法走来获取所有的beanName,然后遍历调用determineUrlsForHandler方法进行筛选,具体的代码如下:

//如果beanName是以/开头或者这个beanName的别名是以/开头的都添加到list中去,然后转成数组返回出去
protected String[] determineUrlsForHandler(String beanName) {
	List<String> urls = new ArrayList<>();
	if (beanName.startsWith("/")) {
		urls.add(beanName);
	}
	String[] aliases = obtainApplicationContext().getAliases(beanName);
	for (String alias : aliases) {
		if (alias.startsWith("/")) {
			urls.add(alias);
		}
	}
	return StringUtils.toStringArray(urls);
}

上面的代码就是如果beanName是以/开头或者这个beanName的别名是以/开头的都添加到list中去,然后转成数组返回出去,接着看registerHandler(urls, beanName);方法,具体的代码如下:

protected void registerHandler(String[] urlPaths, String beanName) throws BeansException, IllegalStateException {
	Assert.notNull(urlPaths, "URL path array must not be null");
	for (String urlPath : urlPaths) {
		registerHandler(urlPath, beanName);
	}
}

先遍历这个整的集合,然后对每一个urlPath进行注册,我们再看registerHandler(urlPath, beanName);方法,具体的代码如下:

protected void registerHandler(String urlPath, Object handler) throws BeansException, IllegalStateException {
	Assert.notNull(urlPath, "URL path must not be null");
	Assert.notNull(handler, "Handler object must not be null");
	Object resolvedHandler = handler;

	// Eagerly resolve handler if referencing singleton via name.
    // 这个判断正常的情况下是成立的
	if (!this.lazyInitHandlers && handler instanceof String) {
        //获取对应的handler,就是对应的beanName
		String handlerName = (String) handler;
        //获取容器
		ApplicationContext applicationContext = obtainApplicationContext();
        //判断这个bean是否是单例的
		if (applicationContext.isSingleton(handlerName)) {
            //获取这个bean,如果没有创建了,就直接创建,如果有创建,直接从容器中获取
			resolvedHandler = applicationContext.getBean(handlerName);
		}
	}

    //判断这个map中有没有这个Url,如果有并且这两个值不同,就直接报错
	Object mappedHandler = this.handlerMap.get(urlPath);
	if (mappedHandler != null) {
		if (mappedHandler != resolvedHandler) {
			throw new IllegalStateException(
					"Cannot map " + getHandlerDescription(handler) + " to URL path [" + urlPath +
					"]: There is already " + getHandlerDescription(mappedHandler) + " mapped.");
		}
	}
	else {
        //如果这个Url等于/直接设置到为RootHandler
		if (urlPath.equals("/")) {
			if (logger.isTraceEnabled()) {
				logger.trace("Root mapping to " + getHandlerDescription(handler));
			}
			setRootHandler(resolvedHandler);
		}
        //如果这个Url等于/*直接设置到为DefaultHandler
		else if (urlPath.equals("/*")) {
			if (logger.isTraceEnabled()) {
				logger.trace("Default mapping to " + getHandlerDescription(handler));
			}
			setDefaultHandler(resolvedHandler);
		}
        //如果都不是的话,直接put到这个handlerMap中去。
		else {
			this.handlerMap.put(urlPath, resolvedHandler);
			if (logger.isTraceEnabled()) {
				logger.trace("Mapped [" + urlPath + "] onto " + getHandlerDescription(handler));
			}
		}
	}
}

上面的代码就是进行一些判断,如果这个Url为"/",那么我们将这个设置为RootHandler,如果这个Url是"/*",那么我们将这个设置为DefaultHandler,最后如果都不是的话,我们直接put到handlerMap中去。我们可以写一个类测试一下这种情况,具体的代码如下:

package com.ys.controller;


import org.springframework.stereotype.Component;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.mvc.AbstractController;
import org.springframework.web.servlet.mvc.Controller;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

@Component("/beanName.do")
public class BeanNameController implements Controller {

    @Override
    public ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response) throws Exception {
        return new ModelAndView("index");
    }
}

然后我们继续debug,具体的如下图:
在这里插入图片描述

可以看到我们这个handlerMap中就是我们刚才添加的Bean。我们继续看原来的代码:

protected Object lookupHandler(String urlPath, HttpServletRequest request) throws Exception {
	// Direct match?
    //直接匹配 找到我们刚才的beanName.do
	Object handler = this.handlerMap.get(urlPath);
    //如果没有找到的话,直接返回null
	if (handler != null) {
		// Bean name or resolved handler?
        // 这儿表示这个handler还没有创建
		if (handler instanceof String) {
			String handlerName = (String) handler;
            //从容器中获取这个Handler
			handler = obtainApplicationContext().getBean(handlerName);
		}
        //校验这个Handler,默认的是空的
		validateHandler(handler, request);
		return buildPathExposingHandler(handler, urlPath, urlPath, null);
	}

	// Pattern match?
    //正则匹配
    //省略一部分代码
		
	// No handler found...
    // 没有找到对应的Handler
	return null;
}

然后调用buildPathExposingHandler(handler, urlPath, urlPath, null);方法,具体的代码如下:

protected Object buildPathExposingHandler(Object rawHandler, String bestMatchingPattern,
		String pathWithinMapping, @Nullable Map<String, String> uriTemplateVariables) {

	HandlerExecutionChain chain = new HandlerExecutionChain(rawHandler);
	chain.addInterceptor(new PathExposingHandlerInterceptor(bestMatchingPattern, pathWithinMapping));
	if (!CollectionUtils.isEmpty(uriTemplateVariables)) {
		chain.addInterceptor(new UriTemplateVariablesHandlerInterceptor(uriTemplateVariables));
	}
	return chain;
}

于是这儿就封装好了HandlerExecutionChain对象然后返回,我们继续看原来的代码。

protected Object getHandlerInternal(HttpServletRequest request) throws Exception {
    //获取请求的Url,这儿是/beanName.do
	String lookupPath = getUrlPathHelper().getLookupPathForRequest(request);
    //往request中设置一个属性值
	request.setAttribute(LOOKUP_PATH, lookupPath);
    //这个就是调用上面的获取Handler的方法,这儿是会获取到的
	Object 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();
		}
        //如果还是空,就获取默认的Handler,也就是前面封装的/*
		if (rawHandler == null) {
			rawHandler = getDefaultHandler();
		}
        //如果上面获取的值不为空,这儿就走一遍验证,然后封装成HandlerExecutionChain对象返回
		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;
}

这儿就将获取的handler返回,然后我们再看原来的代码,具体的如下:

public final HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
    //获取handler
	Object handler = getHandlerInternal(request);
    //如果handler为空直接获取默认的handler
	if (handler == null) {
		handler = getDefaultHandler();
	}
    //如果还是空就直接返回null
	if (handler == null) {
		return null;
	}
	// Bean name or resolved handler?
    // 如果handler的类型是String,证明没有创建,就getBean创建一遍
	if (handler instanceof String) {
		String handlerName = (String) handler;
		handler = obtainApplicationContext().getBean(handlerName);
	}

    //从handler中获取HandlerExecutionChain对象
	HandlerExecutionChain executionChain = getHandlerExecutionChain(handler, request);

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

    //判断是不是跨域的请求,如果是重新封装一遍
	if (hasCorsConfigurationSource(handler) || CorsUtils.isPreFlightRequest(request)) {
		CorsConfiguration config = (this.corsConfigurationSource != null ? this.corsConfigurationSource.getCorsConfiguration(request) : null);
		CorsConfiguration handlerConfig = getCorsConfiguration(handler, request);
		config = (config != null ? config.combine(handlerConfig) : handlerConfig);
		executionChain = getCorsHandlerExecutionChain(request, executionChain, config);
	}

    //返回封装好的HandlerExecutionChain对象
	return executionChain;
}

到此整个BeanNameUrlHandlerMapping的处理的流程就讲完了,笔者来做个总结,具体的如下图:

在这里插入图片描述

笔者这儿把第一个BeanNameUrlHandlerMapping的图就画完了,后面还有两个Handler的方法,我们继续往下看。

3.2RequestMappingHandlerMapping

前面第一个HandlerMapping的没有找到对应的Handler,这个时候就需要调用下一个HandlerMapping来查找Handler,笔者写出如下的测试的代码,具体的代码如下:

package com.ys.controller;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;

import java.util.HashMap;
import java.util.Map;

@Controller
@RequestMapping("/test")
public class TestController {

    @RequestMapping("/index.do")
    public String index(){
        return "index";
    }
}

这个时候我们在浏览器中访问这个地址,然后笔者继续debug看RequestMappingHandlerMapping中的getHandler方法。调用的还是父类中的getHandler()方法,具体的代码如下:

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 = obtainApplicationContext().getBean(handlerName);
	}

	HandlerExecutionChain executionChain = getHandlerExecutionChain(handler, request);

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

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

	return executionChain;
}

和原来的代码一样,笔者在这就不赘述,直接看getHandlerInternal(request);方法,具体的代码如下:

protected HandlerMethod getHandlerInternal(HttpServletRequest request) throws Exception {
	request.removeAttribute(PRODUCIBLE_MEDIA_TYPES_ATTRIBUTE);
	try {
		return super.getHandlerInternal(request);
	}
	finally {
		ProducesRequestCondition.clearMediaTypesAttribute(request);
	}
}

可以发现代码走来移除了某个属性,同时调用了父类的getHandlerInternal(request);方法,具体的代码如下:

protected HandlerMethod getHandlerInternal(HttpServletRequest request) throws Exception {
    //获取请求的Url
	String lookupPath = getUrlPathHelper().getLookupPathForRequest(request);
    //设置属性
	request.setAttribute(LOOKUP_PATH, lookupPath);
    //加锁
	this.mappingRegistry.acquireReadLock();
	try {
		HandlerMethod handlerMethod = lookupHandlerMethod(lookupPath, request);
		return (handlerMethod != null ? handlerMethod.createWithResolvedBean() : null);
	}
	finally {
        //解锁
		this.mappingRegistry.releaseReadLock();
	}
}

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

protected HandlerMethod lookupHandlerMethod(String lookupPath, HttpServletRequest request) throws Exception {
	List<Match> matches = new ArrayList<>();
    //这个时候会从mappingRegistery中取出这个Url对应的handler,你会发现这个handler的值不为空。所以什么时候添加进去的呢?
	List<T> directPathMatches = this.mappingRegistry.getMappingsByUrl(lookupPath);
	//省略一部分代码...
}

上面的代码是根据Url来获取对应的Handler,而到这个时候的mappingRegistery的值不为空,那么什么时候往这个值添加了值,通过查找的方式,我们找到了如下的代码。具体的代码如下:

public void afterPropertiesSet() {
	initHandlerMethods();
}

我们再来看下RequestMappingHandlerMapping类的继承的关系,具体的如下图:

在这里插入图片描述

可以发现RequestMappingHandlerMappering的父类AbstractHandlerMethodMapping实现InitializeBean的接口,所以就会调用到上面的afterPropertiesSet()方法的,在afterPropertiesSet()方法中调用了initHandlerMethods()方法,具体的代码如下:

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

上面的代码会遍历所有的beanName,然后只要开头不是scopedTarget.,就会执行processCandidateBean(beanName);具体的代码如下:

protected void processCandidateBean(String beanName) {
	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.isTraceEnabled()) {
			logger.trace("Could not resolve type for bean '" + beanName + "'", ex);
		}
	}
    //判断这个类上面是否加了@Controller注解或者是@RequestMapping注解,加了就执行下面的代码
	if (beanType != null && isHandler(beanType)) {
		detectHandlerMethods(beanName);
	}
}

如果这个类加了@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);
        //查找对应的方法
		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.isTraceEnabled()) {
			logger.trace(formatMappings(userType, methods));
		}
		methods.forEach((method, mapping) -> {
			Method invocableMethod = AopUtils.selectInvocableMethod(method, userType);
			registerHandlerMethod(handler, invocableMethod, mapping);
		});
	}
}

上面的代码就是调用selectMethods()方法来查找匹配的方法,具体的代码如下:

public static <T> Map<Method, T> selectMethods(Class<?> targetType, final MetadataLookup<T> metadataLookup) {
	final Map<Method, T> methodMap = new LinkedHashMap<>();
	Set<Class<?>> 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中去。具体的代码如下:

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

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

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

	if (handlerType != null) {
        //获取原始类型,这个类可能是代理类
		Class<?> userType = ClassUtils.getUserClass(handlerType);
        //查找对应的方法,最后将方法和请求的Url对应起来,但是这个时候还没有完成映射
		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.isTraceEnabled()) {
			logger.trace(formatMappings(userType, methods));
		}
        //遍历刚才的那个map
		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);
}

继续调用对应的方法,具体的代码如下:

public void register(T mapping, Object handler, Method method) {
	// Assert that the handler method is not a suspending one.
    // 这段代码是有关kotlin,可以直接跳过
	if (KotlinDetector.isKotlinType(method.getDeclaringClass())) {
		Class<?>[] parameterTypes = method.getParameterTypes();
		if ((parameterTypes.length > 0) && "kotlin.coroutines.Continuation".equals(parameterTypes[parameterTypes.length - 1].getName())) {
			throw new IllegalStateException("Unsupported suspending handler method detected: " + method);
		}
	}
    //加锁
	this.readWriteLock.writeLock().lock();
	try {
        //将方法包装成HandlerMethod对象
		HandlerMethod handlerMethod = createHandlerMethod(handler, method);
        //校验
		validateMethodMapping(handlerMethod, mapping);
        //将请求的地址和方法映射起来存储到mappingLookup中去
		this.mappingLookup.put(mapping, handlerMethod);

        //获取Url
		List<String> directUrls = getDirectUrls(mapping);
		for (String url : directUrls) {
            //将Url和mapping关联起来
			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<>(mapping, handlerMethod, directUrls, name));
	}
	finally {
		this.readWriteLock.writeLock().unlock();
	}
}

上面的代码就是根据提供的方法创建HandlerMethod对象,最后将一些变量put到registry中去。最终就就完成Url和方法的映射,至此整个mappingRegistry变量就有值了。我们继续看原来的代码,具体的代码如下:

protected HandlerMethod lookupHandlerMethod(String lookupPath, HttpServletRequest request) throws Exception {
	List<Match> matches = new ArrayList<>();
    //先根据Url找到对应的mapping
	List<T> directPathMatches = this.mappingRegistry.getMappingsByUrl(lookupPath);
	if (directPathMatches != null) {
        //根据匹配到mapping找到对应的HandlerMethod
		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()) {
        //取出第一个匹配的
		Match bestMatch = matches.get(0);
        //如果长度大于1,就进行排序,取出一个最满足条件的HandlerMethod
		if (matches.size() > 1) {
			Comparator<Match> 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)) {
				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();
				String uri = request.getRequestURI();
				throw new IllegalStateException(
							"Ambiguous handler methods mapped for '" + uri + "': {" + m1 + ", " + m2 + "}");
			}
		}
        //设置好属性
		request.setAttribute(BEST_MATCHING_HANDLER_ATTRIBUTE, bestMatch.handlerMethod);
        //又设置了一个属性
		handleMatch(bestMatch.mapping, lookupPath, request);
        //将这个HandlerMethod返回
		return bestMatch.handlerMethod;
	}
	else {
        //重新再查找一遍
		return handleNoMatch(this.mappingRegistry.getMappings().keySet(), lookupPath, request);
	}
}

上面的代码就是从mappingRegistry中取出Url对应的mapping,然后根据这个mapping查找对应的HandlerMethod返回,如果查不到,就直接返回null,最后将这个HandlerMethod返回,具体的代码如下:

protected HandlerMethod getHandlerInternal(HttpServletRequest request) throws Exception {
	String lookupPath = getUrlPathHelper().getLookupPathForRequest(request);
	request.setAttribute(LOOKUP_PATH, lookupPath);
	this.mappingRegistry.acquireReadLock();
	try {
		HandlerMethod handlerMethod = lookupHandlerMethod(lookupPath, request);
        //如果不为空,就调用createWithResolvedBean方法,这个方法就是判断这个要调用的方法所在的类是否已经创建,如果没有创建,就需要创建。
		return (handlerMethod != null ? handlerMethod.createWithResolvedBean() : null);
	}
	finally {
		this.mappingRegistry.releaseReadLock();
	}
}

至此整个RequestMappingHandlerMapping查找Handler的方法就讲完了。因为后面调用的方法和BeanNameUrlHandlerMapping调用的方法是一样的,调用的是这两个类的父类中的方法,所以这儿笔者就不概述。笔者还是像上面的BeanNameUrlHandlerMapping的一样,进行总结一下,具体的如下图:

在这里插入图片描述

3.3RouterFunctionMapping

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

在这里插入图片描述

这个类实现了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<String, RouterFunction> beans =
			(this.detectHandlerFunctionsInAncestorContexts ?
					BeanFactoryUtils.beansOfTypeIncludingAncestors(applicationContext, RouterFunction.class) :
					applicationContext.getBeansOfType(RouterFunction.class));

	List<RouterFunction> 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<HttpMessageConverter<?>> 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;
}

上面添加了四个消息转换器,主要是ByteArrayHttpMessageConverterStringHttpMessageConverterSourceHttpMessageConverterAllEncompassingFormHttpMessageConverter这四个消息转换器。这个时候初始化的流程完成。这个时候继续看找Handler的流程,具体的代码如下:

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

上面的代码就是从routerFunction中找到指定的路由,如果没有指定的路由,就直接返回null。至此所有Spring自带的HandlerMapping已经介绍完了。最后再总结一波,具体的如下图:

在这里插入图片描述

4.写在最后

Spring提供的三大HandlerMapping,然后调用HandlerMapping中的getHandler方法,获取对应的Handler,如果有一个HandlerMapping中能找到一个Handler就直接返回,如果没有找到,就继续调用下一个HandlerMappinggetHandler方法,直至找到对应Handler,如果没有找到就直接返回null,同时这几个HandlerMapping都利用了Spring的扩展点,往对应的HandlerMapping初始化了一些东西。后面的博客会介绍HandlerAdapter

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值