相信 spring-mvc 这种被玩坏了的架构理念, 大家都烂熟于胸了, 不过还是想来扒一扒他的细节.
一个 http 请求, 怎么样被 spring 接收, 又怎样做出响应呢?
一般地, 我们会配置一个 Web.xml, 然后开始代码之旅.
在 Web.xml 中配置 servlet-mapping, 将请求转发到 DispatcherServlet, 那么我们认为 DispatcherServlet 是我们的第一棒交接者!
- <servlet>
- <servlet-name>springMVC</servlet-name>
- <servlet-class>org.springframework.Web.servlet.DispatcherServlet</servlet-class>
- <init-param>
- <param-name>contextConfigLocation</param-name>
- <param-value>classpath:applicationContext.xml</param-value>
- </init-param>
- <load-on-startup>1</load-on-startup>
- </servlet>
- <servlet-mapping>
- <servlet-name>springMVC</servlet-name>
- <url-pattern>/</url-pattern>
- </servlet-mapping>
的
- // tomcat 接到 http 请求后, 会主动创建一个 servlet, 然后调用进应用代码中, 创建的代码如下
- // Allocate a servlet instance to process this request
- try {
- if (!unavailable) {
- servlet = wrapper.allocate();
- }
- } catch (ServletException e) {
- wrapper.getLogger().error(sm.getString("applicationDispatcher.allocateException",
- wrapper.getName()), StandardWrapper.getRootCause(e));
- servletException = e;
- } catch (Throwable e) {
- ExceptionUtils.handleThrowable(e);
- wrapper.getLogger().error(sm.getString("applicationDispatcher.allocateException",
- wrapper.getName()), e);
- servletException = new ServletException
- (sm.getString("applicationDispatcher.allocateException",
- wrapper.getName()), e);
- servlet = null;
- }
- // org.apache.catalina.core.StandardWrapper.allocate(), 创建 servlet,
- /**
- * Allocate an initialized instance of this Servlet that is ready to have
- * its <code>service()</code> method called. If the servlet class does
- * not implement <code>SingleThreadModel</code>, the (only) initialized
- * instance may be returned immediately. If the servlet class implements
- * <code>SingleThreadModel</code>, the Wrapper implementation must ensure
- * that this instance is not allocated again until it is deallocated by a
- * call to <code>deallocate()</code>.
- *
- * @exception ServletException if the servlet init() method threw
- * an exception
- * @exception ServletException if a loading error occurs
- */
- @Override
- public Servlet allocate() throws ServletException {
- // If we are currently unloading this servlet, throw an exception
- if (unloading) {
- throw new ServletException(sm.getString("standardWrapper.unloading", getName()));
- }
- boolean newInstance = false;
- // If not SingleThreadedModel, return the same instance every time
- if (!singleThreadModel) {
- // Load and initialize our instance if necessary
- if (instance == null || !instanceInitialized) {
- synchronized (this) {
- if (instance == null) {
- try {
- if (log.isDebugEnabled()) {
- log.debug("Allocating non-STM instance");
- }
- // Note: We don't know if the Servlet implements
- // SingleThreadModel until we have loaded it.
- instance = loadServlet();
- newInstance = true;
- if (!singleThreadModel) {
- // For non-STM, increment here to prevent a race
- // condition with unload. Bug 43683, test case
- // #3
- countAllocated.incrementAndGet();
- }
- } catch (ServletException e) {
- throw e;
- } catch (Throwable e) {
- ExceptionUtils.handleThrowable(e);
- throw new ServletException(sm.getString("standardWrapper.allocate"), e);
- }
- }
- if (!instanceInitialized) {
- initServlet(instance);
- }
- }
- }
- if (singleThreadModel) {
- if (newInstance) {
- // Have to do this outside of the sync above to prevent a
- // possible deadlock
- synchronized (instancePool) {
- instancePool.push(instance);
- nInstances++;
- }
- }
- } else {
- if (log.isTraceEnabled()) {
- log.trace("Returning non-STM instance");
- }
- // For new instances, count will have been incremented at the
- // time of creation
- if (!newInstance) {
- countAllocated.incrementAndGet();
- }
- return instance;
- }
- }
- synchronized (instancePool) {
- while (countAllocated.get()>= nInstances) {
- // Allocate a new instance if possible, or else wait
- if (nInstances <maxInstances) {
- try {
- instancePool.push(loadServlet());
- nInstances++;
- } catch (ServletException e) {
- throw e;
- } catch (Throwable e) {
- ExceptionUtils.handleThrowable(e);
- throw new ServletException(sm.getString("standardWrapper.allocate"), e);
- }
- } else {
- try {
- instancePool.wait();
- } catch (InterruptedException e) {
- // Ignore
- }
- }
- }
- if (log.isTraceEnabled()) {
- log.trace("Returning allocated STM instance");
- }
- countAllocated.incrementAndGet();
- return instancePool.pop();
- }
- }
- View Code
- // 最后, 会先调用 filter, 完成之后再调用 servlet.service()
- private void internalDoFilter(ServletRequest request,
- ServletResponse response)
- throws IOException, ServletException {
- // Call the next filter if there is one
- if (pos <n) {
- ApplicationFilterConfig filterConfig = filters[pos++];
- try {
- Filter filter = filterConfig.getFilter();
- if (request.isAsyncSupported() && "false".equalsIgnoreCase(
- filterConfig.getFilterDef().getAsyncSupported())) {
- request.setAttribute(Globals.ASYNC_SUPPORTED_ATTR, Boolean.FALSE);
- }
- if( Globals.IS_SECURITY_ENABLED ) {
- final ServletRequest req = request;
- final ServletResponse res = response;
- Principal principal =
- ((HttpServletRequest) req).getUserPrincipal();
- Object[] args = new Object[]{req, res, this};
- SecurityUtil.doAsPrivilege ("doFilter", filter, classType, args, principal);
- } else {
- filter.doFilter(request, response, this);
- }
- } catch (IOException | ServletException | RuntimeException e) {
- throw e;
- } catch (Throwable e) {
- e = ExceptionUtils.unwrapInvocationTargetException(e);
- ExceptionUtils.handleThrowable(e);
- throw new ServletException(sm.getString("filterChain.filter"), e);
- }
- return;
- }
- // We fell off the end of the chain -- call the servlet instance
- try {
- if (ApplicationDispatcher.WRAP_SAME_OBJECT) {
- lastServicedRequest.set(request);
- lastServicedResponse.set(response);
- }
- if (request.isAsyncSupported() && !servletSupportsAsync) {
- request.setAttribute(Globals.ASYNC_SUPPORTED_ATTR,
- Boolean.FALSE);
- }
- // Use potentially wrapped request from this point
- if ((request instanceof HttpServletRequest) &&
- (response instanceof HttpServletResponse) &&
- Globals.IS_SECURITY_ENABLED ) {
- final ServletRequest req = request;
- final ServletResponse res = response;
- Principal principal =
- ((HttpServletRequest) req).getUserPrincipal();
- Object[] args = new Object[]{req, res};
- SecurityUtil.doAsPrivilege("service",
- servlet,
- classTypeUsedInService,
- args,
- principal);
- } else {
- servlet.service(request, response);
- }
- } catch (IOException | ServletException | RuntimeException e) {
- throw e;
- } catch (Throwable e) {
- e = ExceptionUtils.unwrapInvocationTargetException(e);
- ExceptionUtils.handleThrowable(e);
- throw new ServletException(sm.getString("filterChain.servlet"), e);
- } finally {
- if (ApplicationDispatcher.WRAP_SAME_OBJECT) {
- lastServicedRequest.set(null);
- lastServicedResponse.set(null);
- }
- }
- }
- // 现在, 到了 DispatcherServlet 出场了, 由 DispatcherServlet 的父类 org.springframework.Web.servlet.FrameworkServlet.service() 接力, 开始处理
- /**
- * Override the parent class implementation in order to intercept PATCH requests.
- */
- @Override
- protected void service(HttpServletRequest request, HttpServletResponse response)
- throws ServletException, IOException {
- HttpMethod httpMethod = HttpMethod.resolve(request.getMethod());
- if (HttpMethod.PATCH == httpMethod || httpMethod == null) {
- processRequest(request, response);
- }
- else {
- // 交由 HttpServlet.service() 处理, 转发 doGet,doPost 等等方法
- super.service(request, response);
- }
- }
- // 父类 HttpServlet.service(), 只负责转发请求, 由子类进行相应处理
- protected void service(HttpServletRequest req, HttpServletResponse resp)
- throws ServletException, IOException
- {
- String method = req.getMethod();
- if (method.equals(METHOD_GET)) {
- long lastModified = getLastModified(req);
- if (lastModified == -1) {
- // servlet doesn't support if-modified-since, no reason
- // to go through further expensive logic
- doGet(req, resp);
- } else {
- long ifModifiedSince = 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);
- doHead(req, resp);
- } else if (method.equals(METHOD_POST)) {
- doPost(req, resp);
- } else if (method.equals(METHOD_PUT)) {
- doPut(req, resp);
- } else if (method.equals(METHOD_DELETE)) {
- doDelete(req, resp);
- } else if (method.equals(METHOD_OPTIONS)) {
- doOptions(req,resp);
- } else if (method.equals(METHOD_TRACE)) {
- doTrace(req,resp);
- } else {
- //
- // Note that this means NO servlet supports whatever
- // method was requested, anywhere on this server.
- //
- String errMsg = lStrings.getString("http.method_not_implemented");
- Object[] errArgs = new Object[1];
- errArgs[0] = method;
- errMsg = MessageFormat.format(errMsg, errArgs);
- resp.sendError(HttpServletResponse.SC_NOT_IMPLEMENTED, errMsg);
- }
- }
- // 比如 FrameworkServlet.doGet()/doXXX() 方法重写, 调用 processRequest()
- /**
- * 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);
- }
- View Code
- // FrameworkServlet.processRequest() 做一些全局设置, 然后交由 doService() 进行具体处理, doService() 由子类实现, 也即 DispatcherServlet.doService()
- protected final void processRequest(HttpServletRequest request, HttpServletResponse response)
- throws ServletException, IOException {
- long startTime = System.currentTimeMillis();
- Throwable failureCause = null;
- LocaleContext previousLocaleContext = LocaleContextHolder.getLocaleContext();
- LocaleContext localeContext = buildLocaleContext(request);
- RequestAttributes previousAttributes = RequestContextHolder.getRequestAttributes();
- ServletRequestAttributes requestAttributes = buildRequestAttributes(request, response, previousAttributes);
- WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
- asyncManager.registerCallableInterceptor(FrameworkServlet.class.getName(), new RequestBindingInterceptor());
- initContextHolders(request, localeContext, requestAttributes);
- try {
- // 具体处理, 交由 DispatcherServlet 处理
- doService(request, response);
- }
- catch (ServletException ex) {
- failureCause = ex;
- throw ex;
- }
- catch (IOException ex) {
- failureCause = ex;
- throw ex;
- }
- catch (Throwable ex) {
- failureCause = ex;
- throw new NestedServletException("Request processing failed", ex);
- }
- finally {
- resetContextHolders(request, previousLocaleContext, previousAttributes);
- if (requestAttributes != null) {
- // 请求完成回调
- requestAttributes.requestCompleted();
- }
- if (logger.isDebugEnabled()) {
- if (failureCause != null) {
- this.logger.debug("Could not complete request", failureCause);
- }
- else {
- if (asyncManager.isConcurrentHandlingStarted()) {
- logger.debug("Leaving response open for concurrent processing");
- }
- else {
- this.logger.debug("Successfully completed request");
- }
- }
- }
- // 发布完成事件
- publishRequestHandledEvent(request, response, startTime, failureCause);
- }
- }
- // locale 上下文管理
- private void initContextHolders(
- HttpServletRequest request, LocaleContext localeContext, RequestAttributes requestAttributes) {
- // private static final ThreadLocal<LocaleContext> localeContextHolder = new NamedThreadLocal<LocaleContext>("LocaleContext");
- if (localeContext != null) {
- LocaleContextHolder.setLocaleContext(localeContext, this.threadContextInheritable);
- }
- // private static final ThreadLocal<RequestAttributes> requestAttributesHolder = new NamedThreadLocal<RequestAttributes>("Request attributes");
- if (requestAttributes != null) {
- RequestContextHolder.setRequestAttributes(requestAttributes, this.threadContextInheritable);
- }
- if (logger.isTraceEnabled()) {
- logger.trace("Bound request context to thread:" + request);
- }
- }
- // org.springframework.Web.servlet.DispatcherServlet, 进行具体请求转发
- /**
- * 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 {
- if (logger.isDebugEnabled()) {
- String resumed = WebAsyncUtils.getAsyncManager(request).hasConcurrentResult() ? "resumed" : "";
- logger.debug("DispatcherServlet with name'" + getServletName() + "'" + resumed +
- "processing" + request.getMethod() + "request for [" + getRequestUri(request) + "]");
- }
- // Keep a snapshot of the request attributes in case of an include,
- // to be able to restore the original attributes after the include.
- Map<String, Object> attributesSnapshot = null;
- if (WebUtils.isIncludeRequest(request)) {
- attributesSnapshot = new HashMap<String, Object>();
- Enumeration<?> attrNames = request.getAttributeNames();
- while (attrNames.hasMoreElements()) {
- String attrName = (String) attrNames.nextElement();
- if (this.cleanupAfterInclude || attrName.startsWith(DEFAULT_STRATEGIES_PREFIX)) {
- attributesSnapshot.put(attrName, request.getAttribute(attrName));
- }
- }
- }
- // Make framework objects available to handlers and view objects.
- // 设备各种处理器到 request 中
- // 上下文管理
- request.setAttribute(WEB_APPLICATION_CONTEXT_ATTRIBUTE, getWebApplicationContext());
- // locale 管理
- request.setAttribute(LOCALE_RESOLVER_ATTRIBUTE, this.localeResolver);
- // themeResolver
- request.setAttribute(THEME_RESOLVER_ATTRIBUTE, this.themeResolver);
- request.setAttribute(THEME_SOURCE_ATTRIBUTE, getThemeSource());
- FlashMap inputFlashMap = this.flashMapManager.retrieveAndUpdate(request, response);
- if (inputFlashMap != null) {
- request.setAttribute(INPUT_FLASH_MAP_ATTRIBUTE, Collections.unmodifiableMap(inputFlashMap));
- }
- request.setAttribute(OUTPUT_FLASH_MAP_ATTRIBUTE, new FlashMap());
- request.setAttribute(FLASH_MAP_MANAGER_ATTRIBUTE, this.flashMapManager);
- try {
- doDispatch(request, response);
- }
- finally {
- if (!WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted()) {
- // Restore the original attribute snapshot, in case of an include.
- if (attributesSnapshot != null) {
- restoreAttributesAfterInclude(request, attributesSnapshot);
- }
- }
- }
- }
- // doDispatch(), 实际分发方法
- /**
- * Process the actual dispatching to the handler.
- * <p>The handler will be obtained by applying the servlet's HandlerMappings in order.
- * The HandlerAdapter will be obtained by querying the servlet's installed HandlerAdapters
- * to find the first that supports the handler class.
- * <p>All HTTP methods are handled by this method. It's up to HandlerAdapters or handlers
- * themselves to decide which methods are acceptable.
- * @param request current HTTP request
- * @param response current HTTP response
- * @throws Exception in case of any kind of processing failure
- */
- protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
- HttpServletRequest processedRequest = request;
- HandlerExecutionChain mappedHandler = null;
- boolean multipartRequestParsed = false;
- WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
- try {
- ModelAndView mv = null;
- Exception dispatchException = null;
- try {
- // 封装 multipart 文件上传, 如果是文件上传类, 则将其封装为 MultipartHttpServletRequest, 以便后续直接使用
- processedRequest = checkMultipart(request);
- multipartRequestParsed = (processedRequest != request);
- // 获取具体 uri 业务处理器, 即 由 RequestMapping 设置的路径
- mappedHandler = getHandler(processedRequest);
- if (mappedHandler == null || mappedHandler.getHandler() == null) {
- // 如果没有找到 handler 则结束处理
- noHandlerFound(processedRequest, response);
- return;
- }
- // Determine handler adapter for the current request.
- HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
- // 对 GET|HEAD 进行 last-modified 进行检测
- String method = request.getMethod();
- boolean isGet = "GET".equals(method);
- if (isGet || "HEAD".equals(method)) {
- long lastModified = ha.getLastModified(request, mappedHandler.getHandler());
- if (logger.isDebugEnabled()) {
- logger.debug("Last-Modified value for [" + getRequestUri(request) + "] is:" + lastModified);
- }
- if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) {
- return;
- }
- }
- // 进行处理器前置调用, 如果未通过, 则结束
- if (!mappedHandler.applyPreHandle(processedRequest, response)) {
- return;
- }
- // Actually invoke the handler.
- // 调用业务处理
- mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
- if (asyncManager.isConcurrentHandlingStarted()) {
- return;
- }
- // 设置 view
- applyDefaultViewName(processedRequest, mv);
- // 后置处理
- mappedHandler.applyPostHandle(processedRequest, response, mv);
- }
- catch (Exception ex) {
- dispatchException = ex;
- }
- catch (Throwable err) {
- // As of 4.3, we're processing Errors thrown from handler methods as well,
- // making them available for @ExceptionHandler methods and other scenarios.
- dispatchException = new NestedServletException("Handler dispatch failed", err);
- }
- // 最后再处理一次结果, 如果有必要的话
- processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
- }
- catch (Exception ex) {
- triggerAfterCompletion(processedRequest, response, mappedHandler, ex);
- }
- catch (Throwable err) {
- triggerAfterCompletion(processedRequest, response, mappedHandler,
- new NestedServletException("Handler processing failed", err));
- }
- finally {
- // 清理环境
- if (asyncManager.isConcurrentHandlingStarted()) {
- // Instead of postHandle and afterCompletion
- if (mappedHandler != null) {
- mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);
- }
- }
- else {
- // Clean up any resources used by a multipart request.
- if (multipartRequestParsed) {
- cleanupMultipart(processedRequest);
- }
- }
- }
- }
- // multipart 检测, 需设置 multipartResolver, 方能转换, 否则直接返回原始请求
- /**
- * 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 {
- if (this.multipartResolver != null && this.multipartResolver.isMultipart(request)) {
- if (WebUtils.getNativeRequest(request, MultipartHttpServletRequest.class) != null) {
- logger.debug("Request is already a MultipartHttpServletRequest - if not in a forward," +
- "this typically results from an additional MultipartFilter in web.xml");
- }
- else if (hasMultipartException(request) ) {
- logger.debug("Multipart resolution failed for current request before -" +
- "skipping re-resolution for undisturbed error rendering");
- }
- else {
- try {
- // 调用 multipartResolver.resolveMultipart(), 由 resolver 进行文件转换处理
- 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;
- }
- View Code
- // 关键: 获取 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
- */
- protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
- // handlerMappings 中保存了所有可用的路由
- for (HandlerMapping hm : this.handlerMappings) {
- if (logger.isTraceEnabled()) {
- logger.trace(
- "Testing handler map [" + hm + "] in DispatcherServlet with name'" + getServletName() + "'");
- }
- // 由具体的 HandlerMapping 去解析自己的映射
- HandlerExecutionChain handler = hm.getHandler(request);
- if (handler != null) {
- return handler;
- }
- }
- return null;
- }
- // org.springframework.Web.servlet.mvc.method.annotation.RequestMappingHandlerMapping
- // org.springframework.Web.servlet.handler.AbstractHandlerMapping.getHandler()
- /**
- * 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
- public final HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
- // 获取 handlerMethod
- Object handler = getHandlerInternal(request);
- if (handler == null) {
- handler = getDefaultHandler();
- }
- if (handler == null) {
- return null;
- }
- // Bean name or resolved handler?
- if (handler instanceof String) {
- String handlerName = (String) handler;
- handler = getApplicationContext().getBean(handlerName);
- }
- // 获取 handler 链
- HandlerExecutionChain executionChain = getHandlerExecutionChain(handler, request);
- if (CorsUtils.isCorsRequest(request)) {
- CorsConfiguration globalConfig = this.corsConfigSource.getCorsConfiguration(request);
- CorsConfiguration handlerConfig = getCorsConfiguration(handler, request);
- CorsConfiguration config = (globalConfig != null ? globalConfig.combine(handlerConfig) : handlerConfig);
- executionChain = getCorsHandlerExecutionChain(request, executionChain, config);
- }
- return executionChain;
- }
- // org.springframework.Web.servlet.handler.AbstractHandlerMethodMapping.getHandlerInternal(), 查找 handler
- /**
- * Look up a handler method for the given request.
- */
- @Override
- protected HandlerMethod getHandlerInternal(HttpServletRequest request) throws Exception {
- // UrlHelper.getLookupPathForRequest() 进行路径分析
- String lookupPath = getUrlPathHelper().getLookupPathForRequest(request);
- if (logger.isDebugEnabled()) {
- logger.debug("Looking up handler method for path" + lookupPath);
- }
- // 加锁读取
- this.mappingRegistry.acquireReadLock();
- try {
- // 根据 path 查找 handlerMethod
- HandlerMethod handlerMethod = lookupHandlerMethod(lookupPath, request);
- if (logger.isDebugEnabled()) {
- if (handlerMethod != null) {
- logger.debug("Returning handler method [" + handlerMethod + "]");
- }
- else {
- logger.debug("Did not find handler method for [" + lookupPath + "]");
- }
- }
- // 找到匹配后, 将 handler 通过 bean 查找方式包装进 HandlerMethod 中返回
- return (handlerMethod != null ? handlerMethod.createWithResolvedBean() : null);
- }
- finally {
- this.mappingRegistry.releaseReadLock();
- }
- }
- // 更细节的查询 handler 动作
- // org.springframework.Web.util.UrlPathHelper
- /**
- * Return the mapping lookup path for the given request, within the current
- * servlet mapping if applicable, else within the Web application.
- * <p>Detects include request URL if called within a RequestDispatcher include.
- * @param request current HTTP request
- * @return the lookup path
- * @see #getPathWithinApplication
- * @see #getPathWithinServletMapping
- */
- public String getLookupPathForRequest(HttpServletRequest request) {
- // Always use full path within current servlet context?
- if (this.alwaysUseFullPath) {
- return getPathWithinApplication(request);
- }
- // Else, use path within current servlet mapping if applicable
- String REST = getPathWithinServletMapping(request);
- if (!"".equals(REST)) {
- return REST;
- }
- else {
- // 返回 /hello/test
- return getPathWithinApplication(request);
- }
- }
- //
- /**
- * Return the path within the servlet mapping for the given request,
- * i.e. the part of the request's URL beyond the part that called the servlet,
- * or "" if the whole URL has been used to identify the servlet.
- * <p>Detects include request URL if called within a RequestDispatcher include.
- * <p>E.g.: servlet mapping = "/*"; request URI = "/test/a" -> "/test/a".
- * <p>E.g.: servlet mapping = "/"; request URI = "/test/a" -> "/test/a".
- * <p>E.g.: servlet mapping = "/test/*"; request URI = "/test/a" -> "/a".
- * <p>E.g.: servlet mapping = "/test"; request URI = "/test" -> "".
- * <p>E.g.: servlet mapping = "/*.test"; request URI = "/a.test" -> "".
- * @param request current HTTP request
- * @return the path within the servlet mapping, or ""
- */
- public String getPathWithinServletMapping(HttpServletRequest request) {
- String pathWithinApp = getPathWithinApplication(request);
- String servletPath = getServletPath(request);
- String sanitizedPathWithinApp = getSanitizedPath(pathWithinApp);
- String path;
- // If the App container sanitized the servletPath, check against the sanitized version
- if (servletPath.contains(sanitizedPathWithinApp)) {
- path = getRemainingPath(sanitizedPathWithinApp, servletPath, false);
- }
- else {
- path = getRemainingPath(pathWithinApp, servletPath, false);
- }
- if (path != null) {
- // Normal case: URI contains servlet path.
- return path;
- }
- else {
- // Special case: URI is different from servlet path.
- String pathInfo = request.getPathInfo();
- if (pathInfo != null) {
- // Use path info if available. Indicates index page within a servlet mapping?
- // e.g. with index page: URI="/", servletPath="/index.html"
- return pathInfo;
- }
- if (!this.urlDecode) {
- // No path info... (not mapped by prefix, nor by extension, nor "/*")
- // For the default servlet mapping (i.e. "/"), urlDecode=false can
- // cause issues since getServletPath() returns a decoded path.
- // If decoding pathWithinApp yields a match just use pathWithinApp.
- path = getRemainingPath(decodeInternal(request, pathWithinApp), servletPath, false);
- if (path != null) {
- return pathWithinApp;
- }
- }
- // Otherwise, use the full servlet path.
- return servletPath;
- }
- }
- // org.springframework.Web.servlet.handler.AbstractHandlerMethodMapping
- /**
- * Look up the best-matching handler method for the current request.
- * If multiple matches are found, the best match is selected.
- * @param lookupPath mapping lookup path within the current servlet mapping
- * @param request the current request
- * @return the best-matching handler method, or {@code null} if no match
- * @see #handleMatch(Object, String, HttpServletRequest)
- * @see #handleNoMatch(Set, String, HttpServletRequest)
- */
- protected HandlerMethod lookupHandlerMethod(String lookupPath, HttpServletRequest request) throws Exception {
- List<Match> matches = new ArrayList<Match>();
- List<T> directPathMatches = this.mappingRegistry.getMappingsByUrl(lookupPath);
- if (directPathMatches != null) {
- // 添加到匹配中, 考虑不止一个匹配
- addMatchingMappings(directPathMatches, matches, request);
- }
- if (matches.isEmpty()) {
- // No choice but to go through all mappings...
- addMatchingMappings(this.mappingRegistry.getMappings().keySet(), matches, request);
- }
- if (!matches.isEmpty()) {
- // 如果有多个匹配, 进行优先级排序, 判断是否可用, 然后取第一个作为最终的 handler
- Comparator<Match> comparator = new MatchComparator(getMappingComparator(request));
- Collections.sort(matches, comparator);
- if (logger.isTraceEnabled()) {
- logger.trace("Found" + matches.size() + "matching mapping(s) for [" +
- lookupPath + "] :" + matches);
- }
- Match bestMatch = matches.get(0);
- if (matches.size()> 1) {
- if (CorsUtils.isPreFlightRequest(request)) {
- return PREFLIGHT_AMBIGUOUS_MATCH;
- }
- Match secondBestMatch = matches.get(1);
- if (comparator.compare(bestMatch, secondBestMatch) == 0) {
- Method m1 = bestMatch.handlerMethod.getMethod();
- Method m2 = secondBestMatch.handlerMethod.getMethod();
- throw new IllegalStateException("Ambiguous handler methods mapped for HTTP path'" +
- request.getRequestURL() + "': {" + m1 + "," + m2 + "}");
- }
- }
- // 解析参数到 handlerMethod 中
- handleMatch(bestMatch.mapping, lookupPath, request);
- return bestMatch.handlerMethod;
- }
- else {
- return handleNoMatch(this.mappingRegistry.getMappings().keySet(), lookupPath, request);
- }
- }
- // AbstractHandlerMethodMapping$MappingRegistry.getMappingsByUrl()
- /**
- * Return matches for the given URL path. Not thread-safe.
- * @see #acquireReadLock()
- */
- public List<T> getMappingsByUrl(String urlPath) {
- // private final MultiValueMap<String, T> urlLookup = new LinkedMultiValueMap<String, T>();
- // 内部使用 Map<K, List<V>> targetMap; 进行保存关系映射
- return this.urlLookup.get(urlPath);
- }
- // AbstractHandlerMethodMapping, 将确实有效的 mapping 添加到 matches 中
- private void addMatchingMappings(Collection<T> mappings, List<Match> matches, HttpServletRequest request) {
- for (T mapping : mappings) {
- // 重新严格校验路径, 参数, header, 返回新的 mapping
- T match = getMatchingMapping(mapping, request);
- if (match != null) {
- // 将 handler 放入 match
- // private class Match {}
- matches.add(new Match(match, this.mappingRegistry.getMappings().get(mapping)));
- }
- }
- }
- View Code
- // org.springframework.Web.servlet.mvc.condition.HeadersRequestCondition
- // org.springframework.Web.servlet.mvc.method.RequestMappingInfo
- // 进行参数校验, 并封装返回参数信息到 RequestMappingInfo 中返回
- /**
- * 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
- public RequestMappingInfo getMatchingCondition(HttpServletRequest request) {
- RequestMethodsRequestCondition methods = this.methodsCondition.getMatchingCondition(request);
- ParamsRequestCondition params = this.paramsCondition.getMatchingCondition(request);
- HeadersRequestCondition headers = this.headersCondition.getMatchingCondition(request);
- ConsumesRequestCondition consumes = this.consumesCondition.getMatchingCondition(request);
- ProducesRequestCondition produces = this.producesCondition.getMatchingCondition(request);
- // 其中某个不匹配, 则返回 null
- if (methods == null || params == null || headers == null || consumes == null || produces == null) {
- return null;
- }
- PatternsRequestCondition patterns = this.patternsCondition.getMatchingCondition(request);
- if (patterns == null) {
- return null;
- }
- RequestConditionHolder custom = this.customConditionHolder.getMatchingCondition(request);
- if (custom == null) {
- return null;
- }
- return new RequestMappingInfo(this.name, patterns,
- methods, params, headers, consumes, produces, custom.getCondition());
- }
- /**
- * Expose URI template variables, matrix variables, and producible media types in the request.
- * @see HandlerMapping#URI_TEMPLATE_VARIABLES_ATTRIBUTE
- * @see HandlerMapping#MATRIX_VARIABLES_ATTRIBUTE
- * @see HandlerMapping#PRODUCIBLE_MEDIA_TYPES_ATTRIBUTE
- */
- @Override
- protected void handleMatch(RequestMappingInfo info, String lookupPath, HttpServletRequest request) {
- super.handleMatch(info, lookupPath, request);
- String bestPattern;
- Map<String, String> uriVariables;
- Map<String, String> decodedUriVariables;
- Set<String> patterns = info.getPatternsCondition().getPatterns();
- if (patterns.isEmpty()) {
- bestPattern = lookupPath;
- uriVariables = Collections.emptyMap();
- decodedUriVariables = Collections.emptyMap();
- }
- else {
- bestPattern = patterns.iterator().next();
- uriVariables = getPathMatcher().extractUriTemplateVariables(bestPattern, lookupPath);
- decodedUriVariables = getUrlPathHelper().decodePathVariables(request, uriVariables);
- }
- request.setAttribute(BEST_MATCHING_PATTERN_ATTRIBUTE, bestPattern);
- request.setAttribute(HandlerMapping.URI_TEMPLATE_VARIABLES_ATTRIBUTE, decodedUriVariables);
- if (isMatrixVariableContentAvailable()) {
- Map<String, MultiValueMap<String, String>> matrixVars = extractMatrixVariables(request, uriVariables);
- request.setAttribute(HandlerMapping.MATRIX_VARIABLES_ATTRIBUTE, matrixVars);
- }
- if (!info.getProducesCondition().getProducibleMediaTypes().isEmpty()) {
- Set<MediaType> mediaTypes = info.getProducesCondition().getProducibleMediaTypes();
- request.setAttribute(PRODUCIBLE_MEDIA_TYPES_ATTRIBUTE, mediaTypes);
- }
- }
- View Code
- // org.springframework.Web.method.HandlerMethod.createWithResolvedBean(), 重新包装一个带 bean 实例的 HandlerMethod
- /**
- * If the provided instance contains a bean name rather than an object instance,
- * the bean name is resolved before a {@link HandlerMethod} is created and returned.
- */
- public HandlerMethod createWithResolvedBean() {
- Object handler = this.bean;
- if (this.bean instanceof String) {
- String beanName = (String) this.bean;
- // spring beans 组件
- handler = this.beanFactory.getBean(beanName);
- }
- return new HandlerMethod(this, handler);
- }
- /**
- * Build a {@link HandlerExecutionChain} for the given handler, including
- * applicable interceptors.
- * <p>The default implementation builds a standard {@link HandlerExecutionChain}
- * with the given handler, the handler mapping's common interceptors, and any
- * {@link MappedInterceptor}s matching to the current request URL. Interceptors
- * are added in the order they were registered. Subclasses may override this
- * in order to extend/rearrange the list of interceptors.
- * <p><b>NOTE:</b> The passed-in handler object may be a raw handler or a
- * pre-built {@link HandlerExecutionChain}. This method should handle those
- * two cases explicitly, either building a new {@link HandlerExecutionChain}
- * or extending the existing chain.
- * <p>For simply adding an interceptor in a custom subclass, consider calling
- * {@code super.getHandlerExecutionChain(handler, request)} and invoking
- * {@link HandlerExecutionChain#addInterceptor} on the returned chain object.
- * @param handler the resolved handler instance (never {@code null})
- * @param request current HTTP request
- * @return the HandlerExecutionChain (never {@code null})
- * @see #getAdaptedInterceptors()
- */
- protected HandlerExecutionChain getHandlerExecutionChain(Object handler, HttpServletRequest request) {
- HandlerExecutionChain chain = (handler instanceof HandlerExecutionChain ?
- (HandlerExecutionChain) handler : new HandlerExecutionChain(handler));
- String lookupPath = this.urlPathHelper.getLookupPathForRequest(request);
- for (HandlerInterceptor interceptor : this.adaptedInterceptors) {
- if (interceptor instanceof MappedInterceptor) {
- MappedInterceptor mappedInterceptor = (MappedInterceptor) interceptor;
- if (mappedInterceptor.matches(lookupPath, this.pathMatcher)) {
- // 取出内层的 interceptor 使用
- chain.addInterceptor(mappedInterceptor.getInterceptor());
- }
- }
- else {
- chain.addInterceptor(interceptor);
- }
- }
- return chain;
- }
- // 获取 adapter, 适配不同类型的调用
- // org.springframework.Web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter
- // org.springframework.Web.servlet.mvc.HttpRequestHandlerAdapter
- // org.springframework.Web.servlet.mvc.SimpleControllerHandlerAdapter
- /**
- * Return the HandlerAdapter for this handler object.
- * @param handler the handler object to find an adapter for
- * @throws ServletException if no HandlerAdapter can be found for the handler. This is a fatal error.
- */
- protected HandlerAdapter getHandlerAdapter(Object handler) throws ServletException {
- for (HandlerAdapter ha : this.handlerAdapters) {
- if (logger.isTraceEnabled()) {
- logger.trace("Testing handler adapter [" + ha + "]");
- }
- if (ha.supports(handler)) {
- return ha;
- }
- }
- throw new ServletException("No adapter for handler [" + handler +
- "]: The DispatcherServlet configuration needs to include a HandlerAdapter that supports this handler");
- }
- // mappedHandler.applyPreHandle(processedRequest, response) , 前置调用处理, interceptor 拦截调用
- /**
- * Apply preHandle methods of registered interceptors.
- * @return {@code true} if the execution chain should proceed with the
- * next interceptor or the handler itself. Else, DispatcherServlet assumes
- * that this interceptor has already dealt with the response itself.
- */
- boolean applyPreHandle(HttpServletRequest request, HttpServletResponse response) throws Exception {
- HandlerInterceptor[] interceptors = getInterceptors();
- if (!ObjectUtils.isEmpty(interceptors)) {
- for (int i = 0; i <interceptors.length; i++) {
- HandlerInterceptor interceptor = interceptors[i];
- // 接收 preHandle 返回值, false 则终止
- if (!interceptor.preHandle(request, response, this.handler)) {
- triggerAfterCompletion(request, response, null);
- return false;
- }
- this.interceptorIndex = i;
- }
- }
- return true;
- }
- // mv = ha.handle(processedRequest, response, mappedHandler.getHandler()); 真正调用 handler 处理业务
- // org.springframework.Web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter
- // method.AbstractHandlerMethodAdapter
- /**
- * This implementation expects the handler to be an {@link HandlerMethod}.
- */
- @Override
- public final ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler)
- throws Exception {
- // 内部调用
- return handleInternal(request, response, (HandlerMethod) handler);
- }
- //
- @Override
- protected ModelAndView handleInternal(HttpServletRequest request,
- HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {
- ModelAndView mav;
- // 预检查是否可支持
- checkRequest(request);
- // Execute invokeHandlerMethod in synchronized block if required.
- if (this.synchronizeOnSession) {
- HttpSession session = request.getSession(false);
- if (session != null) {
- Object mutex = WebUtils.getSessionMutex(session);
- synchronized (mutex) {
- mav = invokeHandlerMethod(request, response, handlerMethod);
- }
- }
- else {
- // No HttpSession available -> no mutex necessary
- mav = invokeHandlerMethod(request, response, handlerMethod);
- }
- }
- else {
- // No synchronization on session demanded at all...
- // invoke
- mav = invokeHandlerMethod(request, response, handlerMethod);
- }
- if (!response.containsHeader(HEADER_CACHE_CONTROL)) {
- if (getSessionAttributesHandler(handlerMethod).hasSessionAttributes()) {
- applyCacheSeconds(response, this.cacheSecondsForSessionAttributeHandlers);
- }
- else {
- prepareResponse(response);
- }
- }
- return mav;
- }
- //org.springframework.Web.servlet.support.WebContentGenerator.checkRequest()
- /**
- * Check the given request for supported methods and a required session, if any.
- * @param request current HTTP request
- * @throws ServletException if the request cannot be handled because a check failed
- * @since 4.2
- */
- protected final void checkRequest(HttpServletRequest request) throws ServletException {
- // Check whether we should support the request method.
- String method = request.getMethod();
- if (this.supportedMethods != null && !this.supportedMethods.contains(method)) {
- throw new HttpRequestMethodNotSupportedException(method, this.supportedMethods);
- }
- // Check whether a session is required.
- if (this.requireSession && request.getSession(false) == null) {
- throw new HttpSessionRequiredException("Pre-existing session required but none found");
- }
- }
- View Code
- // 调用 Adapter 的 handler 处理方法
- /**
- * Invoke the {@link RequestMapping} handler method preparing a {@link ModelAndView}
- * if view resolution is required.
- * @since 4.2
- * @see #createInvocableHandlerMethod(HandlerMethod)
- */
- protected ModelAndView invokeHandlerMethod(HttpServletRequest request,
- HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {
- ServletWebRequest webRequest = new ServletWebRequest(request, response);
- try {
- // 获取参数绑定工厂
- WebDataBinderFactory binderFactory = getDataBinderFactory(handlerMethod);
- // 参数解析
- ModelFactory modelFactory = getModelFactory(handlerMethod, binderFactory);
- // createInvocableHandlerMethod, 反射调用工具类
- ServletInvocableHandlerMethod invocableMethod = createInvocableHandlerMethod(handlerMethod);
- invocableMethod.setHandlerMethodArgumentResolvers(this.argumentResolvers);
- invocableMethod.setHandlerMethodReturnValueHandlers(this.returnValueHandlers);
- invocableMethod.setDataBinderFactory(binderFactory);
- invocableMethod.setParameterNameDiscoverer(this.parameterNameDiscoverer);
- // mav
- ModelAndViewContainer mavContainer = new ModelAndViewContainer();
- mavContainer.addAllAttributes(RequestContextUtils.getInputFlashMap(request));
- modelFactory.initModel(webRequest, mavContainer, invocableMethod);
- mavContainer.setIgnoreDefaultModelOnRedirect(this.ignoreDefaultModelOnRedirect);
- AsyncWebRequest asyncWebRequest = WebAsyncUtils.createAsyncWebRequest(request, response);
- asyncWebRequest.setTimeout(this.asyncRequestTimeout);
- WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
- asyncManager.setTaskExecutor(this.taskExecutor);
- asyncManager.setAsyncWebRequest(asyncWebRequest);
- asyncManager.registerCallableInterceptors(this.callableInterceptors);
- asyncManager.registerDeferredResultInterceptors(this.deferredResultInterceptors);
- if (asyncManager.hasConcurrentResult()) {
- Object result = asyncManager.getConcurrentResult();
- mavContainer = (ModelAndViewContainer) asyncManager.getConcurrentResultContext()[0];
- asyncManager.clearConcurrentResult();
- if (logger.isDebugEnabled()) {
- logger.debug("Found concurrent result value [" + result + "]");
- }
- invocableMethod = invocableMethod.wrapConcurrentResult(result);
- }
- // 调用业务 controller
- invocableMethod.invokeAndHandle(webRequest, mavContainer);
- if (asyncManager.isConcurrentHandlingStarted()) {
- return null;
- }
- // 返回 mv, jsp, 如果是 JSON 一类的输出, 则直接返回 null
- return getModelAndView(mavContainer, modelFactory, webRequest);
- }
- finally {
- webRequest.requestCompleted();
- }
- }
细节点的工厂类创建:
// 参数绑定工厂 private WebDataBinderFactory getDataBinderFactory(HandlerMethod handlerMethod) throws Exception { Class<?> handlerType = handlerMethod.getBeanType(); Set<Method> methods = this.initBinderCache.get(handlerType); if (methods == null) { methods = MethodIntrospector.selectMethods(handlerType, INIT_BINDER_METHODS); this.initBinderCache.put(handlerType, methods); } List<InvocableHandlerMethod> initBinderMethods = new ArrayList<InvocableHandlerMethod>(); // Global methods first for (Entry<ControllerAdviceBean, Set<Method>> entry : this.initBinderAdviceCache.entrySet()) { if (entry.getKey().isApplicableToBeanType(handlerType)) { Object bean = entry.getKey().resolveBean(); for (Method method : entry.getValue()) { initBinderMethods.add(createInitBinderMethod(bean, method)); } } } for (Method method : methods) { Object bean = handlerMethod.getBean(); initBinderMethods.add(createInitBinderMethod(bean, method)); } return createDataBinderFactory(initBinderMethods); } // 参数解析工厂 private ModelFactory getModelFactory(HandlerMethod handlerMethod, WebDataBinderFactory binderFactory) { SessionAttributesHandler sessionAttrHandler = getSessionAttributesHandler(handlerMethod); Class<?> handlerType = handlerMethod.getBeanType(); Set<Method> methods = this.modelAttributeCache.get(handlerType); if (methods == null) { methods = MethodIntrospector.selectMethods(handlerType, MODEL_ATTRIBUTE_METHODS); this.modelAttributeCache.put(handlerType, methods); } List<InvocableHandlerMethod> attrMethods = new ArrayList<InvocableHandlerMethod>(); // Global methods first for (Entry<ControllerAdviceBean, Set<Method>> entry : this.modelAttributeAdviceCache.entrySet()) { if (entry.getKey().isApplicableToBeanType(handlerType)) { Object bean = entry.getKey().resolveBean(); for (Method method : entry.getValue()) { attrMethods.add(createModelAttributeMethod(binderFactory, bean, method)); } } } for (Method method : methods) { Object bean = handlerMethod.getBean(); attrMethods.add(createModelAttributeMethod(binderFactory, bean, method)); } return new ModelFactory(attrMethods, binderFactory, sessionAttrHandler); } View Code // org.springframework.Web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle() /** * Invoke the method and handle the return value through one of the * configured {@link HandlerMethodReturnValueHandler}s. * @param webRequest the current request * @param mavContainer the ModelAndViewContainer for this request * @param providedArgs "given" arguments matched by type (not resolved) */ public void invokeAndHandle(ServletWebRequest webRequest, ModelAndViewContainer mavContainer, Object... providedArgs) throws Exception { // 直接调用 handler Object returnValue = invokeForRequest(webRequest, mavContainer, providedArgs); // 设置响应码 setResponseStatus(webRequest); if (returnValue == null) { if (isRequestNotModified(webRequest) || getResponseStatus() != null || mavContainer.isRequestHandled()) { mavContainer.setRequestHandled(true); return; } } else if (StringUtils.hasText(getResponseStatusReason())) { mavContainer.setRequestHandled(true); return; } mavContainer.setRequestHandled(false); try { // 处理返回值 this.returnValueHandlers.handleReturnValue( returnValue, getReturnValueType(returnValue), mavContainer, webRequest); } catch (Exception ex) { if (logger.isTraceEnabled()) { logger.trace(getReturnValueHandlingErrorMessage("Error handling return value", returnValue), ex); } throw ex; } } // InvocableHandlerMethod.invokeForRequest() /** * Invoke the method after resolving its argument values in the context of the given request. * <p>Argument values are commonly resolved through {@link HandlerMethodArgumentResolver}s. * The {@code providedArgs} parameter however may supply argument values to be used directly, * i.e. without argument resolution. Examples of provided argument values include a * {@link WebDataBinder}, a {@link SessionStatus}, or a thrown exception instance. * Provided argument values are checked before argument resolvers. * @param request the current request * @param mavContainer the ModelAndViewContainer for this request * @param providedArgs "given" arguments matched by type, not resolved * @return the raw value returned by the invoked method * @exception Exception raised if no suitable argument resolver can be found, * or if the method raised an exception */ public Object invokeForRequest(NativeWebRequest request, ModelAndViewContainer mavContainer, Object... providedArgs) throws Exception { // 获取方法参数 Object[] args = getMethodArgumentValues(request, mavContainer, providedArgs); if (logger.isTraceEnabled()) { logger.trace("Invoking'" + ClassUtils.getQualifiedMethodName(getMethod(), getBeanType()) + "'with arguments" + Arrays.toString(args)); } // invoke Object returnValue = doInvoke(args); if (logger.isTraceEnabled()) { logger.trace("Method [" + ClassUtils.getQualifiedMethodName(getMethod(), getBeanType()) + "] returned [" + returnValue + "]"); } return returnValue; } // 参数封装, InvocableHandlerMethod.getMethodArgumentValues() // InvocableHandlerMethod.getMethodArgumentValues() /** * Get the method argument values for the current request. */ private Object[] getMethodArgumentValues(NativeWebRequest request, ModelAndViewContainer mavContainer, Object... providedArgs) throws Exception { MethodParameter[] parameters = getMethodParameters(); Object[] args = new Object[parameters.length]; for (int i = 0; i <parameters.length; i++) { MethodParameter parameter = parameters[i]; parameter.initParameterNameDiscovery(this.parameterNameDiscoverer); args[i] = resolveProvidedArgument(parameter, providedArgs); if (args[i] != null) { continue; } if (this.argumentResolvers.supportsParameter(parameter)) { try { args[i] = this.argumentResolvers.resolveArgument( parameter, mavContainer, request, this.dataBinderFactory); continue; } catch (Exception ex) { if (logger.isDebugEnabled()) { logger.debug(getArgumentResolutionErrorMessage("Failed to resolve", i), ex); } throw ex; } } if (args[i] == null) { throw new IllegalStateException("Could not resolve method parameter at index" + parameter.getParameterIndex() + "in" + parameter.getMethod().toGenericString() + ":" + getArgumentResolutionErrorMessage("No suitable resolver for", i)); } } return args; } // org.springframework.Web.method.support.HandlerMethodArgumentResolverComposite /** * Iterate over registered {@link HandlerMethodArgumentResolver}s and invoke the one that supports it. * @throws IllegalStateException if no suitable {@link HandlerMethodArgumentResolver} is found. */ @Override public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer, NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception { HandlerMethodArgumentResolver resolver = getArgumentResolver(parameter); if (resolver == null) { throw new IllegalArgumentException("Unknown parameter type [" + parameter.getParameterType().getName() + "]"); } return resolver.resolveArgument(parameter, mavContainer, webRequest, binderFactory); } // AbstractNamedValueMethodArgumentResolver @Override public final Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer, NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception { NamedValueInfo namedValueInfo = getNamedValueInfo(parameter); MethodParameter nestedParameter = parameter.nestedIfOptional(); // 处理 key Object resolvedName = resolveStringValue(namedValueInfo.name); if (resolvedName == null) { throw new IllegalArgumentException( "Specified name must not resolve to null: [" + namedValueInfo.name + "]"); } // 获取 value Object arg = resolveName(resolvedName.toString(), nestedParameter, webRequest); if (arg == null) { if (namedValueInfo.defaultValue != null) { arg = resolveStringValue(namedValueInfo.defaultValue); } else if (namedValueInfo.required && !nestedParameter.isOptional()) { handleMissingValue(namedValueInfo.name, nestedParameter, webRequest); } arg = handleNullValue(namedValueInfo.name, arg, nestedParameter.getNestedParameterType()); } else if ("".equals(arg) && namedValueInfo.defaultValue != null) { arg = resolveStringValue(namedValueInfo.defaultValue); } if (binderFactory != null) { WebDataBinder binder = binderFactory.createBinder(webRequest, null, namedValueInfo.name); try { arg = binder.convertIfNecessary(arg, parameter.getParameterType(), parameter); } catch (ConversionNotSupportedException ex) { throw new MethodArgumentConversionNotSupportedException(arg, ex.getRequiredType(), namedValueInfo.name, parameter, ex.getCause()); } catch (TypeMismatchException ex) { throw new MethodArgumentTypeMismatchException(arg, ex.getRequiredType(), namedValueInfo.name, parameter, ex.getCause()); } } handleResolvedValue(arg, namedValueInfo.name, parameter, mavContainer, webRequest); return arg; } View Code // Object returnValue = doInvoke(args); // InvocableHandlerMethod.doInvoke(), 正常返回结果, 异常包装返回 /** * Invoke the handler method with the given argument values. */ protected Object doInvoke(Object... args) throws Exception { // 确保方法可调用 ReflectionUtils.makeAccessible(getBridgedMethod()); try { // 调用业务方法 return getBridgedMethod().invoke(getBean(), args); } catch (IllegalArgumentException ex) { assertTargetBean(getBridgedMethod(), getBean(), args); String text = (ex.getMessage() != null ? ex.getMessage() : "Illegal argument"); throw new IllegalStateException(getInvocationErrorMessage(text, args), ex); } catch (InvocationTargetException ex) { // Unwrap for HandlerExceptionResolvers ... Throwable targetException = ex.getTargetException(); if (targetException instanceof RuntimeException) { throw (RuntimeException) targetException; } else if (targetException instanceof Error) { throw (Error) targetException; } else if (targetException instanceof Exception) { throw (Exception) targetException; } else { String text = getInvocationErrorMessage("Failed to invoke handler method", args); throw new IllegalStateException(text, targetException); } } } // 至此业务方法终于调用完成 // 接下来, 处理返回值 // this.returnValueHandlers.handleReturnValue( // returnValue, getReturnValueType(returnValue), mavContainer, webRequest); // org.springframework.Web.method.support.HandlerMethodReturnValueHandlerComposite.handleReturnValue() /** * Iterate over registered {@link HandlerMethodReturnValueHandler}s and invoke the one that supports it. * @throws IllegalStateException if no suitable {@link HandlerMethodReturnValueHandler} is found. */ @Override public void handleReturnValue(Object returnValue, MethodParameter returnType, ModelAndViewContainer mavContainer, NativeWebRequest webRequest) throws Exception { HandlerMethodReturnValueHandler handler = selectHandler(returnValue, returnType); if (handler == null) { throw new IllegalArgumentException("Unknown return value type:" + returnType.getParameterType().getName()); } handler.handleReturnValue(returnValue, returnType, mavContainer, webRequest); } // 返回值处理器 // org.springframework.Web.servlet.mvc.method.annotation.ModelAndViewMethodReturnValueHandler // org.springframework.Web.method.annotation.ModelMethodProcessor // org.springframework.Web.servlet.mvc.method.annotation.ViewMethodReturnValueHandler // org.springframework.Web.servlet.mvc.method.annotation.ResponseBodyEmitterReturnValueHandler // org.springframework.Web.servlet.mvc.method.annotation.StreamingResponseBodyReturnValueHandler // org.springframework.Web.servlet.mvc.method.annotation.HttpEntityMethodProcessor // org.springframework.Web.servlet.mvc.method.annotation.HttpHeadersReturnValueHandler // org.springframework.Web.servlet.mvc.method.annotation.CallableMethodReturnValueHandler // org.springframework.Web.servlet.mvc.method.annotation.DeferredResultMethodReturnValueHandler // org.springframework.Web.servlet.mvc.method.annotation.AsyncTaskMethodReturnValueHandler // org.springframework.Web.method.annotation.ModelAttributeMethodProcessor // org.springframework.Web.servlet.mvc.method.annotation.RequestResponseBodyMethodProcessor // org.springframework.Web.servlet.mvc.method.annotation.ViewNameMethodReturnValueHandler // org.springframework.Web.method.annotation.MapMethodProcessor // org.springframework.Web.method.annotation.ModelAttributeMethodProcessor private HandlerMethodReturnValueHandler selectHandler(Object value, MethodParameter returnType) { boolean isAsyncValue = isAsyncReturnValue(value, returnType); for (HandlerMethodReturnValueHandler handler : this.returnValueHandlers) { if (isAsyncValue && !(handler instanceof AsyncHandlerMethodReturnValueHandler)) { continue; } // return Callable.class.isAssignableFrom(returnType.getParameterType()); // return (AnnotatedElementUtils.hasAnnotation(returnType.getContainingClass(), ResponseBody.class) || // returnType.hasMethodAnnotation(ResponseBody.class)); if (handler.supportsReturnType(returnType)) { return handler; } } return null; } // org.springframework.Web.servlet.mvc.method.annotation.RequestResponseBodyMethodProcessor.handleReturnValue(), 处理返回结果 @Override public void handleReturnValue(Object returnValue, MethodParameter returnType, ModelAndViewContainer mavContainer, NativeWebRequest webRequest) throws IOException, HttpMediaTypeNotAcceptableException, HttpMessageNotWritableException { mavContainer.setRequestHandled(true); ServletServerHttpRequest inputMessage = createInputMessage(webRequest); ServletServerHttpResponse outputMessage = createOutputMessage(webRequest); // 写返回值 // Try even with null return value. ResponseBodyAdvice could get involved. writeWithMessageConverters(returnValue, returnType, inputMessage, outputMessage); } /** * Creates a new {@link HttpOutputMessage} from the given {@link NativeWebRequest}. * @param webRequest the Web request to create an output message from * @return the output message */ protected ServletServerHttpResponse createOutputMessage(NativeWebRequest webRequest) { HttpServletResponse response = webRequest.getNativeResponse(HttpServletResponse.class); return new ServletServerHttpResponse(response); } /** * Writes the given return type to the given output message. * @param value the value to write to the output message * @param returnType the type of the value * @param inputMessage the input messages. Used to inspect the {@code Accept} header. * @param outputMessage the output message to write to * @throws IOException thrown in case of I/O errors * @throws HttpMediaTypeNotAcceptableException thrown when the conditions indicated * by the {@code Accept} header on the request cannot be met by the message converters */ @SuppressWarnings("unchecked") protected <T> void writeWithMessageConverters(T value, MethodParameter returnType, ServletServerHttpRequest inputMessage, ServletServerHttpResponse outputMessage) throws IOException, HttpMediaTypeNotAcceptableException, HttpMessageNotWritableException { Object outputValue; Class<?> valueType; Type declaredType; if (value instanceof CharSequence) { // string 的返回值输出 outputValue = value.toString(); valueType = String.class; declaredType = String.class; } else { outputValue = value; valueType = getReturnValueType(outputValue, returnType); declaredType = getGenericType(returnType); } HttpServletRequest request = inputMessage.getServletRequest(); // 获取返回的 content-type 值 List<MediaType> requestedMediaTypes = getAcceptableMediaTypes(request); List<MediaType> producibleMediaTypes = getProducibleMediaTypes(request, valueType, declaredType); if (outputValue != null && producibleMediaTypes.isEmpty()) { throw new IllegalArgumentException("No converter found for return value of type:" + valueType); } Set<MediaType> compatibleMediaTypes = new LinkedHashSet<MediaType>(); for (MediaType requestedType : requestedMediaTypes) { for (MediaType producibleType : producibleMediaTypes) { if (requestedType.isCompatibleWith(producibleType)) { compatibleMediaTypes.add(getMostSpecificMediaType(requestedType, producibleType)); } } } if (compatibleMediaTypes.isEmpty()) { if (outputValue != null) { throw new HttpMediaTypeNotAcceptableException(producibleMediaTypes); } return; } List<MediaType> mediaTypes = new ArrayList<MediaType>(compatibleMediaTypes); MediaType.sortBySpecificityAndQuality(mediaTypes); // 确定输出的类型 MediaType selectedMediaType = null; for (MediaType mediaType : mediaTypes) { if (mediaType.isConcrete()) { selectedMediaType = mediaType; break; } else if (mediaType.equals(MediaType.ALL) || mediaType.equals(MEDIA_TYPE_APPLICATION)) { selectedMediaType = MediaType.APPLICATION_OCTET_STREAM; break; } } if (selectedMediaType != null) { selectedMediaType = selectedMediaType.removeQualityValue(); // 使用 messageConverter 进行返回值转换 // org.springframework.http.converter.ByteArrayHttpMessageConverter // org.springframework.http.converter.StringHttpMessageConverter // org.springframework.http.converter.ResourceHttpMessageConverter // org.springframework.http.converter.xml.SourceHttpMessageConverter // org.springframework.http.converter.support.AllEncompassingFormHttpMessageConverter // org.springframework.http.converter.xml.Jaxb2RootElementHttpMessageConverter // org.springframework.http.converter.ByteArrayHttpMessageConverter // org.springframework.http.converter.StringHttpMessageConverter // org.springframework.http.converter.ResourceHttpMessageConverter // org.springframework.http.converter.xml.SourceHttpMessageConverter // org.springframework.http.converter.support.AllEncompassingFormHttpMessageConverter // org.springframework.http.converter.xml.Jaxb2RootElementHttpMessageConverter for (HttpMessageConverter<?> messageConverter : this.messageConverters) { if (messageConverter instanceof GenericHttpMessageConverter) { if (((GenericHttpMessageConverter) messageConverter).canWrite( declaredType, valueType, selectedMediaType)) { outputValue = (T) getAdvice().beforeBodyWrite(outputValue, returnType, selectedMediaType, (Class<? extends HttpMessageConverter<?>>) messageConverter.getClass(), inputMessage, outputMessage); if (outputValue != null) { addContentDispositionHeader(inputMessage, outputMessage); ((GenericHttpMessageConverter) messageConverter).write( outputValue, declaredType, selectedMediaType, outputMessage); if (logger.isDebugEnabled()) { logger.debug("Written [" + outputValue + "] as \"" + selectedMediaType + "\" using ["+ messageConverter +"]"); } } return; } } else if (messageConverter.canWrite(valueType, selectedMediaType)) { // 输出值 outputValue = (T) getAdvice().beforeBodyWrite(outputValue, returnType, selectedMediaType, (Class<? extends HttpMessageConverter<?>>) messageConverter.getClass(), inputMessage, outputMessage); if (outputValue != null) { addContentDispositionHeader(inputMessage, outputMessage); // write ((HttpMessageConverter) messageConverter).write(outputValue, selectedMediaType, outputMessage); if (logger.isDebugEnabled()) { logger.debug("Written [" + outputValue + "] as \"" + selectedMediaType + "\" using ["+ messageConverter +"]"); } } return; } } } if (outputValue != null) { throw new HttpMediaTypeNotAcceptableException(this.allSupportedMediaTypes); } } // 消息转换器 // org.springframework.http.converter.StringHttpMessageConverter // org.springframework.http.converter.AbstractHttpMessageConverter // org.springframework.http.converter.StringHttpMessageConverter // org.springframework.http.converter.AbstractHttpMessageConverter /** * This implementation sets the default headers by calling {@link #addDefaultHeaders}, * and then calls {@link #writeInternal}. */ @Override public final void write(final T t, MediaType contentType, HttpOutputMessage outputMessage) throws IOException, HttpMessageNotWritableException { final HttpHeaders headers = outputMessage.getHeaders(); addDefaultHeaders(headers, t, contentType); if (outputMessage instanceof StreamingHttpOutputMessage) { StreamingHttpOutputMessage streamingOutputMessage = (StreamingHttpOutputMessage) outputMessage; streamingOutputMessage.setBody(new StreamingHttpOutputMessage.Body() { @Override public void writeTo(final OutputStream outputStream) throws IOException { writeInternal(t, new HttpOutputMessage() { @Override public OutputStream getBody() throws IOException { return outputStream; } @Override public HttpHeaders getHeaders() { return headers; } }); } }); } else { // write writeInternal(t, outputMessage); // 再次 flush() outputMessage.getBody().flush(); } } // StringHttpMessageConverter @Override protected void writeInternal(String str, HttpOutputMessage outputMessage) throws IOException { if (this.writeAcceptCharset) { outputMessage.getHeaders().setAcceptCharset(getAcceptedCharsets()); } Charset charset = getContentTypeCharset(outputMessage.getHeaders().getContentType()); StreamUtils.copy(str, charset, outputMessage.getBody()); } // org.springframework.util.StreamUtils.copy() // 像 socket 中写入返回值, 即向浏览器一类请求客户端输出返回值, 到此, 客户端得到响应 /** * Copy the contents of the given String to the given output OutputStream. * Leaves the stream open when done. * @param in the String to copy from * @param charset the Charset * @param out the OutputStream to copy to * @throws IOException in case of I/O errors */ public static void copy(String in, Charset charset, OutputStream out) throws IOException { Assert.notNull(in, "No input String specified"); Assert.notNull(charset, "No charset specified"); Assert.notNull(out, "No OutputStream specified"); Writer writer = new OutputStreamWriter(out, charset); writer.write(in); writer.flush(); } // return getModelAndView(mavContainer, modelFactory, webRequest); // RequestMappingHandlerAdapter // return getModelAndView(mavContainer, modelFactory, webRequest); // RequestMappingHandlerAdapter private ModelAndView getModelAndView(ModelAndViewContainer mavContainer, ModelFactory modelFactory, NativeWebRequest webRequest) throws Exception { // 如果已经处理完成, 返回 null modelFactory.updateModel(webRequest, mavContainer); if (mavContainer.isRequestHandled()) { return null; } // 否则处理 mv ModelMap model = mavContainer.getModel(); ModelAndView mav = new ModelAndView(mavContainer.getViewName(), model, mavContainer.getStatus()); if (!mavContainer.isViewReference()) { mav.setView((View) mavContainer.getView()); } if (model instanceof RedirectAttributes) { Map<String, ?> flashAttributes = ((RedirectAttributes) model).getFlashAttributes(); HttpServletRequest request = webRequest.getNativeRequest(HttpServletRequest.class); RequestContextUtils.getOutputFlashMap(request).putAll(flashAttributes); } return mav; } // prepareResponse(response); // RequestMappingHandlerAdapter /** * Prepare the given response according to the settings of this generator. * Applies the number of cache seconds specified for this generator. * @param response current HTTP response * @since 4.2 */ protected final void prepareResponse(HttpServletResponse response) { if (this.cacheControl != null) { applyCacheControl(response, this.cacheControl); } else { applyCacheSeconds(response, this.cacheSeconds); } if (servlet3Present && this.varyByRequestHeaders != null) { for (String value : getVaryRequestHeadersToAdd(response)) { response.addHeader("Vary", value); } } } View Code // mappedHandler.applyPostHandle(processedRequest, response, mv); // org.springframework.Web.servlet.HandlerExecutionChain.applyPostHandle(), 调用 interceptor.postHandle() // org.springframework.Web.servlet.HandlerExecutionChain.applyPostHandle(), 调用 interceptor.postHandle() /** * Apply postHandle methods of registered interceptors. */ void applyPostHandle(HttpServletRequest request, HttpServletResponse response, ModelAndView mv) throws Exception { HandlerInterceptor[] interceptors = getInterceptors(); if (!ObjectUtils.isEmpty(interceptors)) { for (int i = interceptors.length - 1; i>= 0; i--) { HandlerInterceptor interceptor = interceptors[i]; interceptor.postHandle(request, response, this.handler, mv); } } } // processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException); /** * Handle the result of handler selection and handler invocation, which is * either a ModelAndView or an Exception to be resolved to a ModelAndView. */ private void processDispatchResult(HttpServletRequest request, HttpServletResponse response, HandlerExecutionChain mappedHandler, ModelAndView mv, Exception exception) throws Exception { boolean errorView = false; // 发生异常, 使用 errorView if (exception != null) { if (exception instanceof ModelAndViewDefiningException) { logger.debug("ModelAndViewDefiningException encountered", exception); mv = ((ModelAndViewDefiningException) exception).getModelAndView(); } else { Object handler = (mappedHandler != null ? mappedHandler.getHandler() : null); mv = processHandlerException(request, response, handler, exception); errorView = (mv != null); } } // Did the handler return a view to render? if (mv != null && !mv.wasCleared()) { render(mv, request, response); if (errorView) { WebUtils.clearErrorRequestAttributes(request); } } else { if (logger.isDebugEnabled()) { logger.debug("Null ModelAndView returned to DispatcherServlet with name'" + getServletName() + "': assuming HandlerAdapter completed request handling"); } } if (WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted()) { // Concurrent handling started during a forward return; } if (mappedHandler != null) { // 最后再一个触发器钩子, 触发 interceptor.afterCompletion() mappedHandler.triggerAfterCompletion(request, response, null); } } /** * Trigger afterCompletion callbacks on the mapped HandlerInterceptors. * Will just invoke afterCompletion for all interceptors whose preHandle invocation * has successfully completed and returned true. */ void triggerAfterCompletion(HttpServletRequest request, HttpServletResponse response, Exception ex) throws Exception { HandlerInterceptor[] interceptors = getInterceptors(); if (!ObjectUtils.isEmpty(interceptors)) { for (int i = this.interceptorIndex; i>= 0; i--) { HandlerInterceptor interceptor = interceptors[i]; try { interceptor.afterCompletion(request, response, this.handler, ex); } catch (Throwable ex2) { logger.error("HandlerInterceptor.afterCompletion threw exception", ex2); } } } } // 依次返回 doDispatch(), 清理环境 // 返回 FrameworkServlet.doService() // FrameworkServlet finally 中还会触发 requestAttributes.requestCompleted(); // org.springframework.Web.context.request.ServletRequestAttributes.requestCompleted() /** * Signal that the request has been completed. * <p>Executes all request destruction callbacks and updates the * session attributes that have been accessed during request processing. */ public void requestCompleted() { executeRequestDestructionCallbacks(); updateAccessedSessionAttributes(); this.requestActive = false; } /** * Execute all callbacks that have been registered for execution * after request completion. */ private void executeRequestDestructionCallbacks() { synchronized (this.requestDestructionCallbacks) { for (Runnable runnable : this.requestDestructionCallbacks.values()) { // 同步调用 DestructionCallbacks.run() runnable.run(); } this.requestDestructionCallbacks.clear(); } } /** * Update all accessed session attributes through {@code session.setAttribute} * calls, explicitly indicating to the container that they might have been modified. */ @Override protected void updateAccessedSessionAttributes() { if (!this.sessionAttributesToUpdate.isEmpty()) { // Update all affected session attributes. HttpSession session = getSession(false); if (session != null) { try { for (Map.Entry<String, Object> entry : this.sessionAttributesToUpdate.entrySet()) { String name = entry.getKey(); Object newValue = entry.getValue(); Object oldValue = session.getAttribute(name); if (oldValue == newValue && !isImmutableSessionAttribute(name, newValue)) { session.setAttribute(name, newValue); } } } catch (IllegalStateException ex) { // Session invalidated - shouldn't usually happen. } } this.sessionAttributesToUpdate.clear(); } } // 最后 publishRequestHandledEvent(request, response, startTime, failureCause); // FrameworkServlet private void publishRequestHandledEvent( HttpServletRequest request, HttpServletResponse response, long startTime, Throwable failureCause) { if (this.publishEvents) { // Whether or not we succeeded, publish an event. long processingTime = System.currentTimeMillis() - startTime; int statusCode = (responseGetStatusAvailable ? response.getStatus() : -1); // 发布 event 到 spring, 使感兴趣的监听者进行相应处理 this.webApplicationContext.publishEvent( new ServletRequestHandledEvent(this, request.getRequestURI(), request.getRemoteAddr(), request.getMethod(), getServletConfig().getServletName(), WebUtils.getSessionId(request), getUsernameForRequest(request), processingTime, failureCause, statusCode)); } }
来源: http://www.tuicool.com/articles/Mnuiaeq