死磕Spring系列:MVC源码分析

14 篇文章 5 订阅

为了方便利用使用的思维进行理解,我们可以先从程序调用入口出发,先对调用层面进行说明,再对程序框架本身处理进行深入。简而言之就是先说明一个请求进入mvc逻辑需要经过哪些处理步骤(1~7节),再说明处理步骤中mvc是怎样提供参数支持的(8~10节)。

1. Servlet

Servlet是一个处理http请求的标准类,它处于javax.servlet.http包,属于java标准库的扩展部分。其中主要有initservicedestroy方法,作用分别为初始化时调用,接收到请求调用,销毁时调用。
Servlet是一个顶层接口,其中Spring传入到Tomcat容器的ApplicationFilterChain类中的Servlet参数是DispatcherServlet实例。


2. ApplicationFilterChain

在客户端发送请求时,ApplicationFilterChain过滤器链会调用Servlet类中的service方法。

private void internalDoFilter(ServletRequest request,
                              ServletResponse response)
    throws IOException, ServletException {
	......
    servlet.service(request, response);
    ......
}

此处的servlet是DispatcherServlet的实例,至于是如何创建的,需要去阅读相关源码,此处不做深入讨论。
由于DispatcherServlet中未重写service(ServletRequest req, ServletResponse res)方法,所以此处调用的是HttpServlet类中的方法。

3. HttpServlet

@Override
public void service(ServletRequest req, ServletResponse res)
    throws ServletException, IOException {

    HttpServletRequest  request;
    HttpServletResponse response;

    try {
        request = (HttpServletRequest) req;
        response = (HttpServletResponse) res;
    } catch (ClassCastException e) {
        throw new ServletException(lStrings.getString("http.non_http"));
    }
    service(request, response);
}

在Spring项目中,FrameworkServlet作为HttpServlet的子类,DispatcherServlet的父类,重写了service(HttpServletRequest request, HttpServletResponse response)方法,所以此处service(request, response)调用的是FrameworkServlet中的方法。

4. FrameworkServlet

/**
 * Override the parent class implementation in order to intercept PATCH requests.
 */
@Override
protected void service(HttpServletRequest request, HttpServletResponse response)
		throws ServletException, IOException {

	HttpMethod httpMethod = HttpMethod.resolve(request.getMethod());
	if (httpMethod == HttpMethod.PATCH || httpMethod == null) {
		processRequest(request, response);
	}
	else {
		super.service(request, response);
	}
}

如果请求的method是patch类型或者没有请求的method,则直接进行处理,否则调用父类(HttpServlet)中的service方法,注意此处的参数类型为HttpServletRequestHttpServletResponse
HttpServlet中的service方法:

protected void service(HttpServletRequest req, HttpServletResponse resp)
    throws ServletException, IOException {

    String method = req.getMethod();

    if (method.equals(METHOD_GET)) {
        long lastModified = getLastModified(req);
        if (lastModified == -1) {
            // servlet doesn't support if-modified-since, no reason
            // to go through further expensive logic
            doGet(req, resp);
        } else {
            long ifModifiedSince;
            try {
                ifModifiedSince = req.getDateHeader(HEADER_IFMODSINCE);
            } catch (IllegalArgumentException iae) {
                // Invalid date header - proceed as if none was set
                ifModifiedSince = -1;
            }
            if (ifModifiedSince < (lastModified / 1000 * 1000)) {
                // If the servlet mod time is later, call doGet()
                // Round down to the nearest second for a proper compare
                // A ifModifiedSince of -1 will always be less
                maybeSetLastModified(resp, lastModified);
                doGet(req, resp);
            } else {
                resp.setStatus(HttpServletResponse.SC_NOT_MODIFIED);
            }
        }

    } else if (method.equals(METHOD_HEAD)) {
        long lastModified = getLastModified(req);
        maybeSetLastModified(resp, lastModified);
        doHead(req, resp);

    } else if (method.equals(METHOD_POST)) {
        doPost(req, resp);

    } else if (method.equals(METHOD_PUT)) {
        doPut(req, resp);

    } else if (method.equals(METHOD_DELETE)) {
        doDelete(req, resp);

    } else if (method.equals(METHOD_OPTIONS)) {
        doOptions(req,resp);

    } else if (method.equals(METHOD_TRACE)) {
        doTrace(req,resp);

    } else {
        //
        // Note that this means NO servlet supports whatever
        // method was requested, anywhere on this server.
        //

        String errMsg = lStrings.getString("http.method_not_implemented");
        Object[] errArgs = new Object[1];
        errArgs[0] = method;
        errMsg = MessageFormat.format(errMsg, errArgs);

        resp.sendError(HttpServletResponse.SC_NOT_IMPLEMENTED, errMsg);
    }
}

主要功能就是根据method(get/put/post…)调用子类的doGet、doPost、doPut等等方法,这些方法都在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);
}

其中大部分调用的都是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);
	}
}

其中主要功能为:
1)向ThreadLocal中存入LocaleContextRequestAttributes
2)执行doService方法。
3)将ThreadLocal中的LocaleContext与RequestAttributes置空。
4)标记请求处理已完成。
5)发布请求处理事件。

4.1 扩展

上述逻辑处理后,我们可以解锁以下几个隐藏操作:
1)任意代码段即可获取LocaleContext与RequestAttributes。

// 获取语言环境
LocaleContext localeContext = LocaleContextHolder.getLocaleContext();

// 获取HttpServletRequest
RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
HttpServletRequest request = ((ServletRequestAttributes) requestAttributes).getRequest();

2)通过事件,获取到当前请求的url、sessionId、method等等信息。

@Component
public class MyEventListener implements ApplicationListener<ServletRequestHandledEvent> {

    @Override
    public void onApplicationEvent(ServletRequestHandledEvent event) {
        System.out.println(event.getRequestUrl());
    }
}

processRequest方法调用的doService方法,是在DispatcherServlet类中重写的一个方法。

5. DispatcherServlet

5.1 doService

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

	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);
			}
		}
		ServletRequestPathUtils.setParsedRequestPath(previousRequestPath, request);
	}
}

其中主要就是给request设置参数与调用doDispatch方法。

5.2 doDispatch

protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
	HttpServletRequest processedRequest = request;cc
	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);
			}
		}
	}
}

以上代码主要功能:
1)检查是否是文件请求。
2)根据request查找到对应的HandlerMethodHandlerInterceptor,并封装为一个HandlerExecutionChain
3)根据HandlerAdaptersupports方法找到支持当前HandlerMethod的HandlerAdapter。
4)调用HandlerExecutionChain拥有的拦截器的preHandle方法,如果返回false则直接触发拦截器的afterCompletion方法,并终止执行。
5)对请求进行supportedMethodsrequireSession检查,使用反射执行方法。
6)执行拦截器的postHandle方法。

这里有一个需要着重需要注意的地方,那就是MVC是怎样根据request找到对应的执行方法HandlerMethod的呢?
我们接着查看mappedHandler = getHandler(processedRequest);这行代码。

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

此处逻辑非常简单,就是循环handlerMappings对象,调用其getHandler方法,如果得到的结果不为null,那么直接返回这个结果。
这里的handlerMappings对象有5个值:



具体是从哪里设置的,等会儿我们再细讲。这几个类本身都没有实现getHandler方法,都是调用其共同父类AbstractHandlerMapping类中的实现。

6. AbstractHandlerMapping

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

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

通过以上代码,我们可以得知其主要功能:
1)根据request查找到对应的handler,如果对应的handler为null,则设置默认的handler,如果返回的是一个String,则在beanFactory中查找以此为beanName的bean作为handler。
2)确保缓存lookupPath。
3)将handler与拦截器封装为一个HandlerExecutionChain。
4)跨域配置。

继续进入getHandlerInternal方法。

6.2 getHandlerInternal

AbstractHandlerMapping的子类RequestMappingInfoHandlerMapping中有实现:

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

这里调用了父类AbstractHandlerMethodMapping中的实现。

7. AbstractHandlerMethodMapping

7.1 getHandlerInternal

@Override
protected HandlerMethod getHandlerInternal(HttpServletRequest request) throws Exception {
	String lookupPath = initLookupPath(request);
	this.mappingRegistry.acquireReadLock();
	try {
		HandlerMethod handlerMethod = lookupHandlerMethod(lookupPath, request);
		return (handlerMethod != null ? handlerMethod.createWithResolvedBean() : null);
	}
	finally {
		this.mappingRegistry.releaseReadLock();
	}
}

1)获取去除了content-path的请求路径。
2)mappingRegistry获取读锁,查找HandlerMethod,mappingRegistry释放读锁。

继续深入lookupHandlerMethod查找方法。

7.2 lookupHandlerMethod

protected HandlerMethod lookupHandlerMethod(String lookupPath, HttpServletRequest request) throws Exception {
	List<Match> matches = new ArrayList<>();
	List<T> 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<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)) {
				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);
	}
}

其中主要执行逻辑为:
1)从MappingRegistrypathLookup中匹配请求路径,得到对应的RequestMappingInfo,再根据RequestMappingInfo自身的methodsConditionparamsConditionheadersConditionconsumesConditionproducesConditionpathPatternsConditionpatternsConditioncustomConditionHolder进行一系列的匹配,只要全都匹配成功,那就将根据此RequestMappingInfo为key,在MappingRegistry类中的类型为Map<T, MappingRegistration>的registry变量中找到对应的类型为MappingRegistration的value。(MappingRegistration中保存了HandlerMethod)。
2)如果匹配到多个结果,则按照MatchComparator排序取第一个为bestMatch。返回bestMatch持有的HandlerMethod

至此我们成功的找到了request对应的HandlerMethod
但是,这些对象的变量是什么时候给设置进去的呢?它们的初始化逻辑是怎样的呢?
众所周知,第一次调用Servlet时会执行其init方法,DispatcherServlet也是个Servlet,那么来看看其初始化流程。

8. DispatcherServlet

8.1 init

在初始执行Servlet时,Tomcat容器会调用Servlet类的init方法,源码在org.apache.catalina.core.StandardWrapper中的initServlet方法。

    private synchronized void initServlet(Servlet servlet)
            throws ServletException {
        ......
        servlet.init(facade);
        ......
    }

此处Servlet为DispatcherServlet,是Spring容器启动时传入到Tomcat容器的参数。
而DispatcherServlet并未重写init方法,所以一路向上,调用父类GenericServlet的init方法:

public void init(ServletConfig config) throws ServletException {
    this.config = config;
    this.init();
}

这里的init被GenericServlet的子类,DispatcherServlet的父类HttpServletBean重写:

public final void init() throws ServletException {
	......
	initServletBean();
}

这里的initServletBean被HttpServletBean的的子类,DispatcherServlet的父类FrameworkServlet重写:

protected final void initServletBean() throws ServletException {
    ......
	this.webApplicationContext = initWebApplicationContext();
}
protected WebApplicationContext initWebApplicationContext() {
    ......
	synchronized (this.onRefreshMonitor) {
		onRefresh(wac);
	}
    ......
	return wac;
}

这里的onRefresh方法就是调用的被子类DispatcherServlet实现的方法。

protected void onRefresh(ApplicationContext context) {
	initStrategies(context);
}

调用栈如下:

这里我们直接分析initStrategies方法。

8.2 initStrategies

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

可以看到,Spring写的代码挺漂亮的,从方法名上就能清晰的知道其作用。
这个方法主要做这几件事:
1)初始化文件解析器,可用于判断文件参数,如max-file-size、max-request-size等的判断。
2)初始化区域解析器,其功能为设置消息国际化。
3)初始化主题解析器,可动态切换主题。
4)初始化处理映射器,用来找到请求对应的具体处理方法。
5)初始化处理适配器,用来执行method。
6)初始化异常解析器,用来处理异常信息。
7)初始化视图名称翻译器,其中可以设置prefix、suffix等信息,跳转视图时,viewNameTranslator可以拼接前、后缀得到完整的视图名称。
8)初始化视图解析器,有了这个,接口返回的视图才能正常的进行跳转。
9)初始化FlashMapManager,主要用于redirect传参。

我们现在主要分析handlerMapping中那些参数是怎样读取并设置进去的,所以我们需要解读initHandlerMappings方法。

8.3 initHandlerMappings

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

	if (this.detectAllHandlerMappings) {
		// Find all HandlerMappings in the ApplicationContext, including ancestor contexts.
		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.
	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;
		}
	}
}

这个方法最重要的就是从beanFactory中获取HandlerMapping
可以看到此时handlerMappings变量中有5个类型的HandlerMapping。

我们来分析一下RequestMappingHandlerMapping。

9. RequestMappingHandlerMapping

9.1 类图


可以看到,RequestMappingHandlerMapping间接的实现了InitializingBean接口,那么,它就会在实例化bean时,在调用init方法之前,调用其afterPropertiesSet方法。
不太熟悉SpringBean生命周期的,可以参考文章《死磕Spring系列:从源码理解SpringBean生命周期》

9.2 afterPropertiesSet

RequestMappingHandlerMapping类中afterPropertiesSet具体实现如下:

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

就是设置一些必要的参数与调用父类的afterPropertiesSet方法。这个方法是在AbstractHandlerMethodMapping类中的实现。

10. AbstractHandlerMethodMapping

10.1 afterPropertiesSet

public void afterPropertiesSet() {
	initHandlerMethods();
}

10.2 initHandlerMethods

protected void initHandlerMethods() {
	for (String beanName : getCandidateBeanNames()) {
		if (!beanName.startsWith(SCOPED_TARGET_NAME_PREFIX)) {
			processCandidateBean(beanName);
		}
	}
	handlerMethodsInitialized(getHandlerMethods());
}

其中就是获取beanFactory中几乎所有的beanName,如果不是以scopedTarget.开头的bean则执行processCandidateBean方法。
最后执行handlerMethodsInitialized方法。

10.3 processCandidateBean

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);
		}
	}
	if (beanType != null && isHandler(beanType)) {
		detectHandlerMethods(beanName);
	}
}

主要逻辑为如果该beanName在beanFactory中,且该类有@Controller或者@RequestMapping注解,则执行detectHandlerMethods

10.3.1 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);
		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));
		}
		else if (mappingsLogger.isDebugEnabled()) {
			mappingsLogger.debug(formatMappings(userType, methods));
		}
		methods.forEach((method, mapping) -> {
			Method invocableMethod = AopUtils.selectInvocableMethod(method, userType);
			registerHandlerMethod(handler, invocableMethod, mapping);
		});
	}
}

其中的主要就是:
1)扫描类的带有@RequestMapping注解的Method,将其与为去其生成的RequestMappingInfo逐个注册到mappingRegistry中。
注册的方法registerHandlerMethod是调用MappingRegistry类的register方法:

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

		Set<String> directPaths = AbstractHandlerMethodMapping.this.getDirectPaths(mapping);
		for (String path : directPaths) {
			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();
	}
}

其中的主要就是:
1)开启写锁。
2)创建HandlerMethod对象。
3)将路径中没有可变参数的将其pathRequestMappingInfo添加到pathLookup中。如**"/test/""/test/{id}"即为有可变参数。
4)将
handlerMethod
与其生成的name添加到nameLookup中。如DataController中的req方法,生成的name为DC#req
5)将配置了跨域信息的handlerMethod与跨域配置CorsConfiguration放入corsLookup。
6)类中所有的MVC方法都会以RequestMappingInfo为key,MappingRegistration为value的形式添加到registry中。
7)释放写锁。

由于这里已经将相关数据初始化,所以在对请求进行方法匹配的时候,就能直接使用已有数据进行处理。

10.4 handlerMethodsInitialized

protected void handlerMethodsInitialized(Map<T, HandlerMethod> handlerMethods) {
	// Total includes detected mappings + explicit registrations via registerMapping
	int total = handlerMethods.size();
	if ((logger.isTraceEnabled() && total == 0) || (logger.isDebugEnabled() && total > 0) ) {
		logger.debug(total + " mappings in " + formatMappingName());
	}
}

这里就是打印日志的功能。

总结来讲,MVC的工作逻辑分为两步。
1)首次调用时,初始化各种解析器,并封装各种元数据到变量中,便于后续处理。
2)发起http请求时,DispatcherServlet接收到处理请求,首先使用HandlerMapping匹配得到HandlerExecutionChain,再由HandlerExecutionChain中的HandlerMethod得到适配的HandlerAdapter,由HandlerAdapter负责执行HandlerMethod后得到ModelAndView,最后由视图解析器ViewResolver解析得到View,由View进行页面跳转。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值