02-HandlerMapping

SpringMVC九大内置组件

  • HandlerMapping
  • HandlerAdapter
  • ViewResolver
  • HandlerExceptionResolver
  • RequestToViewNameTranslator
  • LocaleResolver
  • ThemeResolver
  • MultipartResolver
  • FlashMapManager

1. service()

1. META-INF

  • 自建的module,没有spring.handlers spring.schemas,项目编译会覆盖这两个文件
  • resources/META-INF/spring.handlers resources/META-INF/spring.schemas取其余module下这两个文件并集集合

2. doService()

image-20240126142206738
image-20240125092544992

1. HttpServlet

  • Tomcat 核心组件
    • Servlet 容器
    • JSP 引擎
    • HTTP 服务器
    • WebSocket 支持和集群和负载均衡

Tomcat代码

public abstract class HttpServlet extends GenericServlet {

    private static final String METHOD_DELETE = "DELETE";
    private static final String METHOD_HEAD = "HEAD";
    private static final String METHOD_GET = "GET";
    private static final String METHOD_OPTIONS = "OPTIONS";
    private static final String METHOD_POST = "POST";
    private static final String METHOD_PUT = "PUT";
    private static final String METHOD_TRACE = "TRACE";

    private static final String HEADER_IFMODSINCE = "If-Modified-Since";
    private static final String HEADER_LASTMOD = "Last-Modified";

    private static final String LSTRING_FILE = "javax.servlet.http.LocalStrings";
    private static ResourceBundle lStrings = ResourceBundle.getBundle(LSTRING_FILE);

    @Override
    public void service(ServletRequest req, ServletResponse res) throws ServletException, IOException {
        HttpServletRequest  request;
        HttpServletResponse response;

        if (!(req instanceof HttpServletRequest && res instanceof HttpServletResponse)) {
            throw new ServletException("non-HTTP request or response");
        }

        request = (HttpServletRequest) req;
        response = (HttpServletResponse) res;

        // 1... FrameworkServlet
        service(request, response);
    }

    protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        String method = req.getMethod();

        if (method.equals(METHOD_GET)) {
            // 1.1. `lastModified`
            long lastModified = getLastModified(req);
            if (lastModified == -1) {
                // servlet doesn't support if-modified-since, no reason
                // to go through further expensive logic
                // 1.2... FrameworkServlet
                doGet(req, resp);
            } else {
                long ifModifiedSince = req.getDateHeader(HEADER_IFMODSINCE);
                if (ifModifiedSince < lastModified) {
                    // 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);
            // 2... FrameworkServlet
            doHead(req, resp);

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

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

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

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

        } else if (method.equals(METHOD_TRACE)) {
            // 7... FrameworkServlet
            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);
        }
    }

}





























 






 





 

















 



 



 



 



 



 















1. lastModified
  • 处理GET、HEAD请求的Last-Modified。当浏览器第一次向服务器请求资源时,服务器会在返回的请求头里包含一个last_modified的属性,代表资源最后是什么时候修改的。之后再发送请求,会携带接收到的Last_modified。服务器将两个Last_modified对比。过期了返回新的资源,否则直接返回304表示未过期,直接使用之前缓存的结果即可
image-20240111162526763

2. FrameworkServlet

  • super.service(request, response);后面调用Tomcat代码,无法debug。直接do***()即可
@SuppressWarnings("serial")
public abstract class FrameworkServlet extends HttpServletBean implements ApplicationContextAware {

	/**
	 * 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());
		// 处理patch请求
		if (httpMethod == HttpMethod.PATCH || httpMethod == null) {
			processRequest(request, response);
		}
		else {
			// 1... HttpServlet
			super.service(request, response);
		}
	}

	/**
	 * Delegate GET requests to processRequest/doService.
	 * <p>Will also be invoked by HttpServlet's default implementation of {@code doHead},
	 * with a {@code NoBodyResponse} that just captures the content length.
	 * @see #doService
	 * @see #doHead
	 */
	@Override
	protected final void doGet(HttpServletRequest request, HttpServletResponse response)
			throws ServletException, IOException {

		processRequest(request, response);
	}

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

		// 记录当前时间,用于计算处理请求花费的时间
		long startTime = System.currentTimeMillis();
		// 记录异常,用于保存处理请求过程中发送的异常
		Throwable failureCause = null;

		// 1. 获取LocaleContextHolder中原来保存的LocaleContext(保存的本地化信息)
		LocaleContext previousLocaleContext = LocaleContextHolder.getLocaleContext();
		// 2.1... DispatcherServlet 获取当前请求的LocaleContext
		LocaleContext localeContext = buildLocaleContext(request);

		// 获取RequestContextHolder总原来保存的RequestAttribute(管理request和session的属性)
		RequestAttributes previousAttributes = RequestContextHolder.getRequestAttributes();
		// 获取当前请求的ServletRequestAttribute
		ServletRequestAttributes requestAttributes = buildRequestAttributes(request, response, previousAttributes);

		// 获取异步管理器
		WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
		asyncManager.registerCallableInterceptor(FrameworkServlet.class.getName(), new RequestBindingInterceptor());

		// 2.2. 当前请求的LocaleContext、ServletRequestAttribute设置到LocaleContextHolder、RequestContextHolder
		initContextHolders(request, localeContext, requestAttributes);

		try {
			// 3... DispatcherServlet
			doService(request, response);
		}
		catch (ServletException | IOException ex) {
			// 记录抛出的异常
			failureCause = ex;
			throw ex;
		}
		catch (Throwable ex) {
			// 记录抛出的异常
			failureCause = ex;
			throw new NestedServletException("Request processing failed", ex);
		}

		finally {
			// 2.3. 恢复原来的LocaleContext、ServletRequestAttributes到LocaleContextHolder、RequestContextHolder
			resetContextHolders(request, previousLocaleContext, previousAttributes);
			if (requestAttributes != null) {
				requestAttributes.requestCompleted();
			}
			// 日志级别为debug,打印请求日志
			logResult(request, response, failureCause, asyncManager);
			// 发布ServletRequestHandledEvent请求处理完成事件
			publishRequestHandledEvent(request, response, startTime, failureCause);
		}
	}

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

        processRequest(request, response);
    }

    @Override
    protected final void doPut(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {

        processRequest(request, response);
    }

    @Override
    protected final void doDelete(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {

        processRequest(request, response);
    }

    @Override
    protected void doOptions(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {

        // 如果 dispatcherOptionsRequest 为true,则处理该请求,默认为true
        if (this.dispatchOptionsRequest || CorsUtils.isPreFlightRequest(request)) {
            // 处理请求
            processRequest(request, response);
            // 如果响应Header包含allow,则不需要提交给父方法处理
            if (response.containsHeader("Allow")) {
                // Proper OPTIONS response coming from a handler - we're done.
                return;
            }
        }

        // Use response wrapper in order to always add PATCH to the allowed methods
        // 调用父方法,并在响应Header的allow增加patch的值
        super.doOptions(request, new HttpServletResponseWrapper(response) {
            @Override
            public void setHeader(String name, String value) {
                if ("Allow".equals(name)) {
                    value = (StringUtils.hasLength(value) ? value + ", " : "") + HttpMethod.PATCH.name();
                }
                super.setHeader(name, value);
            }
        });
    }

    @Override
    protected void doTrace(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {

        // 如果 dispatchTraceRequest 为 true ,则处理该请求,默认为 false
        if (this.dispatchTraceRequest) {
            // 处理请求
            processRequest(request, response);
            // 如果响应的内容类型为 "message/http" ,则不需要交给父方法处理
            if ("message/http".equals(response.getContentType())) {
                // Proper TRACE response coming from a handler - we're done.
                return;
            }
        }
        // 调用父方法
        super.doTrace(request, response);
    }

}














 



 














 













 











 



 














 














 






 






 









 



























 











3. DispatcherServlet

@SuppressWarnings("serial")
public class DispatcherServlet extends FrameworkServlet {

    /**
     * 语言处理器,提供国际化的支持
     * LocaleResolver used by this servlet.
     */
    @Nullable
    private LocaleResolver localeResolver;

	@Override
	protected LocaleContext buildLocaleContext(final HttpServletRequest request) {
	    // 1. 内置组件
		LocaleResolver lr = this.localeResolver;
		if (lr instanceof LocaleContextResolver) {
			return ((LocaleContextResolver) lr).resolveLocaleContext(request);
		}
		else {
            // 2. AcceptHeaderLocaleResolver
			return () -> (lr != null ? lr.resolveLocale(request) : request.getLocale());
		}
	}

	/**
	 * Exposes the DispatcherServlet-specific request attributes and delegates to {@link #doDispatch}
	 * for the actual dispatching.
	 */
	@Override
	protected void doService(HttpServletRequest request, HttpServletResponse response) throws Exception {
		// 如果日志级别为DEBUG,则打印请求日志
		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.
		// 当include请求时,对request的Attribute做快照备份
		Map<String, Object> attributesSnapshot = null;
		if (WebUtils.isIncludeRequest(request)) { // skip
			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.
		// 设置Spring框架中的常用对象到Request属性中,这四个属性会在handler和view中使用
		request.setAttribute(WEB_APPLICATION_CONTEXT_ATTRIBUTE, getWebApplicationContext());  // .CONTEXT
		request.setAttribute(LOCALE_RESOLVER_ATTRIBUTE, this.localeResolver);				  // .LOCALE_RESOLVER
		request.setAttribute(THEME_RESOLVER_ATTRIBUTE, this.themeResolver); 				  // .THEME_RESOLVER
		request.setAttribute(THEME_SOURCE_ATTRIBUTE, getThemeSource());						  // .THEME_SOURCE

		// FlashMap的相关配置,主要用于Redirect转发时参数的传递
		// post请求redirect本身只能通过url传递参数,而url有长度的限制同时还容易对外暴露,可以使用flashMap来传递参数
		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 {
			// 1.. 执行请求的分发
			doDispatch(request, response);
		}
		finally {
			// 异步处理相关
			if (!WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted()) {
				// Restore the original attribute snapshot, in case of an include.
				// 还原request快照的属性
				if (attributesSnapshot != null) {
					restoreAttributesAfterInclude(request, attributesSnapshot);
				}
			}
		}
	}

}













 





 















































 














2. doDispatch()

两个阶段,两层try_catch

  1. 请求处理
  2. 页面渲染
39d73c42e47f0b50e3e51d156b584bb0
@SuppressWarnings("serial")
public class DispatcherServlet extends FrameworkServlet {

    protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
        // 实际处理时所用的request,如果不是上传请求,则直接使用接收到的request,否则封装成上传类型的request
        HttpServletRequest processedRequest = request;
        // 处理请求的处理器链(包含处理器和对应的interceptor)
        HandlerExecutionChain mappedHandler = null;
        // 是不是上传请求的标志
        boolean multipartRequestParsed = false;

        // 获取异步管理器
        WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);

        try {
            // 封装model和view的容器
            ModelAndView mv = null;
            // 处理请求过程中抛出的异常,但是不包含渲染过程中抛出的异常
            Exception dispatchException = null;

            try {
                // 1. 上传请求,则通过multipartResolver将其封装成MultipartHttpServletRequest
                processedRequest = checkMultipart(request);
                // 设置上传请求标志
                multipartRequestParsed = (processedRequest != request);

                // 2. Determine handler for the current request.
                // 获取请求对应的HandlerExecutionChain对象(HandlerMethod、HandlerInterceptor)
                mappedHandler = getHandler(processedRequest);
                // 获取不到,则根据配置抛出异常、返回404错误
                if (mappedHandler == null) {
                    noHandlerFound(processedRequest, response);
                    return;
                }

                // 3. Determine handler adapter for the current request.
                // 获得当前handler对应的HandlerAdapter对象
                HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());

                // 4. Process last-modified header, if supported by the handler.
                // 处理GET、HEAD请求的Last-Modified
                String method = request.getMethod();
                boolean isGet = "GET".equals(method);
                if (isGet || "HEAD".equals(method)) {
                    // 获取请求中Service端最后被修改时间
                    long lastModified = ha.getLastModified(request, mappedHandler.getHandler());
                    if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) {
                        return;
                    }
                }

                // 5. 执行响应的Interceptor的preHandler
                // 注意:该方法如果有一个拦截器的前置处理返回false,则开始倒序触发所有的拦截器的 已完成处理
                if (!mappedHandler.applyPreHandle(processedRequest, response)) {
                    return;
                }

                // 6. Actually invoke the handler.
                // 真正的调用handler方法,也就是执行对应的方法,并返回视图
                mv = ha.handle(processedRequest, response, mappedHandler.getHandler());

                // 7. 需要异步处理,直接返回
                if (asyncManager.isConcurrentHandlingStarted()) {
                    return;
                }

                // 8. 当view为空时,根据request设置默认的view
                applyDefaultViewName(processedRequest, mv);

                // 9. 执行响应的interceptor的postHandler
                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);
            }
            // 10. 处理返回结果,包括处理异常、渲染页面、触发Interceptor的afterCompletion
            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));
        }
        // 11. 释放资源
        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);
                }
            }
        }
    }

}






















 





 








 



 











 





 


 




 


 









 








 


















image-20240126220224477

1. HandlerInterceptor

public interface HandlerInterceptor {

	default boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
			throws Exception {

		return true;
	}

	default void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,
			@Nullable ModelAndView modelAndView) throws Exception {
	}

	default void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler,
			@Nullable Exception ex) throws Exception {
	}

}

2. HandlerAdapter

/**
 * MVC framework SPI, allowing parameterization of the core MVC workflow.
 *
 * <p>Interface that must be implemented for each handler type to handle a request.
 * This interface is used to allow the {@link DispatcherServlet} to be indefinitely
 * extensible. The {@code DispatcherServlet} accesses all installed handlers through
 * this interface, meaning that it does not contain code specific to any handler type.
 *
 * <p>Note that a handler can be of type {@code Object}. This is to enable
 * handlers from other frameworks to be integrated with this framework without
 * custom coding, as well as to allow for annotation-driven handler objects that
 * do not obey any specific Java interface.
 *
 * <p>This interface is not intended for application developers. It is available
 * to handlers who want to develop their own web workflow.
 *
 * <p>Note: {@code HandlerAdapter} implementors may implement the {@link
 * org.springframework.core.Ordered} interface to be able to specify a sorting
 * order (and thus a priority) for getting applied by the {@code DispatcherServlet}.
 * Non-Ordered instances get treated as lowest priority.
 *
 * @author Rod Johnson
 * @author Juergen Hoeller
 * @see org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter
 * @see org.springframework.web.servlet.handler.SimpleServletHandlerAdapter
 */
public interface HandlerAdapter {

    /**
     * 是否支持该处理器
     * <p>
     * Given a handler instance, return whether or not this {@code HandlerAdapter}
     * can support it. Typical HandlerAdapters will base the decision on the handler
     * type. HandlerAdapters will usually only support one handler type each.
     * <p>A typical implementation:
     * <p>{@code
     * return (handler instanceof MyHandler);
     * }
     *
     * @param handler the handler object to check
     * @return whether or not this object can use the given handler
     */
    boolean supports(Object handler);

    /**
     * 执行处理器,返回ModelAndView结果
     * <p>
     * Use the given handler to handle this request.
     * The workflow that is required may vary widely.
     *
     * @param request  current HTTP request
     * @param response current HTTP response
     * @param handler  the handler to use. This object must have previously been passed
     *                 to the {@code supports} method of this interface, which must have
     *                 returned {@code true}.
     * @return a ModelAndView object with the name of the view and the required
     * model data, or {@code null} if the request has been handled directly
     * @throws Exception in case of errors
     */
    @Nullable
    ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception;

    /**
     * 返回请求的最新更新时间,如果不支持该操作,则返回-1即可
     * <p>
     * Same contract as for HttpServlet's {@code getLastModified} method.
     * Can simply return -1 if there's no support in the handler class.
     *
     * @param request current HTTP request
     * @param handler the handler to use
     * @return the lastModified value for the given handler
     * see javax.servlet.http.HttpServlet#getLastModified
     * @see org.springframework.web.servlet.mvc.LastModified#getLastModified
     */
    long getLastModified(HttpServletRequest request, Object handler);

}

3. ViewResolver

/**
 * 将String类型的视图名和locale解析为view类型的视图
 * viewResolver需要找到渲染所用的模板和视图的类型进行渲染,具体的渲染过程则交给不同的视图来完成
 * 最常使用的有
 * UrlBasedViewResolver:针对单一视图
 * InternalResourceViewResolver:针对jsp类型的视图
 * FreeMarkerViewResolver:针对freeMaker
 * <p>
 * Interface to be implemented by objects that can resolve views by name.
 *
 * <p>View state doesn't change during the running of the application,
 * so implementations are free to cache views.
 *
 * <p>Implementations are encouraged to support internationalization,
 * i.e. localized view resolution.
 *
 * @author Rod Johnson
 * @author Juergen Hoeller
 * @see org.springframework.web.servlet.view.InternalResourceViewResolver
 * @see org.springframework.web.servlet.view.ResourceBundleViewResolver
 * @see org.springframework.web.servlet.view.XmlViewResolver
 */
public interface ViewResolver {

    /**
     * 根据视图名和国际化,获得最终的view对象
     * <p>
     * Resolve the given view by name.
     * <p>Note: To allow for ViewResolver chaining, a ViewResolver should
     * return {@code null} if a view with the given name is not defined in it.
     * However, this is not required: Some ViewResolvers will always attempt
     * to build View objects with the given name, unable to return {@code null}
     * (rather throwing an exception when View creation failed).
     *
     * @param viewName name of the view to resolve
     * @param locale   the Locale in which to resolve the view.
     *                 ViewResolvers that support internationalization should respect this.
     * @return the View object, or {@code null} if not found
     * (optional, to allow for ViewResolver chaining)
     * @throws Exception if the view cannot be resolved
     *                   (typically in case of problems creating an actual View object)
     */
    @Nullable
    View resolveViewName(String viewName, Locale locale) throws Exception;

}

3. upload

  • https://commons.apache.org/proper/commons-fileupload下载CommonsFileUploadSupport的jar包
image-20240112105311265
image-20240112105427460

1. checkMultipart()

  • 不存在默认的multipartResolver,所以需要IOC注入
<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
    <property name="maxUploadSize" value="17367648787"/>
    <property name="defaultEncoding" value="UTF-8"/>
</bean>
  • this.multipartResolver.isMultipart(request)
  • this.multipartResolver.resolveMultipart(request)
@SuppressWarnings("serial")
public class DispatcherServlet extends FrameworkServlet {

    /**
     * multipart数据文件处理器
     * MultipartResolver used by this servlet.
     */
    @Nullable
    private MultipartResolver multipartResolver; // CommonsMultipartResolver

	/**
	 * Initialize the MultipartResolver used by this class.
	 * <p>If no bean is defined with the given name in the BeanFactory for this namespace,
	 * no multipart handling is provided.
	 */
	private void initMultipartResolver(ApplicationContext context) {
		try {
			// 1. IOC中获取MultipartResolver的Bean
			this.multipartResolver = context.getBean(MULTIPART_RESOLVER_BEAN_NAME, MultipartResolver.class);
			if (logger.isTraceEnabled()) {
				logger.trace("Detected " + this.multipartResolver);
			}
			else if (logger.isDebugEnabled()) {
				logger.debug("Detected " + this.multipartResolver.getClass().getSimpleName());
			}
		}
		catch (NoSuchBeanDefinitionException ex) {
			// Default is no multipart resolver.
			this.multipartResolver = null;
			if (logger.isTraceEnabled()) {
				logger.trace("No MultipartResolver '" + MULTIPART_RESOLVER_BEAN_NAME + "' declared");
			}
		}
	}

    /**
     * Convert the request into a multipart request, and make multipart resolver available.
     * <p>If no multipart resolver is set, simply use the existing request.
     *
     * @param request current HTTP request
     * @return the processed request (multipart wrapper if necessary)
     * @see MultipartResolver#resolveMultipart
     */
    protected HttpServletRequest checkMultipart(HttpServletRequest request) throws MultipartException {
        // 1... CommonsMultipartResolver multipart(文件)请求
        if (this.multipartResolver != null && this.multipartResolver.isMultipart(request)) {
            if (WebUtils.getNativeRequest(request, MultipartHttpServletRequest.class) != null) {
                if (request.getDispatcherType().equals(DispatcherType.REQUEST)) {
                    logger.trace("Request already resolved to MultipartHttpServletRequest, e.g. by MultipartFilter");
                }
            } else if (hasMultipartException(request)) {
                logger.debug("Multipart resolution previously failed for current request - " +
                        "skipping re-resolution for undisturbed error rendering");
            } else {
                try {
                    // 2... CommonsMultipartResolver HttpServletRequest封装成MultipartHttpServletRequest,解析请求参数以及文件
                    return this.multipartResolver.resolveMultipart(request);
                } catch (MultipartException ex) {
                    if (request.getAttribute(WebUtils.ERROR_EXCEPTION_ATTRIBUTE) != null) {
                        logger.debug("Multipart resolution failed for error dispatch", ex);
                        // Keep processing error dispatch with regular request handle below
                    } else {
                        throw ex;
                    }
                }
            }
        }
        // If not returned before: return original request.
        return request;
    }

}


















 


























 










 















<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%
    pageContext.setAttribute("ctx", request.getContextPath());
%>
<html>
<head>
    <title>Title</title>
</head>
<body>
<form action="${ctx}/fileupload" enctype="multipart/form-data" method="post">
    描述:<input type="text" name="desc"><br><br>
    文件:<input type="file" name="file"><br><br>
    <input type="submit" value="上传">
</form>
</body>
</html>









 






2. CommonsMultipartResolver

image-20240221163722399
/**
 * Servlet-based {@link MultipartResolver} implementation for
 * <a href="https://commons.apache.org/proper/commons-fileupload">Apache Commons FileUpload</a>
 * 1.2 or above.
 *
 * <p>Provides "maxUploadSize", "maxInMemorySize" and "defaultEncoding" settings as
 * bean properties (inherited from {@link CommonsFileUploadSupport}). See corresponding
 * ServletFileUpload / DiskFileItemFactory properties ("sizeMax", "sizeThreshold",
 * "headerEncoding") for details in terms of defaults and accepted values.
 *
 * <p>Saves temporary files to the servlet container's temporary directory.
 * Needs to be initialized <i>either</i> by an application context <i>or</i>
 * via the constructor that takes a ServletContext (for standalone usage).
 *
 * @author Trevor D. Cook
 * @author Juergen Hoeller
 * @see #CommonsMultipartResolver(ServletContext)
 * @see #setResolveLazily
 * @see org.apache.commons.fileupload.servlet.ServletFileUpload
 * @see org.apache.commons.fileupload.disk.DiskFileItemFactory
 * @since 29.09.2003
 */
public class CommonsMultipartResolver extends CommonsFileUploadSupport
        implements MultipartResolver, ServletContextAware {

    @Override
    protected FileUpload newFileUpload(FileItemFactory fileItemFactory) {
        return new ServletFileUpload(fileItemFactory);
    }

    @Override
    public boolean isMultipart(HttpServletRequest request) {
        // 1... ServletFileUpload
        return ServletFileUpload.isMultipartContent(request);
    }

    @Override
    public MultipartHttpServletRequest resolveMultipart(final HttpServletRequest request) throws MultipartException {
        Assert.notNull(request, "Request must not be null");
        if (this.resolveLazily) {
            return new DefaultMultipartHttpServletRequest(request) {
                @Override
                protected void initializeMultipart() {
                    MultipartParsingResult parsingResult = parseRequest(request);
                    setMultipartFiles(parsingResult.getMultipartFiles());
                    setMultipartParameters(parsingResult.getMultipartParameters());
                    setMultipartParameterContentTypes(parsingResult.getMultipartParameterContentTypes());
                }
            };
        } else {
            // 1..
            MultipartParsingResult parsingResult = parseRequest(request);

            // 2. 请求、多文件、多实参,分别封装
            return new DefaultMultipartHttpServletRequest(request, parsingResult.getMultipartFiles(),
                    parsingResult.getMultipartParameters(), parsingResult.getMultipartParameterContentTypes());
        }
    }

    protected MultipartParsingResult parseRequest(HttpServletRequest request) throws MultipartException {
        // 1.. 读取当前请求编码
        String encoding = determineEncoding(request);
        // 2... CommonsFileUploadSupport => ServletFileUpload 比较请求的编码、XML中配置编码,不一样,会拒绝处理
        FileUpload fileUpload = prepareFileUpload(encoding);
        try {
            // 3... ServletFileUpload 对请求中的multipart文件进行具体的处理
            List<FileItem> fileItems = ((ServletFileUpload) fileUpload).parseRequest(request);
            // 4... CommonsFileUploadSupport
            return parseFileItems(fileItems, encoding);
        } catch (FileUploadBase.SizeLimitExceededException ex) {
            throw new MaxUploadSizeExceededException(fileUpload.getSizeMax(), ex);
        } catch (FileUploadBase.FileSizeLimitExceededException ex) {
            throw new MaxUploadSizeExceededException(fileUpload.getFileSizeMax(), ex);
        } catch (FileUploadException ex) {
            throw new MultipartException("Failed to parse multipart servlet request", ex);
        }
    }

    protected String determineEncoding(HttpServletRequest request) {
        String encoding = request.getCharacterEncoding();
        if (encoding == null) {
            // 1... CommonsFileUploadSupport
            encoding = getDefaultEncoding();
        }
        return encoding;
    }

}


 
























 





 

















 


 






 

 


 

 













 





image-20240112144653568

1. MultipartResolver

  • 常用实现类CommonsMultipartResolver
package org.springframework.web.multipart;

import javax.servlet.http.HttpServletRequest;

/**
 * MultipartResolver用于处理上传请求,处理方式是将普通的request包装成MultipartHttpServletRequest,可以直接调用getFile方法来获取File,
 * 如果上传多个文件,可以调用getFileMap来处理。
 * <p>
 * A strategy interface for multipart file upload resolution in accordance
 * with <a href="https://www.ietf.org/rfc/rfc1867.txt">RFC 1867</a>.
 * Implementations are typically usable both within an application context
 * and standalone.
 *
 * <p>There are two concrete implementations included in Spring, as of Spring 3.1:
 * <ul>
 * <li>{@link org.springframework.web.multipart.commons.CommonsMultipartResolver}
 * for Apache Commons FileUpload
 * <li>{@link org.springframework.web.multipart.support.StandardServletMultipartResolver}
 * for the Servlet 3.0+ Part API
 * </ul>
 *
 * <p>There is no default resolver implementation used for Spring
 * {link org.springframework.web.servlet.DispatcherServlet DispatcherServlets},
 * as an application might choose to parse its multipart requests itself. To define
 * an implementation, create a bean with the id "multipartResolver" in a
 * {link org.springframework.web.servlet.DispatcherServlet DispatcherServlet's}
 * application context. Such a resolver gets applied to all requests handled
 * by that {link org.springframework.web.servlet.DispatcherServlet}.
 *
 * <p>If a {link org.springframework.web.servlet.DispatcherServlet} detects a
 * multipart request, it will resolve it via the configured {@link MultipartResolver}
 * and pass on a wrapped {@link javax.servlet.http.HttpServletRequest}. Controllers
 * can then cast their given request to the {@link MultipartHttpServletRequest}
 * interface, which allows for access to any {@link MultipartFile MultipartFiles}.
 * Note that this cast is only supported in case of an actual multipart request.
 *
 * <pre class="code">
 * public ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response) {
 *   MultipartHttpServletRequest multipartRequest = (MultipartHttpServletRequest) request;
 *   MultipartFile multipartFile = multipartRequest.getFile("image");
 *   ...
 * }</pre>
 * <p>
 * Instead of direct access, command or form controllers can register a
 * {@link org.springframework.web.multipart.support.ByteArrayMultipartFileEditor}
 * or {@link org.springframework.web.multipart.support.StringMultipartFileEditor}
 * with their data binder, to automatically apply multipart content to form
 * bean properties.
 *
 * <p>As an alternative to using a {@link MultipartResolver} with a
 * {link org.springframework.web.servlet.DispatcherServlet},
 * a {@link org.springframework.web.multipart.support.MultipartFilter} can be
 * registered in {@code web.xml}. It will delegate to a corresponding
 * {@link MultipartResolver} bean in the root application context. This is mainly
 * intended for applications that do not use Spring's own web MVC framework.
 *
 * <p>Note: There is hardly ever a need to access the {@link MultipartResolver}
 * itself from application code. It will simply do its work behind the scenes,
 * making {@link MultipartHttpServletRequest MultipartHttpServletRequests}
 * available to controllers.
 *
 * @author Juergen Hoeller
 * @author Trevor D. Cook
 * @see MultipartHttpServletRequest
 * @see MultipartFile
 * @see org.springframework.web.multipart.commons.CommonsMultipartResolver
 * @see org.springframework.web.multipart.support.ByteArrayMultipartFileEditor
 * @see org.springframework.web.multipart.support.StringMultipartFileEditor
 * see org.springframework.web.servlet.DispatcherServlet
 * @since 29.09.2003
 */
public interface MultipartResolver {

    /**
     * 判断是否是上传请求
     */
    boolean isMultipart(HttpServletRequest request);

    /**
     * 将request请求包装成MultipartHttpServletRequest
     */
    MultipartHttpServletRequest resolveMultipart(HttpServletRequest request) throws MultipartException;

    /**
     * 处理完成之后清理上传过程中产生的临时资源
     */
    void cleanupMultipart(MultipartHttpServletRequest request);

}















 

 







































































2. CommonsFileUploadSupport

public abstract class CommonsFileUploadSupport {

	public static final String DEFAULT_CHARACTER_ENCODING = "ISO-8859-1";

    private final DiskFileItemFactory fileItemFactory; // DiskFileItemFactory

    private final FileUpload fileUpload; // ServletFileUpload

    public CommonsFileUploadSupport() {
        // 1.. DiskFileItemFactory
        this.fileItemFactory = newFileItemFactory();
        // 2... CommonsMultipartResolver => ServletFileUpload
        this.fileUpload = newFileUpload(getFileItemFactory());
    }

    protected DiskFileItemFactory newFileItemFactory() {
        return new DiskFileItemFactory();
    }

    protected String getDefaultEncoding() {
        String encoding = getFileUpload().getHeaderEncoding();
        if (encoding == null) {
            // 1. ISO-8859-1
            encoding = WebUtils.DEFAULT_CHARACTER_ENCODING;
        }
        return encoding;
    }

    protected FileUpload prepareFileUpload(@Nullable String encoding) {
        // 1..
        FileUpload fileUpload = getFileUpload(); // ServletFileUpload
        FileUpload actualFileUpload = fileUpload;

        // Use new temporary FileUpload instance if the request specifies
        // its own encoding that does not match the default encoding.
        if (encoding != null && !encoding.equals(fileUpload.getHeaderEncoding())) { // skip
            actualFileUpload = newFileUpload(getFileItemFactory());
            actualFileUpload.setSizeMax(fileUpload.getSizeMax());
            actualFileUpload.setFileSizeMax(fileUpload.getFileSizeMax());
            actualFileUpload.setHeaderEncoding(encoding);
        }

        return actualFileUpload;
    }

	public FileUpload getFileUpload() {
		return this.fileUpload;
	}

    protected MultipartParsingResult parseFileItems(List<FileItem> fileItems, String encoding) {
        // 保存上传的文件
        MultiValueMap<String, MultipartFile> multipartFiles = new LinkedMultiValueMap<>();
        // 保存参数
        Map<String, String[]> multipartParameters = new HashMap<>();
        // 保存参数的contentType
        Map<String, String> multipartParameterContentTypes = new HashMap<>();

        // Extract multipart files and multipart parameters.
        // 将fileItems分为文件和参数两类,并设置到对应的map
        for (FileItem fileItem : fileItems) {
            // 1. 参数类型
            if (fileItem.isFormField()) {
                String value;
                String partEncoding = determineEncoding(fileItem.getContentType(), encoding);
                try {
                    value = fileItem.getString(partEncoding);
                } catch (UnsupportedEncodingException ex) {
                    if (logger.isWarnEnabled()) {
                        logger.warn("Could not decode multipart item '" + fileItem.getFieldName() +
                                "' with encoding '" + partEncoding + "': using platform default");
                    }
                    value = fileItem.getString();
                }
                String[] curParam = multipartParameters.get(fileItem.getFieldName());
                if (curParam == null) {
                    // simple form field
                    // 单个参数
                    multipartParameters.put(fileItem.getFieldName(), new String[]{value});
                } else {
                    // array of simple form fields
                    // 数组参数
                    String[] newParam = StringUtils.addStringToArray(curParam, value);
                    multipartParameters.put(fileItem.getFieldName(), newParam);
                }
                // 保存参数的contentType
                multipartParameterContentTypes.put(fileItem.getFieldName(), fileItem.getContentType());
            }
            // 2. 文件类型
            else {
                // multipart file field
                CommonsMultipartFile file = createMultipartFile(fileItem);
                multipartFiles.add(file.getName(), file);
                LogFormatUtils.traceDebug(logger, traceOn ->
                        "Part '" + file.getName() + "', size " + file.getSize() +
                                " bytes, filename='" + file.getOriginalFilename() + "'" +
                                (traceOn ? ", storage=" + file.getStorageDescription() : "")
                );
            }
        }
        // 3.
        return new MultipartParsingResult(multipartFiles, multipartParameters, multipartParameterContentTypes);
    }

    /**
     * Create a {@link CommonsMultipartFile} wrapper for the given Commons {@link FileItem}.
     *
     * @param fileItem the Commons FileItem to wrap
     * @return the corresponding CommonsMultipartFile (potentially a custom subclass)
     * @see #setPreserveFilename(boolean)
     * @see CommonsMultipartFile#setPreserveFilename(boolean)
     * @since 4.3.5
     */
    protected CommonsMultipartFile createMultipartFile(FileItem fileItem) {
        CommonsMultipartFile multipartFile = new CommonsMultipartFile(fileItem);
        multipartFile.setPreserveFilename(this.preserveFilename);
        return multipartFile;
    }

}










 

 



 






 






 






























 















 




 







 









 












 





1. CommonsMultipartFile
  • MultipartFile 为 controller 上的形参
public class CommonsMultipartFile implements MultipartFile, Serializable {

	protected static final Log logger = LogFactory.getLog(CommonsMultipartFile.class);

	private final FileItem fileItem;

	private final long size;

	private boolean preserveFilename = false;

	/**
	 * Create an instance wrapping the given FileItem.
	 * @param fileItem the FileItem to wrap
	 */
	public CommonsMultipartFile(FileItem fileItem) {
		this.fileItem = fileItem;
		this.size = this.fileItem.getSize();
	}

	@Override
	public String getName() {
		return this.fileItem.getFieldName();
	}

	@Override
	public String getOriginalFilename() {
		String filename = this.fileItem.getName();
		if (filename == null) {
			// Should never happen.
			return "";
		}
		if (this.preserveFilename) {
			// Do not try to strip off a path...
			return filename;
		}

		// Check for Unix-style path
		int unixSep = filename.lastIndexOf('/');
		// Check for Windows-style path
		int winSep = filename.lastIndexOf('\\');
		// Cut off at latest possible point
		int pos = Math.max(winSep, unixSep);
		if (pos != -1)  {
			// Any sort of path separator found...
			return filename.substring(pos + 1);
		}
		else {
			// A plain name
			return filename;
		}
	}

	@Override
	public String getContentType() {
		return this.fileItem.getContentType();
	}

	@Override
	public byte[] getBytes() {
		if (!isAvailable()) {
			throw new IllegalStateException("File has been moved - cannot be read again");
		}
		byte[] bytes = this.fileItem.get();
		return (bytes != null ? bytes : new byte[0]);
	}

	@Override
	public InputStream getInputStream() throws IOException {
		if (!isAvailable()) {
			throw new IllegalStateException("File has been moved - cannot be read again");
		}
		InputStream inputStream = this.fileItem.getInputStream();
		return (inputStream != null ? inputStream : StreamUtils.emptyInput());
	}

	@Override
	public void transferTo(File dest) throws IOException, IllegalStateException {
		if (!isAvailable()) {
			throw new IllegalStateException("File has already been moved - cannot be transferred again");
		}

		if (dest.exists() && !dest.delete()) {
			throw new IOException(
					"Destination file [" + dest.getAbsolutePath() + "] already exists and could not be deleted");
		}

		try {
			this.fileItem.write(dest);
			LogFormatUtils.traceDebug(logger, traceOn -> {
				String action = "transferred";
				if (!this.fileItem.isInMemory()) {
					action = (isAvailable() ? "copied" : "moved");
				}
				return "Part '" + getName() + "',  filename '" + getOriginalFilename() + "'" +
						(traceOn ? ", stored " + getStorageDescription() : "") +
						": " + action + " to [" + dest.getAbsolutePath() + "]";
			});
		}
		catch (FileUploadException ex) {
			throw new IllegalStateException(ex.getMessage(), ex);
		}
		catch (IllegalStateException | IOException ex) {
			// Pass through IllegalStateException when coming from FileItem directly,
			// or propagate an exception from I/O operations within FileItem.write
			throw ex;
		}
		catch (Exception ex) {
			throw new IOException("File transfer failed", ex);
		}
	}

	@Override
	public void transferTo(Path dest) throws IOException, IllegalStateException {
		if (!isAvailable()) {
			throw new IllegalStateException("File has already been moved - cannot be transferred again");
		}

		FileCopyUtils.copy(this.fileItem.getInputStream(), Files.newOutputStream(dest));
	}

}






























































 








 















 





























 



3. ServletFileUpload

image-20240223085904554
public class ServletFileUpload extends FileUpload {

    /**
     * Constant for HTTP POST method.
     */
    private static final String POST_METHOD = "POST";

    public ServletFileUpload(FileItemFactory fileItemFactory) {
        super(fileItemFactory);
    }

    public static final boolean isMultipartContent(
            HttpServletRequest request) {
        if (!POST_METHOD.equalsIgnoreCase(request.getMethod())) {
            return false;
        }
        // 1... FileUploadBase
        return FileUploadBase.isMultipartContent(new ServletRequestContext(request));
    }

    @Override
    public List<FileItem> parseRequest(HttpServletRequest request) throws FileUploadException {
        // 1... FileUploadBase
        return parseRequest(new ServletRequestContext(request));
    }

}








 








 





 



1. FileUpload
public class FileUpload extends FileUploadBase {

    /**
     * The factory to use to create new form items.
     */
    private FileItemFactory fileItemFactory; // DiskFileItemFactory

    public FileUpload(FileItemFactory fileItemFactory) {
        super();
        this.fileItemFactory = fileItemFactory;
    }

}









 



2. FileUploadBase
public abstract class FileUploadBase {

    public static final String CONTENT_TYPE = "Content-type";

    public static final String CONTENT_DISPOSITION = "Content-disposition";

    public static final String CONTENT_LENGTH = "Content-length";

    public static final String FORM_DATA = "form-data";

    public static final String ATTACHMENT = "attachment";

    public static final String MULTIPART = "multipart/";

    public static final String MULTIPART_FORM_DATA = "multipart/form-data";

    public static final String MULTIPART_MIXED = "multipart/mixed";

    public static final boolean isMultipartContent(RequestContext ctx) {
        // 1... ServletRequestContext
        String contentType = ctx.getContentType();
        if (contentType == null) {
            return false;
        }
        // 2. `multipart/`
        if (contentType.toLowerCase(Locale.ENGLISH).startsWith(MULTIPART)) {
            return true;
        }
        return false;
    }

    public List<FileItem> parseRequest(RequestContext ctx)
            throws FileUploadException {
        List<FileItem> items = new ArrayList<FileItem>();
        boolean successful = false;
        try {
            // 1..
            FileItemIterator iter = getItemIterator(ctx);	// FileUploadBase$FileItemIteratorImpl
            FileItemFactory fac = getFileItemFactory();		// DiskFileItemFactory
            if (fac == null) {
                throw new NullPointerException("No FileItemFactory has been set.");
            }
            while (iter.hasNext()) {
                // 2... FileUploadBase$FileItemIteratorImpl => FileItemStreamImpl
                final FileItemStream item = iter.next();
                // Don't use getName() here to prevent an InvalidFileNameException.
                final String fileName = ((FileItemIteratorImpl.FileItemStreamImpl) item).name;
                // 3... DiskFileItemFactory => DiskFileItem
                FileItem fileItem = fac.createItem(item.getFieldName(), item.getContentType(), item.isFormField(), fileName);

                items.add(fileItem);
                try {
                    // 4.
                    Streams.copy(item.openStream(), fileItem.getOutputStream(), true);
                } catch (FileUploadIOException e) {
                    throw (FileUploadException) e.getCause();
                } catch (IOException e) {
                    throw new IOFileUploadException(format("Processing of %s request failed. %s",
                                                           MULTIPART_FORM_DATA, e.getMessage()), e);
                }
                // 5.
                final FileItemHeaders fih = item.getHeaders();
                fileItem.setHeaders(fih);
            }
            successful = true;
            // 6.
            return items;
        } catch (FileUploadIOException e) {
            throw (FileUploadException) e.getCause();
        } catch (IOException e) {
            throw new FileUploadException(e.getMessage(), e);
        } finally {
            if (!successful) {
                for (FileItem fileItem : items) {
                    try {
                        fileItem.delete();
                    } catch (Exception ignored) {
                        // ignored TODO perhaps add to tracker delete failure list somehow?
                    }
                }
            }
        }
    }

    public FileItemIterator getItemIterator(RequestContext ctx) throws FileUploadException, IOException {
        try {
            // 1.
            return new FileItemIteratorImpl(ctx);
        } catch (FileUploadIOException e) {
            // unwrap encapsulated SizeException
            throw (FileUploadException) e.getCause();
        }
    }

    // ------------------------------------------------------------------------

    /**
     * The iterator, which is returned by
     * {@link FileUploadBase#getItemIterator(RequestContext)}.
     */
    private class FileItemIteratorImpl implements FileItemIterator {

        /**
         * The multi part stream to process.
         */
        private final MultipartStream multi;

        /**
         * Creates a new instance.
         *
         * @param ctx The request context.
         * @throws FileUploadException An error occurred while
         *   parsing the request.
         * @throws IOException An I/O error occurred.
         */
        FileItemIteratorImpl(RequestContext ctx) throws FileUploadException, IOException {
            if (ctx == null) {
                throw new NullPointerException("ctx parameter");
            }

            // 1.
            String contentType = ctx.getContentType();
            if ((null == contentType) || (!contentType.toLowerCase(Locale.ENGLISH).startsWith(MULTIPART))) {
                throw new InvalidContentTypeException(String.format(
                        "the request doesn't contain a %s or %s stream, content type header is %s",
                        MULTIPART_FORM_DATA, MULTIPART_MIXED, contentType));
            }


            // 2.
            final long requestSize = ((UploadContext) ctx).contentLength();

            InputStream input; // N.B. this is eventually closed in MultipartStream processing
            if (sizeMax >= 0) {
                if (requestSize != -1 && requestSize > sizeMax) {
                    throw new SizeLimitExceededException(String.format(
                            "the request was rejected because its size (%s) exceeds the configured maximum (%s)",
                            Long.valueOf(requestSize), Long.valueOf(sizeMax)),
                            requestSize, sizeMax);
                }
                // 3.1. N.B. this is eventually closed in MultipartStream processing
                input = new LimitedInputStream(ctx.getInputStream(), sizeMax) {
                    @Override
                    protected void raiseError(long pSizeMax, long pCount)
                            throws IOException {
                        FileUploadException ex = new SizeLimitExceededException(
                        String.format("the request was rejected because its size (%s) exceeds the configured maximum (%s)",
                               Long.valueOf(pCount), Long.valueOf(pSizeMax)),
                               pCount, pSizeMax);
                        throw new FileUploadIOException(ex);
                    }
                };
            } else {
                // 3.2.
                input = ctx.getInputStream();
            }

            String charEncoding = headerEncoding;
            if (charEncoding == null) {
                // 4.
                charEncoding = ctx.getCharacterEncoding();
            }

            boundary = getBoundary(contentType);
            if (boundary == null) {
                IOUtils.closeQuietly(input); // avoid possible resource leak
                throw new FileUploadException("the request was rejected because no multipart boundary was found");
            }

            notifier = new MultipartStream.ProgressNotifier(listener, requestSize);
            try {
                // 5.
                multi = new MultipartStream(input, boundary, notifier);
            } catch (IllegalArgumentException iae) {
                IOUtils.closeQuietly(input); // avoid possible resource leak
                throw new InvalidContentTypeException(
                        String.format("The boundary specified in the %s header is too long", CONTENT_TYPE), iae);
            }
            // 6.
            multi.setHeaderEncoding(charEncoding);

            skipPreamble = true;
            // 7..
            findNextItem();
        }

        /**
         * Returns, whether another instance of {@link FileItemStream}
         * is available.
         *
         * @throws FileUploadException Parsing or processing the
         *   file item failed.
         * @throws IOException Reading the file item failed.
         * @return True, if one or more additional file items
         *   are available, otherwise false.
         */
        @Override
        public boolean hasNext() throws FileUploadException, IOException {
            if (eof) {
                return false;
            }
            if (itemValid) {
                return true;
            }
            try {
                // 1..
                return findNextItem();
            } catch (FileUploadIOException e) {
                // unwrap encapsulated SizeException
                throw (FileUploadException) e.getCause();
            }
        }

        /**
         * Returns the next available {@link FileItemStream}.
         *
         * @throws java.util.NoSuchElementException No more items are
         *   available. Use {@link #hasNext()} to prevent this exception.
         * @throws FileUploadException Parsing or processing the
         *   file item failed.
         * @throws IOException Reading the file item failed.
         * @return FileItemStream instance, which provides
         *   access to the next file item.
         */
        @Override
        public FileItemStream next() throws FileUploadException, IOException {
            if (eof  ||  (!itemValid && !hasNext())) {
                throw new NoSuchElementException();
            }
            itemValid = false;
            return currentItem;
        }

        /**
         * Called for finding the next item, if any.
         *
         * @return True, if an next item was found, otherwise false.
         * @throws IOException An I/O error occurred.
         */
        private boolean findNextItem() throws IOException {
            if (eof) { // skip
                return false;
            }
            if (currentItem != null) { // skip
                currentItem.close();
                currentItem = null;
            }
            for (;;) {
                boolean nextPart;
                if (skipPreamble) {
                    nextPart = multi.skipPreamble();
                } else {
                    nextPart = multi.readBoundary();
                }
                if (!nextPart) { // skip
                    if (currentFieldName == null) {
                        // Outer multipart terminated -> No more data
                        eof = true;
                        return false;
                    }
                    // Inner multipart terminated -> Return to parsing the outer
                    multi.setBoundary(boundary);
                    currentFieldName = null;
                    continue;
                }
                FileItemHeaders headers = getParsedHeaders(multi.readHeaders());
                if (currentFieldName == null) {
                    // 1. We're parsing the outer multipart
                    String fieldName = getFieldName(headers);
                    if (fieldName != null) {
                        // 2.
                        String subContentType = headers.getHeader(CONTENT_TYPE);
                        if (subContentType != null && subContentType.toLowerCase(Locale.ENGLISH).startsWith(MULTIPART_MIXED)) { // skip
                            currentFieldName = fieldName;
                            // Multiple files associated with this field name
                            byte[] subBoundary = getBoundary(subContentType);
                            multi.setBoundary(subBoundary);
                            skipPreamble = true;
                            continue;
                        }
                        // 3.
                        String fileName = getFileName(headers);
                        // 4...
                        currentItem = new FileItemStreamImpl(fileName, fieldName, headers.getHeader(CONTENT_TYPE),
                                fileName == null, getContentLength(headers));
                        // 5.
                        currentItem.setHeaders(headers);
                        notifier.noteItem();
                        itemValid = true;
                        return true;
                    }
                } else {
                    String fileName = getFileName(headers);
                    if (fileName != null) {
                        currentItem = new FileItemStreamImpl(fileName,
                                currentFieldName,
                                headers.getHeader(CONTENT_TYPE),
                                false, getContentLength(headers));
                        currentItem.setHeaders(headers);
                        notifier.noteItem();
                        itemValid = true;
                        return true;
                    }
                }
                multi.discardBodyData();
            }
        }

        /**
         * Default implementation of {@link FileItemStream}.
         */
        class FileItemStreamImpl implements FileItemStream {

            /**
             * The file items content type.
             */
            private final String contentType;

            /**
             * The file items field name.
             */
            private final String fieldName;

            /**
             * The file items file name.
             */
            private final String name;

            /**
             * Whether the file item is a form field.
             */
            private final boolean formField;

            /**
             * The file items input stream.
             */
            private final InputStream stream;

            /**
             * The headers, if any.
             */
            private FileItemHeaders headers;

            /**
             * Creates a new instance.
             *
             * @param pName The items file name, or null.
             * @param pFieldName The items field name.
             * @param pContentType The items content type, or null.
             * @param pFormField Whether the item is a form field.
             * @param pContentLength The items content length, if known, or -1
             * @throws IOException Creating the file item failed.
             */
            FileItemStreamImpl(String pName, String pFieldName,
                    String pContentType, boolean pFormField,
                    long pContentLength) throws IOException {
                name = pName;
                fieldName = pFieldName;
                contentType = pContentType;
                formField = pFormField;
                if (fileSizeMax != -1) { // Check if limit is already exceeded
                    if (pContentLength != -1
                            && pContentLength > fileSizeMax) {
                        FileSizeLimitExceededException e =
                                new FileSizeLimitExceededException(
                                        String.format("The field %s exceeds its maximum permitted size of %s bytes.",
                                                       fieldName, Long.valueOf(fileSizeMax)),
                                        pContentLength, fileSizeMax);
                        e.setFileName(pName);
                        e.setFieldName(pFieldName);
                        throw new FileUploadIOException(e);
                    }
                }
                // 1. OK to construct stream now
                final ItemInputStream itemStream = multi.newInputStream();
                InputStream istream = itemStream;
                if (fileSizeMax != -1) {
                    // 2.
                    istream = new LimitedInputStream(istream, fileSizeMax) {
                        @Override
                        protected void raiseError(long pSizeMax, long pCount)
                                throws IOException {
                            itemStream.close(true);
                            FileSizeLimitExceededException e =
                                new FileSizeLimitExceededException(
                                    String.format("The field %s exceeds its maximum permitted size of %s bytes.",
                                           fieldName, Long.valueOf(pSizeMax)),
                                    pCount, pSizeMax);
                            e.setFieldName(fieldName);
                            e.setFileName(name);
                            throw new FileUploadIOException(e);
                        }
                    };
                }
                // 3.
                stream = istream;
            }

        }

    }

}












 







 




 











 
 





 



 




 







 




 




















 

































 








 










 












 





 











 






 



 






















 























 





































 



 








 

 


 























































































 



 
















 







1. ServletRequestContext
package org.apache.tomcat.util.http.fileupload.servlet;

/**
 * <p>Provides access to the request information needed for a request made to
 * an HTTP servlet.</p>
 *
 * @since FileUpload 1.1
 */
public class ServletRequestContext implements UploadContext {

    // ----------------------------------------------------- Instance Variables

    /**
     * The request for which the context is being provided.
     */
    private final HttpServletRequest request;

    // ----------------------------------------------------------- Constructors

    /**
     * Construct a context for this request.
     *
     * @param request The request to which this context applies.
     */
    public ServletRequestContext(HttpServletRequest request) {
        this.request = request;
    }

    // --------------------------------------------------------- Public Methods

    /**
     * Retrieve the character encoding for the request.
     *
     * @return The character encoding for the request.
     */
    @Override
    public String getCharacterEncoding() {
        return request.getCharacterEncoding();
    }

    /**
     * Retrieve the content type of the request.
     *
     * @return The content type of the request.
     */
    @Override
    public String getContentType() {
        return request.getContentType();
    }

    /**
     * Retrieve the content length of the request.
     *
     * @return The content length of the request.
     * @since 1.3
     */
    @Override
    public long contentLength() {
        long size;
        try {
            size = Long.parseLong(request.getHeader(FileUploadBase.CONTENT_LENGTH));
        } catch (NumberFormatException e) {
            size = request.getContentLength();
        }
        return size;
    }

    /**
     * Retrieve the input stream for the request.
     *
     * @return The input stream for the request.
     *
     * @throws IOException if a problem occurs.
     */
    @Override
    public InputStream getInputStream() throws IOException {
        return request.getInputStream();
    }

}















































 












 















 



2. DiskFileItemFactory
package org.apache.tomcat.util.http.fileupload.disk;

public class DiskFileItemFactory implements FileItemFactory {

    /**
     * Create a new {@link DiskFileItem}
     * instance from the supplied parameters and the local factory
     * configuration.
     *
     * @param fieldName   The name of the form field.
     * @param contentType The content type of the form field.
     * @param isFormField <code>true</code> if this is a plain form field;
     *                    <code>false</code> otherwise.
     * @param fileName    The name of the uploaded file, if any, as supplied
     *                    by the browser or other client.
     *
     * @return The newly created file item.
     */
    @Override
    public FileItem createItem(String fieldName, String contentType, boolean isFormField, String fileName) {
        DiskFileItem result = new DiskFileItem(fieldName, contentType, isFormField, fileName, sizeThreshold, repository);
        result.setDefaultCharset(defaultCharset);
        return result;
    }

}




















 





3. StandardSvltMultipartResolver

// Servlet3.0后的上传组件。不常用
public class StandardServletMultipartResolver implements MultipartResolver {

    @Override
    public boolean isMultipart(HttpServletRequest request) {
        return StringUtils.startsWithIgnoreCase(request.getContentType(), "multipart/");
    }

    @Override
    public MultipartHttpServletRequest resolveMultipart(HttpServletRequest request) throws MultipartException {
        return new StandardMultipartHttpServletRequest(request, this.resolveLazily);
    }

}





 




 



4. 三种Controller

1. implements Controller

public class Ctl1 implements Controller {

    @Override
    public ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response) throws Exception {
        System.out.println("Ctl1 ......");
        return null;
    }

}
 








<bean name="/ctl1" class="com.listao.controller.ctl.Ctl1"/>

2. implements HttpReqHandler

public class Ctl2 implements HttpRequestHandler {

    @Override
    public void handleRequest(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        System.out.println("Ctl2 ......");
    }

}
 







<bean name="/ctl2" class="com.listao.controller.ctl.Ctl2"/>

3. @Controller

@Controller
public class Ctl3 {

    @RequestMapping("/ctl3")
    public void ctl3() {
        System.out.println("Ctl3 ......");
    }

}

5. getHandler()

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
 
 

@SuppressWarnings("serial")
public class DispatcherServlet extends FrameworkServlet {

    /**
     * 处理器匹配器,返回请求对应的处理器和拦截器
     * List of HandlerMappings used by this servlet.
     */
    @Nullable
    private List<HandlerMapping> handlerMappings;

    /**
     * Detect all HandlerMappings or just expect "handlerMapping" bean?.
     */
    private boolean detectAllHandlerMappings = true;

    /**
     * Initialize the HandlerMappings used by this class.
     * <p>If no HandlerMapping beans are defined in the BeanFactory for this namespace,
     * we default to BeanNameUrlHandlerMapping.
     */
    private void initHandlerMappings(ApplicationContext context) {
        // 将handlerMappings置空
        this.handlerMappings = null;

        // 1. 如果开启探测功能,则扫描已注册的HandlerMapping的bean,添加到handlerMappings中
        if (this.detectAllHandlerMappings) {
            // Find all HandlerMappings in the ApplicationContext, including ancestor contexts.
            // 扫描已注册的handlerMapping的bean
            Map<String, HandlerMapping> matchingBeans =
                    BeanFactoryUtils.beansOfTypeIncludingAncestors(context, HandlerMapping.class, true, false);
            // 添加到handlerMappings中,并进行排序
            if (!matchingBeans.isEmpty()) {
                this.handlerMappings = new ArrayList<>(matchingBeans.values());
                // We keep HandlerMappings in sorted order. 可以定义多种视图,按照优先级进行排序操作
                AnnotationAwareOrderComparator.sort(this.handlerMappings);
            }
        }
        // 2. 如果关闭探测功能,则获取Bean名称为handlerMapping对应的bean,将其添加到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.
        // 3. 如果未获得到,则获得默认配置的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");
            }
        }
    }

    /**
     * 返回当前request匹配的interceptor和handler
     * Return the HandlerExecutionChain for this request.
     * <p>Tries all handler mappings in order.
     *
     * @param request current HTTP request
     * @return the HandlerExecutionChain, or {@code null} if no handler could be found
     */
    @Nullable
    protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
        if (this.handlerMappings != null) {
            for (HandlerMapping mapping : this.handlerMappings) {
                // 1... AbstractHandlerMapping
                HandlerExecutionChain handler = mapping.getHandler(request);
                if (handler != null) {
                    return handler;
                }
            }
        }
        return null;
    }

}





























 










 










 




















 









1. HandlerMapping

  • 通过接口学习组件
image-20240114095054483
image-20240112085945360
/**
 * 根据Request找到响应的处理器handler和interceptors
 * <p>
 * Interface to be implemented by objects that define a mapping between
 * requests and handler objects.
 *
 * <p>This class can be implemented by application developers, although this is not
 * necessary, as {@link org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping}
 * and {@link org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping}
 * are included in the framework. The former is the default if no
 * HandlerMapping bean is registered in the application context.
 *
 * <p>HandlerMapping implementations can support mapped interceptors but do not
 * have to. A handler will always be wrapped in a {@link HandlerExecutionChain}
 * instance, optionally accompanied by some {@link HandlerInterceptor} instances.
 * The DispatcherServlet will first call each HandlerInterceptor's
 * {@code preHandle} method in the given order, finally invoking the handler
 * itself if all {@code preHandle} methods have returned {@code true}.
 *
 * <p>The ability to parameterize this mapping is a powerful and unusual
 * capability of this MVC framework. For example, it is possible to write
 * a custom mapping based on session state, cookie state or many other
 * variables. No other MVC framework seems to be equally flexible.
 *
 * <p>Note: Implementations can implement the {@link org.springframework.core.Ordered}
 * interface to be able to specify a sorting order and thus a priority for getting
 * applied by DispatcherServlet. Non-Ordered instances get treated as lowest priority.
 *
 * @author Rod Johnson
 * @author Juergen Hoeller
 * @see org.springframework.core.Ordered
 * @see org.springframework.web.servlet.handler.AbstractHandlerMapping
 * @see org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping
 * @see org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping
 */
public interface HandlerMapping {

    String BEST_MATCHING_HANDLER_ATTRIBUTE = HandlerMapping.class.getName() + ".bestMatchingHandler";

    String LOOKUP_PATH = HandlerMapping.class.getName() + ".lookupPath";

    String PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE = HandlerMapping.class.getName() + ".pathWithinHandlerMapping";

    String BEST_MATCHING_PATTERN_ATTRIBUTE = HandlerMapping.class.getName() + ".bestMatchingPattern";

    String INTROSPECT_TYPE_LEVEL_MAPPING = HandlerMapping.class.getName() + ".introspectTypeLevelMapping";

    String URI_TEMPLATE_VARIABLES_ATTRIBUTE = HandlerMapping.class.getName() + ".uriTemplateVariables";

    String MATRIX_VARIABLES_ATTRIBUTE = HandlerMapping.class.getName() + ".matrixVariables";

    String PRODUCIBLE_MEDIA_TYPES_ATTRIBUTE = HandlerMapping.class.getName() + ".producibleMediaTypes";

    @Nullable
    HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception;

}
































 
 























2. ApplicationObjectSupport

public abstract class ApplicationObjectSupport implements ApplicationContextAware {

	/** Logger that is available to subclasses. */
	protected final Log logger = LogFactory.getLog(getClass());

	/** ApplicationContext this object runs in. */
	@Nullable
	private ApplicationContext applicationContext;

	@Override
	public final void setApplicationContext(@Nullable ApplicationContext context) throws BeansException {
		if (context == null && !isContextRequired()) {
			// Reset internal context state.
			this.applicationContext = null;
			this.messageSourceAccessor = null;
		}
		else if (this.applicationContext == null) {
			// Initialize with passed-in context.
			if (!requiredContextClass().isInstance(context)) {
				throw new ApplicationContextException(
						"Invalid application context: needs to be of type [" + requiredContextClass().getName() + "]");
			}
			this.applicationContext = context;
			this.messageSourceAccessor = new MessageSourceAccessor(context);
            // 1... WebApplicationObjectSupport
			initApplicationContext(context);
		}
		else {
			// Ignore reinitialization if same context passed in.
			if (this.applicationContext != context) {
				throw new ApplicationContextException(
						"Cannot reinitialize with different application context: current one is [" +
						this.applicationContext + "], passed-in one is [" + context + "]");
			}
		}
	}

	protected void initApplicationContext(ApplicationContext context) throws BeansException {
        // 1...
        //      BeanNameUrlHandlerMapping => AbstractDetectingUrlHandlerMapping
        //      RequestMappingHandlerMapping => AbstractHandlerMapping
		initApplicationContext();
	}

	protected void initApplicationContext() throws BeansException {
	}

}
 
























 















 






3. WebApplicationObjectSupport

public abstract class WebApplicationObjectSupport extends ApplicationObjectSupport implements ServletContextAware {

	@Nullable
	private ServletContext servletContext;

	/**
	 * Calls {@link #initServletContext(javax.servlet.ServletContext)} if the
	 * given ApplicationContext is a {@link WebApplicationContext}.
	 */
	@Override
	protected void initApplicationContext(ApplicationContext context) {
        // 1... ApplicationObjectSupport
		super.initApplicationContext(context);
		if (this.servletContext == null && context instanceof WebApplicationContext) {
			this.servletContext = ((WebApplicationContext) context).getServletContext();
			if (this.servletContext != null) {
				initServletContext(this.servletContext);
			}
		}
	}

}












 









4. AbstractHandlerMapping

public abstract class AbstractHandlerMapping extends WebApplicationObjectSupport
        implements HandlerMapping, Ordered, BeanNameAware {

    // 默认处理器,使用的对象类型是Object,子类实现的时候使用HandlerMethod,HandlerExecutionChain等
    @Nullable
    private Object defaultHandler;

    // url路径管理工具
    private UrlPathHelper urlPathHelper = new UrlPathHelper();

    // 基于ant进行path匹配,解决/user/{id}的场景
    private PathMatcher pathMatcher = new AntPathMatcher();

    // 初始化后的拦截器handlerInterceptor数组
    private final List<HandlerInterceptor> adaptedInterceptors = new ArrayList<>();

    /**
     * 用于配置springmvc的拦截器,有两种设置方式,
     * 1. 注册handlerMapping时通过属性设置
     * 2. 通过子类的extendInterceptors钩子方法进行设置
     * <p>
     * 此集合并不会直接使用,而是通过initInterceptors方法按照类型分配到mappedInterceptors和adaptedInterceptors中进行使用
     */
    private final List<Object> interceptors = new ArrayList<>();

	/**
     * Initializes the interceptors.
     *
     * @see #extendInterceptors(java.util.List)
     * @see #initInterceptors()
     */
    @Override
    protected void initApplicationContext() throws BeansException {
        // 1.. 空实现,交给子类实现,用于注册自定义的拦截器到interceptors中,目前暂无子类实现
        extendInterceptors(this.interceptors);
        // 2.. 扫描已注册的MappedInterceptor的Bean们,添加到adaptedInterceptors中
        detectMappedInterceptors(this.adaptedInterceptors);
        // 3.. 将interceptors初始化成 HandlerInterceptor类型,添加到adaptedInterceptors中
        initInterceptors();
    }

    protected void extendInterceptors(List<Object> interceptors) {
    }

    protected void detectMappedInterceptors(List<HandlerInterceptor> mappedInterceptors) {
        // 扫描已注册的MappedInterceptor的Bean,添加到mappedInterceptors中
        // 1. MappedInterceptor会根据请求路径做匹配,是否进行拦截
        mappedInterceptors.addAll(BeanFactoryUtils.beansOfTypeIncludingAncestors(
                obtainApplicationContext(), MappedInterceptor.class, true, false).values());
    }

    protected void initInterceptors() {
        if (!this.interceptors.isEmpty()) {
            for (int i = 0; i < this.interceptors.size(); i++) {
                Object interceptor = this.interceptors.get(i);
                if (interceptor == null) {
                    throw new IllegalArgumentException("Entry number " + i + " in interceptors array is null");
                }
                // 1. 将interceptors初始化成HandlerInterceptor类型,添加到adaptedInterceptors中
                // 注意,HandlerInterceptor无需进行路径匹配,直接拦截全部
                this.adaptedInterceptors.add(adaptInterceptor(interceptor));
            }
        }
    }

    /**
     * Look up a handler for the given request, falling back to the default
     * handler if no specific one is found.
     *
     * @param request current HTTP request
     * @return the corresponding handler instance, or the default handler
     * @see #getHandlerInternal
     */
    @Override
    @Nullable
    public final HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
        // 1... AbstractUrlHandlerMapping,RequestMappingInfoHandlerMapping。获得处理器(HandlerMethod、HandlerExecutionChain)
        Object handler = getHandlerInternal(request);
        // 获得不到,则使用默认处理器
        if (handler == null) {
            handler = getDefaultHandler();
        }
        // 99. 还是获得不到,则返回null
        if (handler == null) {
            return null;
        }
        // Bean name or resolved handler?
        // 如果找到的处理器是String类型,则从Spring容器中找到对应的Bean作为处理器
        if (handler instanceof String) {
            String handlerName = (String) handler;
            handler = obtainApplicationContext().getBean(handlerName);
        }

        // 2.. 创建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);
        }

        return executionChain;
    }

    protected HandlerExecutionChain getHandlerExecutionChain(Object handler, HttpServletRequest request) {
        // xmlController 已经封装 HandlerExecutionChain。@Controller 未封装
        HandlerExecutionChain chain = (handler instanceof HandlerExecutionChain ?
                (HandlerExecutionChain) handler : new HandlerExecutionChain(handler));

        // 请求路径
        String lookupPath = this.urlPathHelper.getLookupPathForRequest(request, LOOKUP_PATH);
        // 1. 添加拦截器
        for (HandlerInterceptor interceptor : this.adaptedInterceptors) {
            // 需要匹配,若路径匹配,则添加到 chain 中
            if (interceptor instanceof MappedInterceptor) {
                MappedInterceptor mappedInterceptor = (MappedInterceptor) interceptor;
                // 2. 拦截器、请求匹配
                if (mappedInterceptor.matches(lookupPath, this.pathMatcher)) {
                    chain.addInterceptor(mappedInterceptor.getInterceptor());
                }
            }
            // 无需匹配,直接添加到 chain 中
            else {
                chain.addInterceptor(interceptor);
            }
        }
        return chain;
    }

}




































 

 









 











 
















 
















 






















 


 






 
 











1. getHandlerExecutionChain()

  • <bean name="/ctl1" class="com.listao.controller.ctl.Ctl1"/>只能写SpringMVC配置文件中
<bean name="/ctl1" class="com.listao.controller.ctl.Ctl1"/>

<mvc:interceptors>
    <!-- 拦截具体请求 -->
    <mvc:interceptor>
        <mvc:mapping path="/ctl1"/>
        <bean class="com.listao.interceptor.HandlerMappingInterceptor"/>
    </mvc:interceptor>
    <!-- 拦截所有请求 -->
    <bean class="com.listao.interceptor.HandlerMappingInterceptor1"/>
</mvc:interceptors>

2. UrlPathHelper

public class UrlPathHelper {

	private boolean alwaysUseFullPath = false;

	public String getLookupPathForRequest(HttpServletRequest request) {
        // 1..
		String pathWithinApp = getPathWithinApplication(request);
		// Always use full path within current servlet context?
		if (this.alwaysUseFullPath) {
			return pathWithinApp;
		}
		// Else, use path within current servlet mapping if applicable
		String rest = getPathWithinServletMapping(request, pathWithinApp);
		if (StringUtils.hasLength(rest)) { // skip
			return rest;
		}
		else {
		    // 2.
			return pathWithinApp;
		}
	}

	public String getPathWithinApplication(HttpServletRequest request) {
		String contextPath = getContextPath(request);   // `/spring_mymvc`
		String requestUri = getRequestUri(request);     // `/spring_mymvc/userList`
		// 1..
		String path = getRemainingPath(requestUri, contextPath, true); // `/userList`
		if (path != null) {
			// Normal case: URI contains context path.
			return (StringUtils.hasText(path) ? path : "/");
		}
		else {
			return requestUri;
		}
	}

	@Nullable
	private String getRemainingPath(String requestUri, String mapping, boolean ignoreCase) {
		int index1 = 0;
		int index2 = 0;
		for (; (index1 < requestUri.length()) && (index2 < mapping.length()); index1++, index2++) {
			char c1 = requestUri.charAt(index1);
			char c2 = mapping.charAt(index2);
			if (c1 == ';') {
				index1 = requestUri.indexOf('/', index1);
				if (index1 == -1) {
					return null;
				}
				c1 = requestUri.charAt(index1);
			}
			if (c1 == c2 || (ignoreCase && (Character.toLowerCase(c1) == Character.toLowerCase(c2)))) {
				continue;
			}
			return null;
		}
		if (index2 != mapping.length()) {
			return null;
		}
		else if (index1 == requestUri.length()) {
			return "";
		}
		else if (requestUri.charAt(index1) == ';') {
			index1 = requestUri.indexOf('/', index1);
		}
		return (index1 != -1 ? requestUri.substring(index1) : "");
	}

}






 
















 
 

 









































6. BeanNameUrlHandlerMapping

public class BeanNameUrlHandlerMapping extends AbstractDetectingUrlHandlerMapping {

    /**
     * Checks name and aliases of the given bean for URLs, starting with "/".
     */
    @Override
    protected String[] determineUrlsForHandler(String beanName) {
        List<String> urls = new ArrayList<>();
        // 如果是以 / 开头,添加到 urls
        if (beanName.startsWith("/")) {
            urls.add(beanName);
        }
        // 获得 beanName 的别名们,如果以 / 开头,则添加到 urls
        String[] aliases = obtainApplicationContext().getAliases(beanName);
        for (String alias : aliases) {
            if (alias.startsWith("/")) {
                urls.add(alias);
            }
        }
        return StringUtils.toStringArray(urls);
    }

}









 













1. AbsUrlHandlerMapping

public abstract class AbstractUrlHandlerMapping extends AbstractHandlerMapping implements MatchableHandlerMapping {

    /**
     * 是否延迟加载处理器,默认关闭
     */
    private boolean lazyInitHandlers = false;

    /**
     * 路径和处理器的映射
     * <p>
     * KEY:路径 lookupHandler(String, HttpServletRequest)
     */
    private final Map<String, Object> handlerMap = new LinkedHashMap<>();


    protected void registerHandler(String[] urlPaths, String beanName) throws BeansException, IllegalStateException {
        Assert.notNull(urlPaths, "URL path array must not be null");
        // 调用另外一个重载方法完成注册
        for (String urlPath : urlPaths) {
            // 1..
            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.
        // 如果handler是String类型并且没有设置lazyInitHandlers,则从springmvc容器中获取handler
        if (!this.lazyInitHandlers && handler instanceof String) {
            String handlerName = (String) handler;
            ApplicationContext applicationContext = obtainApplicationContext();
            if (applicationContext.isSingleton(handlerName)) {
                // 1. IOC#bean
                resolvedHandler = applicationContext.getBean(handlerName);
            }
        }

        // 是否已经存在对应的handler
        Object mappedHandler = this.handlerMap.get(urlPath);
        // 检验mappedHandler是否已存在,如果已存在,并且不是当前resolvedHandler对象,则抛出异常
        if (mappedHandler != null) {
            if (mappedHandler != resolvedHandler) {
                throw new IllegalStateException(
                        "Cannot map " + getHandlerDescription(handler) + " to URL path [" + urlPath +
                                "]: There is already " + getHandlerDescription(mappedHandler) + " mapped.");
            }
        } else {
            // 如果是/根路径,则设置为rootHandler
            if (urlPath.equals("/")) {
                if (logger.isTraceEnabled()) {
                    logger.trace("Root mapping to " + getHandlerDescription(handler));
                }
                // "/"设置为rootHandler
                setRootHandler(resolvedHandler);
            }
            // 如果是/*路径,则设置为默认处理器
            else if (urlPath.equals("/*")) {
                if (logger.isTraceEnabled()) {
                    logger.trace("Default mapping to " + getHandlerDescription(handler));
                }
                // 对"/*"的匹配设置默认的handler
                setDefaultHandler(resolvedHandler);
            } else {
                // 2. 其余的路径绑定关系则存入handlerMap中
                this.handlerMap.put(urlPath, resolvedHandler);
                if (logger.isTraceEnabled()) {
                    logger.trace("Mapped [" + urlPath + "] onto " + getHandlerDescription(handler));
                }
            }
        }
    }

    // --------------------------------------------------------------------------------

    /**
     * Look up a handler for the URL path of the given request.
     *
     * @param request current HTTP request
     * @return the handler instance, or {@code null} if none found
     */
    @Override
    @Nullable
    protected Object getHandlerInternal(HttpServletRequest request) throws Exception {
        // 1... UrlPathHelper 截取用于匹配的URL有效路径
        String lookupPath = getUrlPathHelper().getLookupPathForRequest(request);
        request.setAttribute(LOOKUP_PATH, lookupPath);
        // 2.. 根据路径寻找handler,此处并不是直接从map中获取,很多handler都有通配符的写法,甚至有多个匹配项,此时需要做好选择
        Object handler = lookupHandler(lookupPath, request);
        // 如果找不到处理器,则使用rootHandler或defaultHandler处理器
        if (handler == null) { // skip
            // 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;
            // 如果是根路径,则使用rootHandler处理器
            if (StringUtils.matchesCharacter(lookupPath, '/')) {
                // 如果请求的路径仅仅是“/”,那么使用RootHandler进行处理
                rawHandler = getRootHandler();
            }
            // 如果无法找到handler,则使用默认的handler
            if (rawHandler == null) {
                rawHandler = getDefaultHandler();
            }
            if (rawHandler != null) {
                // Bean name or resolved handler?
                // 如果找到的处理器是String类型,则从容器中找到该beanName对应的Bean作为处理器
                if (rawHandler instanceof String) {
                    String handlerName = (String) rawHandler;
                    rawHandler = obtainApplicationContext().getBean(handlerName);
                }
                // 空方法,校验处理器。目前暂无子类实现该方法
                validateHandler(rawHandler, request);
                // 创建处理器(HandlerExecutionChain对象)
                handler = buildPathExposingHandler(rawHandler, lookupPath, lookupPath, null);
            }
        }
        return handler;
    }

    @Nullable
    protected Object lookupHandler(String urlPath, HttpServletRequest request) throws Exception {
        // Direct match?
        // 1. 直接根据url进行查找handler
        Object handler = this.handlerMap.get(urlPath);
        if (handler != null) {
            // Bean name or resolved handler?
            // 如果找到的处理器是String类型,则从容器中找到该beanName对应的Bean作为处理器
            if (handler instanceof String) {
                String handlerName = (String) handler;
                handler = obtainApplicationContext().getBean(handlerName);
            }
            // 空方法,校验处理器。目前暂无子类实现该方法
            validateHandler(handler, request);
            // 2.. 创建处理器
            return buildPathExposingHandler(handler, urlPath, urlPath, null);
        }

        // Pattern match?
        // 通过表达式进行匹配具体通过antPathMatcher实现
        List<String> matchingPatterns = new ArrayList<>();
        // 情况二,Pattern匹配合适的,并添加到 matchingPatterns 中
        for (String registeredPattern : this.handlerMap.keySet()) {
            if (getPathMatcher().match(registeredPattern, urlPath)) { // match
                // 路径通过Pattern匹配成功
                matchingPatterns.add(registeredPattern);
            } else if (useTrailingSlashMatch()) {
                if (!registeredPattern.endsWith("/") && getPathMatcher().match(registeredPattern + "/", urlPath)) {
                    matchingPatterns.add(registeredPattern + "/");
                }
            }
        }

        // 获得首个匹配(最优)的结果
        String bestMatch = null;
        Comparator<String> patternComparator = getPathMatcher().getPatternComparator(urlPath);
        if (!matchingPatterns.isEmpty()) { // skip
            // 排序
            matchingPatterns.sort(patternComparator);
            if (logger.isTraceEnabled() && matchingPatterns.size() > 1) {
                logger.trace("Matching patterns " + matchingPatterns);
            }
            bestMatch = matchingPatterns.get(0);
        }
        if (bestMatch != null) { // skip
            // 获得bestMatch对应的处理器
            handler = this.handlerMap.get(bestMatch);
            if (handler == null) {
                if (bestMatch.endsWith("/")) {
                    handler = this.handlerMap.get(bestMatch.substring(0, bestMatch.length() - 1));
                }
                // 如果获得不到,抛出IllegalStateException异常
                if (handler == null) {
                    throw new IllegalStateException(
                            "Could not find handler for best pattern match [" + bestMatch + "]");
                }
            }
            // Bean name or resolved handler?
            // 如果找到的处理器是String类型,则从容器中找到该beanName对应的Bean作为处理器
            if (handler instanceof String) {
                String handlerName = (String) handler;
                handler = obtainApplicationContext().getBean(handlerName);
            }
            // 空方法,校验处理器。目前暂无子类实现该方法
            validateHandler(handler, request);
            // 获得最匹配的路径
            String pathWithinMapping = getPathMatcher().extractPathWithinPattern(bestMatch, urlPath);

            // There might be multiple 'best patterns', let's make sure we have the correct URI template variables
            // for all of them
            // 通过sort方法进行排序,第一个作为bestPatternMatch,不过有可能有多个pattern的顺序相同,也就是sort方法返回0,这里就是处理这种情况
            Map<String, String> uriTemplateVariables = new LinkedHashMap<>();
            for (String matchingPattern : matchingPatterns) {
                if (patternComparator.compare(bestMatch, matchingPattern) == 0) {
                    Map<String, String> vars = getPathMatcher().extractUriTemplateVariables(matchingPattern, urlPath);
                    Map<String, String> decodedVars = getUrlPathHelper().decodePathVariables(request, vars);
                    uriTemplateVariables.putAll(decodedVars);
                }
            }
            if (logger.isTraceEnabled() && uriTemplateVariables.size() > 0) {
                logger.trace("URI variables " + uriTemplateVariables);
            }
            // 创建处理器
            return buildPathExposingHandler(handler, bestMatch, pathWithinMapping, uriTemplateVariables);
        }

        // No handler found...
        return null;
    }

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

        // 1. 创建HandlerExecutionChain对象
        HandlerExecutionChain chain = new HandlerExecutionChain(rawHandler);
        // 2. 添加PathExposingHandlerInterceptor拦截器,到chain中
        chain.addInterceptor(new PathExposingHandlerInterceptor(bestMatchingPattern, pathWithinMapping));
        if (!CollectionUtils.isEmpty(uriTemplateVariables)) {
            // 3. 添加UriTemplateVariablesHandlerInterceptor拦截器,到chain中
            chain.addInterceptor(new UriTemplateVariablesHandlerInterceptor(uriTemplateVariables));
        }
        return chain;
    }

    private class PathExposingHandlerInterceptor extends HandlerInterceptorAdapter {

		// 最佳匹配的路径
        private final String bestMatchingPattern;

        // 被匹配的路径
        private final String pathWithinMapping;

        public PathExposingHandlerInterceptor(String bestMatchingPattern, String pathWithinMapping) {
            this.bestMatchingPattern = bestMatchingPattern;
            this.pathWithinMapping = pathWithinMapping;
        }

        @Override
        public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
            exposePathWithinMapping(this.bestMatchingPattern, this.pathWithinMapping, request);
            request.setAttribute(BEST_MATCHING_HANDLER_ATTRIBUTE, handler);
            request.setAttribute(INTROSPECT_TYPE_LEVEL_MAPPING, supportsTypeLevelMappings());
            return true;
        }

    }

    private class UriTemplateVariablesHandlerInterceptor extends HandlerInterceptorAdapter {

        private final Map<String, String> uriTemplateVariables;

        public UriTemplateVariablesHandlerInterceptor(Map<String, String> uriTemplateVariables) {
            this.uriTemplateVariables = uriTemplateVariables;
        }

        @Override
        public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
            exposeUriTemplateVariables(this.uriTemplateVariables, request);
            return true;
        }

    }

}












 







 















 






























 



















 


 


































 










 














































































 

 


 












































2. AbsDetectingUrlHandlerMapping

public abstract class AbstractDetectingUrlHandlerMapping extends AbstractUrlHandlerMapping {

	/**
	 * Calls the {@link #detectHandlers()} method in addition to the
	 * superclass's initialization.
	 */
	@Override
	public void initApplicationContext() throws ApplicationContextException {
	    // 1... AbstractHandlerMapping
		super.initApplicationContext();
		// 2.. 自动探测处理器
		detectHandlers();
	}

    protected void detectHandlers() throws BeansException {
        // 从spring上下文获取所有Object类型的bean名称
        ApplicationContext applicationContext = obtainApplicationContext();
        // 1. 获取容器中所有bean的名字
        String[] beanNames = (this.detectHandlersInAncestorContexts ?
                BeanFactoryUtils.beanNamesForTypeIncludingAncestors(applicationContext, Object.class) :
                applicationContext.getBeanNamesForType(Object.class));

        // Take any bean name that we can determine URLs for.
        // 对每一个beanName解析url,如果能解析到就注册到父类的map中
        for (String beanName : beanNames) {
            // 2... BeanNameUrlHandlerMapping 使用beanName解析url,模板方法,有子类实现
            String[] urls = determineUrlsForHandler(beanName);
            // 如果该bean存在对应的url,则添加该处理器
            if (!ObjectUtils.isEmpty(urls)) {
                // URL paths found: Let's consider it a handler.
                // 3... AbstractUrlHandlerMapping 调用父类的方法,往handlerMap中添加注册器
                registerHandler(urls, beanName);
            }
        }

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

}









 

 






 







 




 









7. RequestMappingHandlerMapping

public class RequestMappingHandlerMapping extends RequestMappingInfoHandlerMapping
        implements MatchableHandlerMapping, EmbeddedValueResolverAware {

    /**
     * RequestMappingInfo 的构建器
     */
    private RequestMappingInfo.BuilderConfiguration config = new RequestMappingInfo.BuilderConfiguration();

    @Override
    @SuppressWarnings("deprecation")
    public void afterPropertiesSet() {
        // 构建 RequestMappingInfo.BuilderConfiguration 对象
        this.config = new RequestMappingInfo.BuilderConfiguration();
        // 设置urlPathHelper默认为UrlPathHelper.class
        this.config.setUrlPathHelper(getUrlPathHelper());
        // 默认为AntPathMatcher,路径匹配校验器
        this.config.setPathMatcher(getPathMatcher());
        // 是否支持后缀补充,默认为true
        this.config.setSuffixPatternMatch(useSuffixPatternMatch());
        // 是否添加"/"后缀,默认为true
        this.config.setTrailingSlashMatch(useTrailingSlashMatch());
        // 是否采用mediaType匹配模式,比如.json/ .xml模式的匹配,默认为false
        this.config.setRegisteredSuffixPatternMatch(useRegisteredSuffixPatternMatch());
        // mediaType处理类
        this.config.setContentNegotiationManager(getContentNegotiationManager());

        // 1... AbstractHandlerMethodMapping
        super.afterPropertiesSet();
    }

    /**
     * Uses method and type-level @{@link RequestMapping} annotations to create
     * the RequestMappingInfo.
     *
     * @return the created RequestMappingInfo, or {@code null} if the method
     * does not have a {@code @RequestMapping} annotation.
     * @see #getCustomMethodCondition(Method)
     * @see #getCustomTypeCondition(Class)
     */
    @Override
    @Nullable
    protected RequestMappingInfo getMappingForMethod(Method method, Class<?> handlerType) {
        // 1.. @RequestMapping创建RequestMappingInfo
        RequestMappingInfo info = createRequestMappingInfo(method);
        if (info != null) {
            // 基于类上的 @RequestMapping 注解,合并进去
            RequestMappingInfo typeInfo = createRequestMappingInfo(handlerType);
            if (typeInfo != null) {
                info = typeInfo.combine(info);
            }
            // 如果有前缀,则设置到 info 中
            String prefix = getPathPrefix(handlerType);
            if (prefix != null) {
                info = RequestMappingInfo.paths(prefix).options(this.config).build().combine(info);
            }
        }
        return info;
    }

    /**
     * Delegates to {@link #createRequestMappingInfo(RequestMapping, RequestCondition)},
     * supplying the appropriate custom {@link RequestCondition} depending on whether
     * the supplied {@code annotatedElement} is a class or method.
     *
     * @see #getCustomTypeCondition(Class)
     * @see #getCustomMethodCondition(Method)
     */
    @Nullable
    private RequestMappingInfo createRequestMappingInfo(AnnotatedElement element) {
        // 1. @RequestMapping
        RequestMapping requestMapping = AnnotatedElementUtils.findMergedAnnotation(element, RequestMapping.class);
        // 获得自定义的条件。目前都是空方法,可以无视
        RequestCondition<?> condition = (element instanceof Class ?
                getCustomTypeCondition((Class<?>) element) : getCustomMethodCondition((Method) element));
        // 2..
        return (requestMapping != null ? createRequestMappingInfo(requestMapping, condition) : null);
    }

    /**
     * Create a {@link RequestMappingInfo} from the supplied
     * {@link RequestMapping @RequestMapping} annotation, which is either
     * a directly declared annotation, a meta-annotation, or the synthesized
     * result of merging annotation attributes within an annotation hierarchy.
     */
    protected RequestMappingInfo createRequestMappingInfo(
            RequestMapping requestMapping, @Nullable RequestCondition<?> customCondition) {

        // 1. 创建RequestMappingInfo.Builder对象,设置对应属性
        RequestMappingInfo.Builder builder = RequestMappingInfo
                .paths(resolveEmbeddedValuesInPatterns(requestMapping.path()))
                .methods(requestMapping.method())
                .params(requestMapping.params())
                .headers(requestMapping.headers())
                .consumes(requestMapping.consumes())
                .produces(requestMapping.produces())
                .mappingName(requestMapping.name());
        if (customCondition != null) {
            builder.customCondition(customCondition);
        }
        // 2. RequestMappingInfo
        return builder.options(this.config).build();
    }

    @Override
    protected void registerHandlerMethod(Object handler, Method method, RequestMappingInfo mapping) {
        // 1. AbstractHandlerMethodMapping
        super.registerHandlerMethod(handler, method, mapping);
        updateConsumesCondition(mapping, method);
    }

    private void updateConsumesCondition(RequestMappingInfo info, Method method) {
        ConsumesRequestCondition condition = info.getConsumesCondition();
        if (!condition.isEmpty()) {
            for (Parameter parameter : method.getParameters()) {
                // 1.
                MergedAnnotation<RequestBody> annot = MergedAnnotations.from(parameter).get(RequestBody.class);
                if (annot.isPresent()) {
                    condition.setBodyRequired(annot.getBoolean("required"));
                    break;
                }
            }
        }
    }

}



























 















 


 

 





















 




 













 










 





 
 







 









1. AbsHandlerMethodMapping

/**
 * 抽象的基础类通过map的映射关系来存储request和handlerMethod之间的关系
 * Abstract base class for {@link HandlerMapping} implementations that define
 * a mapping between a request and a {@link HandlerMethod}.
 *
 * <p>For each registered handler method, a unique mapping is maintained with
 * subclasses defining the details of the mapping type {@code <T>}.
 *
 * @param <T> the mapping for a {@link HandlerMethod} containing the conditions
 *            needed to match the handler method to an incoming request.
 *     此泛型类型用来代表匹配handler的条件专门使用的一种类,这里的条件不只是url,还可以是其他条件
 *         eg:请求方式,请求参数等都可以作为条件,默认使用的RequestMappingInfo
 * @since 3.1
 */
public abstract class AbstractHandlerMethodMapping<T> extends AbstractHandlerMapping implements InitializingBean {

    private static final String SCOPED_TARGET_NAME_PREFIX = "scopedTarget.";

    private final MappingRegistry mappingRegistry = new MappingRegistry();

    // Handler method detection

    /**
     * Detects handler methods at initialization.
     *
     * @see #initHandlerMethods
     */
    @Override
    public void afterPropertiesSet() {
        // 1.. 初始化处理器的方法们
        initHandlerMethods();
    }

    /**
     * Scan beans in the ApplicationContext, detect and register handler methods.
     *
     * @see #getCandidateBeanNames()
     * @see #processCandidateBean
     * @see #handlerMethodsInitialized
     */
    protected void initHandlerMethods() {
        // 遍历Bean,逐个处理
        for (String beanName : getCandidateBeanNames()) {
            // 排除目标代理类,AOP 相关,可查看注释
            if (!beanName.startsWith(SCOPED_TARGET_NAME_PREFIX)) { // scopedTarget.
                // 1..
                processCandidateBean(beanName);
            }
        }
        // 初始化处理器的方法们,目前是空方法,暂无具体的实现
        handlerMethodsInitialized(getHandlerMethods());
    }

    protected void processCandidateBean(String beanName) {
        // 获得 Bean 对应的 Class 对象
        Class<?> beanType = null;
        try {
            // 1.
            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);
            }
        }
        // 2.. 判断Bean是否为处理器(@Controller、@RequestMapping)
        if (beanType != null && isHandler(beanType)) {
            // 3.. 扫描处理器方法
            detectHandlerMethods(beanName);
        }
    }

    @Override
    protected boolean isHandler(Class<?> beanType) {
        // 1. @Controller、@RequestMapping注解
        return (AnnotatedElementUtils.hasAnnotation(beanType, Controller.class) ||
                AnnotatedElementUtils.hasAnnotation(beanType, RequestMapping.class));
    }

    /**
     * Look for handler methods in the specified handler bean.
     *
     * @param handler either a bean name or an actual handler instance
     * @see #getMappingForMethod
     */
    protected void detectHandlerMethods(Object handler) {
        // 获取handler的类型
        Class<?> handlerType = (handler instanceof String ?
                obtainApplicationContext().getType((String) handler) : handler.getClass());

        if (handlerType != null) {
            // 1. 如果是cglib代理的子对象类型,则返回父类型,否则直接返回传入的类型
            Class<?> userType = ClassUtils.getUserClass(handlerType);
            // 保存handler与匹配条件的对应关系,用于给registerHandlerMethod传入匹配条件
            Map<Method, T> methods = MethodIntrospector.selectMethods(userType,
                    (MethodIntrospector.MetadataLookup<T>) method -> {
                        try {
                            // 2... RequestMappingHandlerMapping (@RequestMapping => 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));
            }
            // RequestMappingInfo 将符合要求的method注册起来,也就是保存到三个map中
            methods.forEach((method, mapping) -> {
                Method invocableMethod = AopUtils.selectInvocableMethod(method, userType);
                // 3... RequestMappingHandlerMapping
                registerHandlerMethod(handler, invocableMethod, mapping);
            });
        }
    }

    protected void registerHandlerMethod(Object handler, Method method, T mapping) {
        // 1.. AbstractHandlerMethodMapping$MappingRegistry 内部类
        this.mappingRegistry.register(mapping, handler, method);
    }

    /**
     * Create the HandlerMethod instance.
     *
     * @param handler either a bean name or an actual handler instance
     * @param method  the target method
     * @return the created HandlerMethod
     */
    protected HandlerMethod createHandlerMethod(Object handler, Method method) {
        // 如果 handler 类型为 String, 说明对应一个 Bean 对象的名称
        // 例如 UserController 使用 @Controller 注解后,默认入参 handler 就是它的 beanName ,即 `userController`
        if (handler instanceof String) {
            // 1...
            return new HandlerMethod((String) handler,
                    obtainApplicationContext().getAutowireCapableBeanFactory(), method);
        }
        // 如果 handler 类型非 String ,说明是一个已经是一个 handler 对象,就无需处理,直接创建 HandlerMethod 对象
        return new HandlerMethod(handler, method);
    }

    /**
     * A registry that maintains all mappings to handler methods, exposing methods
     * to perform lookups and providing concurrent access.
     * <p>Package-private for testing purposes.
     */
    class MappingRegistry {

        private final Map<T, MappingRegistration<T>> registry = new HashMap<>();
        private final Map<T, HandlerMethod> mappingLookup = new LinkedHashMap<>();
        private final MultiValueMap<String, T> urlLookup = new LinkedMultiValueMap<>();
        private final Map<String, List<HandlerMethod>> nameLookup = new ConcurrentHashMap<>();

        private final Map<HandlerMethod, CorsConfiguration> corsLookup = new ConcurrentHashMap<>();
        private final ReentrantReadWriteLock readWriteLock = new ReentrantReadWriteLock();

        public void register(T mapping, Object handler, Method method) {
            // Assert that the handler method is not a suspending one.
            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 {
                // 1.. 创建HandlerMethod对象
                HandlerMethod handlerMethod = createHandlerMethod(handler, method);
                // 校验当前mapping是否存在对应的HandlerMethod对象,如果已存在但不是当前的handlerMethod对象则抛出异常
                validateMethodMapping(handlerMethod, mapping);
                // 将mapping与handlerMethod的映射关系保存至this.mappingLookup
                this.mappingLookup.put(mapping, handlerMethod);

                // 获得mapping对应的普通URL数组
                List<String> directUrls = getDirectUrls(mapping);
                // 将url和mapping的映射关系保存至this.urlLookup
                for (String url : directUrls) {
                    // 2. (`/userList`, RequestMappingInfo)
                    this.urlLookup.add(url, mapping);
                }

                // 初始化nameLookup
                String name = null;
                if (getNamingStrategy() != null) {
                    // 获得Mapping的名字
                    name = getNamingStrategy().getName(handlerMethod, mapping);
                    // 将mapping的名字与HandlerMethod的映射关系保存至this.nameLookup
                    addMappingName(name, handlerMethod);
                }

                // 初始化CorsConfiguration配置对象
                CorsConfiguration corsConfig = initCorsConfiguration(handler, method, mapping);
                if (corsConfig != null) { // skip
                    this.corsLookup.put(handlerMethod, corsConfig);
                }

                // 3. 创建MappingRegistration对象
                // 并与mapping映射添加到registry注册表中
                this.registry.put(mapping, new MappingRegistration<>(mapping, handlerMethod, directUrls, name));
            } finally {
                // 释放写锁
                this.readWriteLock.writeLock().unlock();
            }
        }

        private List<String> getDirectUrls(T mapping) {
            List<String> urls = new ArrayList<>(1);
            // 遍历 Mapping 对应的路径
            for (String path : getMappingPathPatterns(mapping)) {
                // 非**模式**路径
                if (!getPathMatcher().isPattern(path)) {
                    urls.add(path);
                }
            }
            return urls;
        }

        private void addMappingName(String name, HandlerMethod handlerMethod) {
            // 获得 Mapping 的名字,对应的 HandlerMethod 数组
            List<HandlerMethod> oldList = this.nameLookup.get(name);
            if (oldList == null) {
                oldList = Collections.emptyList();
            }

            // 如果已经存在,则不用添加
            for (HandlerMethod current : oldList) {
                if (handlerMethod.equals(current)) {
                    return;
                }
            }

            // 添加到 nameLookup 中
            List<HandlerMethod> newList = new ArrayList<>(oldList.size() + 1);
            newList.addAll(oldList);
            newList.add(handlerMethod);
            // 1.
            this.nameLookup.put(name, newList);
        }

        /**
         * Return matches for the given URL path. Not thread-safe.
         *
         * @see #acquireReadLock()
         */
        @Nullable
        public List<T> getMappingsByUrl(String urlPath) {
            // 1.
            return this.urlLookup.get(urlPath);
        }

        /**
         * Return all mappings and handler methods. Not thread-safe.
         *
         * @see #acquireReadLock()
         */
        public Map<T, HandlerMethod> getMappings() {
            return this.mappingLookup;
        }

    }

    // ------------------------------------------------------------------------------------------------

    // Handler method lookup

    /**
     * Look up a handler method for the given request.
     */
    @Override
    protected HandlerMethod getHandlerInternal(HttpServletRequest request) throws Exception {
        // 1... 获取访问的路径,一般类似于request.getServletPath(),返回不含contextPath的访问路径
        String lookupPath = getUrlPathHelper().getLookupPathForRequest(request);
        request.setAttribute(LOOKUP_PATH, lookupPath);
        // 获得读锁
        this.mappingRegistry.acquireReadLock();
        try {
            // 2.. 获取HandlerMethod作为handler对象,这里涉及到路径匹配的优先级
            // 优先级: 精确匹配 > 最长路径匹配 > 扩展名匹配
            HandlerMethod handlerMethod = lookupHandlerMethod(lookupPath, request);
            // handlerMethod内部包含有bean对象,其实指的是对应的controller
            return (handlerMethod != null ? handlerMethod.createWithResolvedBean() : null);
        } finally {
            //  释放读锁
            this.mappingRegistry.releaseReadLock();
        }
    }

    @Nullable
    protected HandlerMethod lookupHandlerMethod(String lookupPath, HttpServletRequest request) throws Exception {
        //  Match数组,存储匹配上当前请求的结果(Mapping + HandlerMethod)
        List<Match> matches = new ArrayList<>();
        // 1... AbstractHandlerMethodMapping$MappingRegistry 首先根据lookupPath获取到匹配条件 => RequestMappingInfo
        List<T> directPathMatches = this.mappingRegistry.getMappingsByUrl(lookupPath);
        if (directPathMatches != null) {
            // 2.. 将找到的匹配条件添加到matches
            addMatchingMappings(directPathMatches, matches, request);
        }
        // 如果不能直接使用lookupPath得到匹配条件,则将所有匹配条件加入matches
        if (matches.isEmpty()) {
            // No choice but to go through all mappings...
            addMatchingMappings(this.mappingRegistry.getMappings().keySet(), matches, request);
        }

        // 将包含匹配条件和handler的matches排序,并取第一个作为bestMatch,如果前面两个排序相同则抛出异常
        if (!matches.isEmpty()) {
            Match bestMatch = matches.get(0);
            if (matches.size() > 1) {
                // 创建MatchComparator对象,排序matches结果,排序器
                Comparator<Match> comparator = new MatchComparator(getMappingComparator(request));
                matches.sort(comparator);
                // 获得首个Match对象,也就是最匹配的
                bestMatch = matches.get(0);
                if (logger.isTraceEnabled()) {
                    logger.trace(matches.size() + " matching mappings: " + matches);
                }
                if (CorsUtils.isPreFlightRequest(request)) {
                    return PREFLIGHT_AMBIGUOUS_MATCH;
                }
                // 比较bestMatch和secondBestMatch,如果相等,说明有问题,抛出IllegalStateException异常
                // 因为,两个优先级一样高,说明无法判断谁更优先
                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);
            // 处理首个Match对象
            handleMatch(bestMatch.mapping, lookupPath, request);
            // 返回首个Match对象的handlerMethod属性
            return bestMatch.handlerMethod;
        }
        // 如果匹配不到,则处理不匹配的情况
        else {
            return handleNoMatch(this.mappingRegistry.getMappings().keySet(), lookupPath, request);
        }
    }

    private void addMatchingMappings(Collection<T> mappings, List<Match> matches, HttpServletRequest request) {
        // 遍历 Mapping 数组
        for (T mapping : mappings) {
            // 1... RequestMappingInfoHandlerMapping 执行匹配
            T match = getMatchingMapping(mapping, request);
            if (match != null) {
                // 如果匹配,则创建 Match 对象。(RequestMappingInfo, HandlerMethod)
                matches.add(new Match(match, this.mappingRegistry.getMappings().get(mapping)));
            }
        }
    }

}


















 











 















 











 







 

 






 
 















 





 












 






 














 














 
 

















 



 






 








 










 











 

























 










 








 














 






 













 


 









 


 
 























 











 


 





1. MethodIntrospector

public final class MethodIntrospector {

    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;

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

            // 1... ReflectionUtils
            ReflectionUtils.doWithMethods(currentHandlerType, method -> {
                Method specificMethod = ClassUtils.getMostSpecificMethod(method, targetClass);
                T result = metadataLookup.inspect(specificMethod);
                if (result != null) {
                    Method bridgedMethod = BridgeMethodResolver.findBridgedMethod(specificMethod);
                    if (bridgedMethod == specificMethod || metadataLookup.inspect(bridgedMethod) == null) {
                        methodMap.put(specificMethod, result);
                    }
                }
            }, ReflectionUtils.USER_DECLARED_METHODS);
        }

        return methodMap;
    }

}

















 
 
 
 
 
 
 
 
 
 






2. ReflectionUtils

public abstract class ReflectionUtils {

    /**
     * 执行给定回调操作在给定类和父类(或者给定的接口或父接口)的所有匹配方法
     * <p>
     * Perform the given callback operation on all matching methods of the given
     * class and superclasses (or given interface and super-interfaces).
     * <p>The same named method occurring on subclass and superclass will appear
     * twice, unless excluded by the specified {@link MethodFilter}.
     *
     * @param clazz the class to introspect
     * @param mc    the callback to invoke for each method
     * @param mf    the filter that determines the methods to apply the callback to
     * @throws IllegalStateException if introspection fails
     */
    public static void doWithMethods(Class<?> clazz, MethodCallback mc, @Nullable MethodFilter mf) {
        // 1. Keep backing up the inheritance hierarchy.
        // 从缓存中获取clazz的所有声明的方法,包括它的所有接口中所有默认方法;没有时就从{@code clazz}中获取,再添加到缓存中,
        Method[] methods = getDeclaredMethods(clazz, false);
        // 遍历所有方法
        for (Method method : methods) {
            // 如果mf不为null 且 method不满足mf的匹配要求
            if (mf != null && !mf.matches(method)) {
                // 跳过该method
                continue;
            }
            try {
                // 2... AbstractHandlerMethodMapping 对method执行回调操作
                mc.doWith(method);
            } catch (IllegalAccessException ex) {
                throw new IllegalStateException("Not allowed to access method '" + method.getName() + "': " + ex);
            }
        }
        // 如果clazz的父类不为nulll且(mf不是与未在{@code java.lang.Object}上声明的所有非桥接非合成方法匹配的预购建方法过滤器或者clazz的父类不为Object
        if (clazz.getSuperclass() != null && (mf != USER_DECLARED_METHODS || clazz.getSuperclass() != Object.class)) {
            // 递归方法
            // 执行给定回调操作在clazz的父类的所有匹配方法, 子类和父类发生的相同命名方法将出现两次,
            // 子类和父类发生的相同命名方法将出现两次,除非被mf排查
            doWithMethods(clazz.getSuperclass(), mc, mf);
        }
        // 如果clazz是接口
        else if (clazz.isInterface()) {
            // 遍历clazz的所有接口
            for (Class<?> superIfc : clazz.getInterfaces()) {
                // 递归方法
                // 执行给定回调操作在superIfc的所有匹配方法, 子类和父类发生的相同命名方法将出现两次,
                // 子类和父类发生的相同命名方法将出现两次,除非被mf排查
                doWithMethods(superIfc, mc, mf);
            }
        }
    }

}


















 









 
























3. HandlerMethod

public class HandlerMethod {

	private final Object bean;

	// 此属性用于新建HandlerMethod时传入的Handler是String的情况,需要使用beanFactory根据传入的String作为beanName获取到对应的bean,并设置为Handler
	@Nullable
	private final BeanFactory beanFactory;

	// bean类型
	private final Class<?> beanType;

	private final Method method;

	// 如果method是bridge method,则设置为其所对应的原有方法,否则直接设置为method
	private final Method bridgedMethod;

	// 处理请求的方法的参数
	private final MethodParameter[] parameters;

	@Nullable
	private HttpStatus responseStatus;

	@Nullable
	private String responseStatusReason;

	/**
	 * Create an instance from a bean name, a method, and a {@code BeanFactory}.
	 * The method {@link #createWithResolvedBean()} may be used later to
	 * re-create the {@code HandlerMethod} with an initialized bean.
	 */
	public HandlerMethod(String beanName, BeanFactory beanFactory, Method method) {
		Assert.hasText(beanName, "Bean name is required");
		Assert.notNull(beanFactory, "BeanFactory is required");
		Assert.notNull(method, "Method is required");
		this.bean = beanName;
		this.beanFactory = beanFactory;
		Class<?> beanType = beanFactory.getType(beanName);
		if (beanType == null) {
			throw new IllegalStateException("Cannot resolve bean type for bean with name '" + beanName + "'");
		}
		this.beanType = ClassUtils.getUserClass(beanType);
		this.method = method;
		// 1.
		this.bridgedMethod = BridgeMethodResolver.findBridgedMethod(method);
		// 2..
		this.parameters = initMethodParameters();
		evaluateResponseStatus();
		this.description = initDescription(this.beanType, this.method);
	}

	private MethodParameter[] initMethodParameters() {
		int count = this.bridgedMethod.getParameterCount();
		MethodParameter[] result = new MethodParameter[count];
		for (int i = 0; i < count; i++) {
			result[i] = new HandlerMethodParameter(i);
		}
		return result;
	}

	/**
	 * A MethodParameter with HandlerMethod-specific behavior.
	 */
	protected class HandlerMethodParameter extends SynthesizingMethodParameter {

		@Nullable
		private volatile Annotation[] combinedAnnotations;
    }

}











 





 

























 

 








 










 



2. ReqMappingInfoHandlerMapping

public abstract class RequestMappingInfoHandlerMapping extends AbstractHandlerMethodMapping<RequestMappingInfo> {

    private static final Method HTTP_OPTIONS_HANDLE_METHOD;

    static {
        try {
            HTTP_OPTIONS_HANDLE_METHOD = HttpOptionsHandler.class.getMethod("handle");
        } catch (NoSuchMethodException ex) {
            // Should never happen
            throw new IllegalStateException("Failed to retrieve internal handler method for HTTP OPTIONS", ex);
        }
    }

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

    @Override
    protected RequestMappingInfo getMatchingMapping(RequestMappingInfo info, HttpServletRequest request) {
        // 1... RequestMappingInfo => 新RequestMappingInfo
        return info.getMatchingCondition(request);
    }

}


















 








 



1. RequestMappingInfo

/**
 * 此类使用7个变量来保存7种类型的RequestCondition对象
 * <p>
 * Request mapping information. Encapsulates the following request mapping conditions:
 * <ol>
 * <li>{@link PatternsRequestCondition}
 * <li>{@link RequestMethodsRequestCondition}
 * <li>{@link ParamsRequestCondition}
 * <li>{@link HeadersRequestCondition}
 * <li>{@link ConsumesRequestCondition}
 * <li>{@link ProducesRequestCondition}
 * <li>{@code RequestCondition} (optional, custom request condition)
 * </ol>
 *
 * @author Arjen Poutsma
 * @author Rossen Stoyanchev
 * @since 3.1
 */
public final class RequestMappingInfo implements RequestCondition<RequestMappingInfo> {


    private static final PatternsRequestCondition EMPTY_PATTERNS = new PatternsRequestCondition();

    private static final RequestMethodsRequestCondition EMPTY_REQUEST_METHODS = new RequestMethodsRequestCondition();

    private static final ParamsRequestCondition EMPTY_PARAMS = new ParamsRequestCondition();

    private static final HeadersRequestCondition EMPTY_HEADERS = new HeadersRequestCondition();

    private static final ConsumesRequestCondition EMPTY_CONSUMES = new ConsumesRequestCondition();

    private static final ProducesRequestCondition EMPTY_PRODUCES = new ProducesRequestCondition();

    private static final RequestConditionHolder EMPTY_CUSTOM = new RequestConditionHolder(null);

    // 名字
    @Nullable
    private final String name;

    // 请求路径的条件
    private final PatternsRequestCondition patternsCondition;

    // 请求方法的条件
    private final RequestMethodsRequestCondition methodsCondition;

    // 请求参数的条件
    private final ParamsRequestCondition paramsCondition;

    // 请求头的条件
    private final HeadersRequestCondition headersCondition;

    // 可消费的 Content-Type 的条件
    private final ConsumesRequestCondition consumesCondition;

    // 可生产的 Content-Type 的条件
    private final ProducesRequestCondition producesCondition;

    // 自定义的条件
    private final RequestConditionHolder customConditionHolder;


    public RequestMappingInfo(@Nullable String name, @Nullable PatternsRequestCondition patterns,
                              @Nullable RequestMethodsRequestCondition methods, @Nullable ParamsRequestCondition params,
                              @Nullable HeadersRequestCondition headers, @Nullable ConsumesRequestCondition consumes,
                              @Nullable ProducesRequestCondition produces, @Nullable RequestCondition<?> custom) {

        this.name = (StringUtils.hasText(name) ? name : null);
		// 3.
        this.patternsCondition = (patterns != null ? patterns : EMPTY_PATTERNS);
        this.methodsCondition = (methods != null ? methods : EMPTY_REQUEST_METHODS);
        this.paramsCondition = (params != null ? params : EMPTY_PARAMS);
        this.headersCondition = (headers != null ? headers : EMPTY_HEADERS);
        this.consumesCondition = (consumes != null ? consumes : EMPTY_CONSUMES);
        this.producesCondition = (produces != null ? produces : EMPTY_PRODUCES);
        this.customConditionHolder = (custom != null ? new RequestConditionHolder(custom) : EMPTY_CUSTOM);

        this.hashCode = calculateHashCode(
                this.patternsCondition, this.methodsCondition, this.paramsCondition, this.headersCondition,
                this.consumesCondition, this.producesCondition, this.customConditionHolder);
    }

    /**
     * Checks if all conditions in this request mapping info match the provided request and returns
     * a potentially new request mapping info with conditions tailored to the current request.
     * <p>For example the returned instance may contain the subset of URL patterns that match to
     * the current request, sorted with best matching patterns on top.
     *
     * @return a new instance in case all conditions match; or {@code null} otherwise
     */
    @Override
    @Nullable
    public RequestMappingInfo getMatchingCondition(HttpServletRequest request) {
        // 匹配 methodsCondition、paramsCondition、headersCondition、consumesCondition、producesCondition
        // 如果任一为空,则返回 null ,表示匹配失败
        RequestMethodsRequestCondition methods = this.methodsCondition.getMatchingCondition(request);
        if (methods == null) {
            return null;
        }
        ParamsRequestCondition params = this.paramsCondition.getMatchingCondition(request);
        if (params == null) {
            return null;
        }
        HeadersRequestCondition headers = this.headersCondition.getMatchingCondition(request);
        if (headers == null) {
            return null;
        }
        ConsumesRequestCondition consumes = this.consumesCondition.getMatchingCondition(request);
        if (consumes == null) {
            return null;
        }
        ProducesRequestCondition produces = this.producesCondition.getMatchingCondition(request);
        if (produces == null) {
            return null;
        }
        // 4. PatternsRequestCondition
        PatternsRequestCondition patterns = this.patternsCondition.getMatchingCondition(request);
        if (patterns == null) {
            return null;
        }
        RequestConditionHolder custom = this.customConditionHolder.getMatchingCondition(request);
        if (custom == null) {
            return null;
        }

        /* 5.
         * 创建匹配的 RequestMappingInfo 对象
         * 为什么要创建 RequestMappingInfo 对象呢?
         *
         * 因为当前 RequestMappingInfo 对象,一个 methodsCondition 可以配置 GET、POST、DELETE 等等条件,
         * 但是实际就匹配一个请求类型,此时 methods 只代表其匹配的那个。
         */
        return new RequestMappingInfo(this.name, patterns,
                methods, params, headers, consumes, produces, custom.getCondition());
    }

    private static class DefaultBuilder implements Builder {

        @Override
        @SuppressWarnings("deprecation")
        public RequestMappingInfo build() {

            // 1.
            PatternsRequestCondition patternsCondition = ObjectUtils.isEmpty(this.paths) ? null :
                    new PatternsRequestCondition(
                            this.paths, this.options.getUrlPathHelper(), this.options.getPathMatcher(),
                            this.options.useSuffixPatternMatch(), this.options.useTrailingSlashMatch(),
                            this.options.getFileExtensions());

            ContentNegotiationManager manager = this.options.getContentNegotiationManager();

            // 2.
            return new RequestMappingInfo(this.mappingName, patternsCondition,
                    ObjectUtils.isEmpty(this.methods) ?
                            null : new RequestMethodsRequestCondition(this.methods),
                    ObjectUtils.isEmpty(this.params) ?
                            null : new ParamsRequestCondition(this.params),
                    ObjectUtils.isEmpty(this.headers) ?
                            null : new HeadersRequestCondition(this.headers),
                    ObjectUtils.isEmpty(this.consumes) && !this.hasContentType ?
                            null : new ConsumesRequestCondition(this.consumes, this.headers),
                    ObjectUtils.isEmpty(this.produces) && !this.hasAccept ?
                            null : new ProducesRequestCondition(this.produces, this.headers, manager),
                    this.customCondition);
        }
    }

}




































































 














































 















 










 








 















2. PatternsRequestCondition

public class PatternsRequestCondition extends AbstractRequestCondition<PatternsRequestCondition> {

	private final static Set<String> EMPTY_PATH_PATTERN = Collections.singleton("");

	/**
	 * 需要匹配的路径
	 */
	private final Set<String> patterns;

	/**
	 * 路径匹配器 AntPathMatcher
	 */
	private final PathMatcher pathMatcher;

	/**
	 * 路径解析器 UrlPathHelper
	 */
	private final UrlPathHelper pathHelper;

	/**
	 * Creates a new instance with the given URL patterns. Each pattern that is
	 * not empty and does not start with "/" is prepended with "/".
	 * @param patterns 0 or more URL patterns; if 0 the condition will match to
	 * every request.
	 */
	public PatternsRequestCondition(String... patterns) {
		this(patterns, null, null, true, true, null);
	}

	@Deprecated
	public PatternsRequestCondition(String[] patterns, @Nullable UrlPathHelper urlPathHelper,
			@Nullable PathMatcher pathMatcher, boolean useSuffixPatternMatch,
			boolean useTrailingSlashMatch, @Nullable List<String> fileExtensions) {

		// 1. 需要匹配的路径表达式集合,默认会加上 `/` 开头
		this.patterns = initPatterns(patterns);
		// 路径解析器 UrlPathHelper
		this.pathHelper = urlPathHelper != null ? urlPathHelper : UrlPathHelper.defaultInstance;
		// 路径匹配器 AntPathMatcher
		this.pathMatcher = pathMatcher != null ? pathMatcher : new AntPathMatcher();
		this.useSuffixPatternMatch = useSuffixPatternMatch;
		this.useTrailingSlashMatch = useTrailingSlashMatch;

		if (fileExtensions != null) {
			for (String fileExtension : fileExtensions) {
				if (fileExtension.charAt(0) != '.') {
					fileExtension = "." + fileExtension;
				}
				this.fileExtensions.add(fileExtension);
			}
		}
	}

	private static Set<String> initPatterns(String[] patterns) {
		if (!hasPattern(patterns)) {
			return EMPTY_PATH_PATTERN;
		}
		Set<String> result = new LinkedHashSet<>(patterns.length);
		for (String pattern : patterns) {
			if (StringUtils.hasLength(pattern) && !pattern.startsWith("/")) {
				// 1. 没有以 `/` 开头则加上
				pattern = "/" + pattern;
			}
			result.add(pattern);
		}
		return result;
	}

	private static boolean hasPattern(String[] patterns) {
		if (!ObjectUtils.isEmpty(patterns)) {
			for (String pattern : patterns) {
				if (StringUtils.hasText(pattern)) {
					return true;
				}
			}
		}
		return false;
	}

	@Override
	@Nullable
	public PatternsRequestCondition getMatchingCondition(HttpServletRequest request) {
		// 通过路径解析器 UrlPathHelper 获取请求路径
		String lookupPath = this.pathHelper.getLookupPathForRequest(request, HandlerMapping.LOOKUP_PATH);
		// 1.. 获取该条件中匹配请求路径的路径
		List<String> matches = getMatchingPatterns(lookupPath);
		// 如果不为空表示匹配,符合条件,则返回 PatternsRequestCondition 对象
		return !matches.isEmpty() ? new PatternsRequestCondition(new LinkedHashSet<>(matches), this) : null;
	}

	public List<String> getMatchingPatterns(String lookupPath) {
		List<String> matches = null;
		for (String pattern : this.patterns) {
            // 2..
			String match = getMatchingPattern(pattern, lookupPath);
			if (match != null) {
				matches = (matches != null ? matches : new ArrayList<>());
				matches.add(match);
			}
		}
		if (matches == null) {
			return Collections.emptyList();
		}
		if (matches.size() > 1) {
			matches.sort(this.pathMatcher.getPatternComparator(lookupPath));
		}
		return matches;
	}

	@Nullable
	private String getMatchingPattern(String pattern, String lookupPath) {
        // 3.
		if (pattern.equals(lookupPath)) {
			// 直接相等,匹配,则直接返回
			return pattern;
		}
		if (this.useSuffixPatternMatch) {
			if (!this.fileExtensions.isEmpty() && lookupPath.indexOf('.') != -1) {
				for (String extension : this.fileExtensions) {
					if (this.pathMatcher.match(pattern + extension, lookupPath)) {
						return pattern + extension;
					}
				}
			}
			else {
				boolean hasSuffix = pattern.indexOf('.') != -1;
				if (!hasSuffix && this.pathMatcher.match(pattern + ".*", lookupPath)) {
					return pattern + ".*";
				}
			}
		}
		// 通过路径匹配器 AntPathMatcher 判断是否匹配
		if (this.pathMatcher.match(pattern, lookupPath)) {
			return pattern;
		}
		if (this.useTrailingSlashMatch) {
			if (!pattern.endsWith("/") && this.pathMatcher.match(pattern + "/", lookupPath)) {
				return pattern + "/";
			}
		}
		return null;
	}

}







 



























 


















 






 























 

 






 

















 































1. RequestCondition
/**
 * 此接口专门用于保存从request提取出的用于匹配handler的条件
 * <p>
 * Contract for request mapping conditions.
 *
 * <p>Request conditions can be combined via {@link #combine(Object)}, matched to
 * a request via {@link #getMatchingCondition(HttpServletRequest)}, and compared
 * to each other via {@link #compareTo(Object, HttpServletRequest)} to determine
 * which is a closer match for a given request.
 *
 * @param <T> the type of objects that this RequestCondition can be combined
 *            with and compared to
 * @author Rossen Stoyanchev
 * @author Arjen Poutsma
 * @since 3.1
 */
public interface RequestCondition<T> {

    T combine(T other);

    @Nullable
    T getMatchingCondition(HttpServletRequest request);

    int compareTo(T other, HttpServletRequest request);

}
image-20240113171905130