简单读!spring-mvc源码之穿越http请求
相信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();
- }
- }
// 最后,会先调用 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);
- }
// 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;
- }
// 关键: 获取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)));
- }
- }
- }
// 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);
- }
- }
// 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");
- }
- }
// 调用 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);
- }
// 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;
- }
// 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);
- }
- }
- }
// 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));
- }
- }
总结几个技术要点:
1. filterchain 责任链模式的使用;
2. 工厂模式的使用;
3. 拦截器监听;
4. 各功能组件化,职责分明;
5. 模板方法模式使用;
6. mvc;
7. 适配器模式应用;
8. 观察者模式应用;
简单读!spring-mvc源码之穿越http请求的更多相关文章
- Spring MVC源码——Root WebApplicationContext
目录 Spring MVC源码--Root WebApplicationContext 上下文层次结构 Root WebApplicationContext 初始化和销毁 ContextLoaderL ...
- 精尽Spring MVC源码分析 - 寻找遗失的 web.xml
该系列文档是本人在学习 Spring MVC 的源码过程中总结下来的,可能对读者不太友好,请结合我的源码注释 Spring MVC 源码分析 GitHub 地址 进行阅读 Spring 版本:5.2. ...
- Spring MVC源码——Servlet WebApplicationContext
上一篇笔记(Spring MVC源码——Root WebApplicationContext)中记录了下 Root WebApplicationContext 的初始化代码.这一篇来看 Servlet ...
- 精尽Spring MVC源码分析 - MultipartResolver 组件
该系列文档是本人在学习 Spring MVC 的源码过程中总结下来的,可能对读者不太友好,请结合我的源码注释 Spring MVC 源码分析 GitHub 地址 进行阅读 Spring 版本:5.2. ...
- 精尽Spring MVC源码分析 - HandlerMapping 组件(一)之 AbstractHandlerMapping
该系列文档是本人在学习 Spring MVC 的源码过程中总结下来的,可能对读者不太友好,请结合我的源码注释 Spring MVC 源码分析 GitHub 地址 进行阅读 Spring 版本:5.2. ...
- 精尽Spring MVC源码分析 - HandlerMapping 组件(四)之 AbstractUrlHandlerMapping
该系列文档是本人在学习 Spring MVC 的源码过程中总结下来的,可能对读者不太友好,请结合我的源码注释 Spring MVC 源码分析 GitHub 地址 进行阅读 Spring 版本:5.2. ...
- 精尽Spring MVC源码分析 - HandlerAdapter 组件(三)之 HandlerMethodArgumentResolver
该系列文档是本人在学习 Spring MVC 的源码过程中总结下来的,可能对读者不太友好,请结合我的源码注释 Spring MVC 源码分析 GitHub 地址 进行阅读 Spring 版本:5.2. ...
- 精尽Spring MVC源码分析 - HandlerAdapter 组件(四)之 HandlerMethodReturnValueHandler
该系列文档是本人在学习 Spring MVC 的源码过程中总结下来的,可能对读者不太友好,请结合我的源码注释 Spring MVC 源码分析 GitHub 地址 进行阅读 Spring 版本:5.2. ...
- 精尽Spring MVC源码分析 - HandlerExceptionResolver 组件
该系列文档是本人在学习 Spring MVC 的源码过程中总结下来的,可能对读者不太友好,请结合我的源码注释 Spring MVC 源码分析 GitHub 地址 进行阅读 Spring 版本:5.2. ...
随机推荐
- C语言编写程序计算圆上的点的坐标
Problem Description There is a cycle with its center on the origin. Now give you a point on the cycl ...
- 谈谈逆向android里面的so
1. 加密sgf算法分析 2.gnugo瘦身记
- centos vi设置tab为4个空格 和括号自动补全
1.打开vim配置文件 vi /etc/vimrc 2.设置tab为4个空格, 在文件末尾添加以下内容 if has( "autocmd" ) filetype plugin in ...
- Isight Linux 2016hf2 安装步骤
把License文件整个拷进去,都给执行权限 把ABAQUS.lic 里的 this_host 改为psn004 27011 改为26011 (区别于2017hf2) 杀掉2017的server ./ ...
- qt布局管理-缩放、多窗口切换
关于缩放的布局 所有控件要求与主窗口同时缩放,那么在所有控件设置好布局后,最后点击最外侧的主控件,选择水平布局或者垂直布局即可. 另外每个空间的属性可更改,如SizePolicy用于设置空间是否可缩放 ...
- python 10 迭代器和三元运算符
一.迭代器 1.迭代器协议:对象必须提供一种next方法,执行该方法要么返回迭代中的下一项,要么引起一个stopIteration异常,终止迭代 2.可迭代对象:实现了迭代器协议的对象 3.pytho ...
- 博客1--tensorflow的图像基本处理操作
话不多,具体内容在开源中国里我的博客:https://my.oschina.net/u/3770644 代码: #!/usr/bin/env python# -*- coding:utf-8 -*-# ...
- PYTHON基础入门(内置函数、推导式)学习
**内建函数**1.通过使用dir()函数可以列出所具备的方法 例:num = 10 dir(num) 例:myList = [1,2,3,4,5,6] dir(num)2.通过使用help()函数可 ...
- mac ssh,mac xshell,xshell替代,ssh客户端,ssh工具,远程桌面加速
下载地址 Windows版下载地址:http://www.hostbuf.com/downloads/finalshell_install.exe Mac版,Linux版安装及教程:http://ww ...
- svn的基本使用方法
一,svn的介绍 Subversion(SVN) 是一个开源的版本控制系統, 也就是说 Subversion 管理着随时间改变的数据. 这些数据放置在一个中央资料档案库(repository) 中. ...