您当前的位置:首页 > 电脑百科 > 程序开发 > 框架

SpringBoot中的Controller详解

时间:2021-11-04 11:17:04  来源:  作者:程序yuan

SpringBoot中的Controller注册

本篇将会以Servlet为切入点,通过源码来看web容器中的Controller是如何注册到HandlerMApping中。请求来了之后,web容器是如何根据请求路径找到对应的Controller方法并执行的。

先讲下本文的大概思路和流程图:

  1. 我们经常使用的RequestMapping这个注解对应的方法最终会被RequestMappingHandlerMapping处理,并封装成一个HandlerMethod注入到自己内部的mappingRegistry 容器中。这一步是Controller的注册,被执行的触发点是因为RequestMappingHandlerMapping这个类实现了InitializingBean接口,由Spring容器触发。
  2. Tomcat容器被启动的时候,最后会调用Servlet的init方法,这里会把所有的HandlerMapping注册到自己内部的handlerMappings属性中。这样Servlet和RequestMapping注解的Controller就建立起了间接关系。
  3. 当请求到来的时候,tomcat拿到并封装好请求体后会调用Servlet的service方法。这个方法最终会走到 DispatcherServlet的doDispatch方法,这个方法中会找到最适合的HandlerMapping并取出对应的HadlerMethod,然后给对应的HandlerAdapter执行.
  4. controller注册流程图
SpringBoot中的Controller详解

 

5. controller发现和使用流程图

SpringBoot中的Controller详解

 

正文开始

处理请求的DispatcherServlet

SpringBoot中的Controller详解

 

Servlet接口的源码

public interface Servlet {
    //初始化
    public void init(ServletConfig config) throws ServletException;

    //响应请求
    public void service(ServletRequest req, ServletResponse res)
            throws ServletException, IOException;

    //获取servlet信息
    public String getServletInfo();
        
    //服务停止时回调
    public void destroy();
}
复制代码

springboot内置了tomcat容器,而tomcat容器是遵循了servlet规范的。servlet规范中定义了初始化、响应、获取配置信息和销毁时回调钩子。从servlet的规范中可以看出,tomcat启动时会调用servlet的init方法,处理请求时会调用service方法,容器销毁时会调用destroy方法。servlet中最核心的实现就是我们所熟知的DispatchServlet,看下DispatchServlet的继承体系

SpringBoot中的Controller详解

 

从DispatchServlet的继承体系中,看下Servlet的初始化做了什么。

Servlet的初始化 init

HttpServletBean中的init方法源码

@Override
public final void init() throws ServletException {

   // 设置servlet的属性
   PropertyValues pvs = new ServletConfigPropertyValues(getServletConfig(), this.requiredProperties);
   if (!pvs.isEmpty()) {
      try {
         BeanWrapper bw = PropertyAccessorFactory.forBeanPropertyAccess(this);
         ResourceLoader resourceLoader = new ServletContextResourceLoader(getServletContext());
         bw.registerCustomEditor(Resource.class, new ResourceEditor(resourceLoader, getEnvironment()));
         initBeanWrapper(bw);
         bw.setPropertyValues(pvs, true);
      }
      catch (BeansException ex) {
         if (logger.isErrorEnabled()) {
            logger.error("Failed to set bean properties on servlet '" + getServletName() + "'", ex);
         }
         throw ex;
      }
   }

   // 具体的初始化方法交给子类实现
   initServletBean();
}
//空实现,具体的由子类实现
protected void initServletBean() throws ServletException {
}
复制代码

从HttpServletBean中的init方法可以看到,这里核心的就是设置了Servlet的一些 bean properties,继续到子类 FrameworkServlet中看initServletBean方法

@Override
protected final void initServletBean() throws ServletException {
   getServletContext().log("Initializing Spring " + getClass().getSimpleName() + " '" + getServletName() + "'");
   if (logger.isInfoEnabled()) {
      logger.info("Initializing Servlet '" + getServletName() + "'");
   }
   long startTime = System.currentTimeMillis();

   try {
      //这里初始化了web的上下文,这里会初始化Servlet的九大策略
      this.webApplicationContext = initWebApplicationContext();
      //这个方法也是空实现
      initFrameworkServlet();
   }
   catch (ServletException | RuntimeException ex) {
      logger.error("Context initialization failed", ex);
      throw ex;
   }

   if (logger.isDebugEnabled()) {
      String value = this.enableLoggingRequestDetails ?
            "shown which may lead to unsafe logging of potentially sensitive data" :
            "masked to prevent unsafe logging of potentially sensitive data";
      logger.debug("enableLoggingRequestDetails='" + this.enableLoggingRequestDetails +
            "': request parameters and headers will be " + value);
   }

   if (logger.isInfoEnabled()) {
      logger.info("Completed initialization in " + (System.currentTimeMillis() - startTime) + " ms");
   }
}

//初始化上下文
protected WebApplicationContext initWebApplicationContext() {
   WebApplicationContext rootContext =
         WebApplicationContextUtils.getWebApplicationContext(getServletContext());
   WebApplicationContext wac = null;

   if (this.webApplicationContext != null) {
      wac = this.webApplicationContext;
      if (wac instanceof ConfigurableWebApplicationContext) {
         ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext) wac;
         if (!cwac.isActive()) {
            if (cwac.getParent() == null) {
               cwac.setParent(rootContext);
            }
            configureAndRefreshWebApplicationContext(cwac);
         }
      }
   }
   if (wac == null) {
      wac = findWebApplicationContext();
   }
   if (wac == null) {
      wac = createWebApplicationContext(rootContext);
   }

   if (!this.refreshEventReceived) {
      synchronized (this.onRefreshMonitor) {
          //springboot只会进入这个方法,这个方法是空实现,具体实现在子类DispatchServlet
         onRefresh(wac);
      }
   }

   if (this.publishContext) {
      String attrName = getServletContextAttributeName();
      getServletContext().setAttribute(attrName, wac);
   }

   return wac;
}
复制代码

接着跟进DispatchServlet的onRefresh方法,这个方法中会初始化DispatchServlet的九大策略,这里我们只关心initHandlerMappings方法

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

//初始化策略
protected void initStrategies(ApplicationContext context) {
   initMultipartResolver(context);
   initLocaleResolver(context);
   initThemeResolver(context);
   //这个是我们关注的核心,Controller注册就在这里实现
   initHandlerMappings(context);
   //这个是处理Controller方法调用的,逻辑跟上面的initHandlerMappings差不多
   initHandlerAdapters(context);
   initHandlerExceptionResolvers(context);
   initRequestToViewNameTranslator(context);
   initViewResolvers(context);
   initFlashMapManager(context);
}
复制代码

核心看下initHandlerMappings方法

private void initHandlerMappings(ApplicationContext context) {
   this.handlerMappings = null;
    //默认为true
   if (this.detectAllHandlerMappings) {
      //默认的HandlerMapping有8个,这里我们只关心RequestMappingHandlerMapping这个类
      Map<String, HandlerMapping> matchingBeans =
            BeanFactoryUtils.beansOfTypeIncludingAncestors(context, HandlerMapping.class, true, false);
      if (!matchingBeans.isEmpty()) {
         this.handlerMappings = new ArrayList<>(matchingBeans.values());
         //排序
         AnnotationAwareOrderComparator.sort(this.handlerMappings);
      }
   }
   else {
      try {
         HandlerMapping hm = context.getBean(HANDLER_MAPPING_BEAN_NAME, HandlerMapping.class);
         //这里就让Serlvet和Controller建立起了间接关系了,这个方法主要是为了给handlerMappings属性赋值
         this.handlerMappings = Collections.singletonList(hm);
      }
      catch (NoSuchBeanDefinitionException ex) {
      }
   }

   //这里如果没有HanlderMapping的话,会给一个默认的处理
   if (this.handlerMappings == null) {
      this.handlerMappings = getDefaultStrategies(context, HandlerMapping.class);
      if (logger.isTraceEnabled()) {
         logger.trace("No HandlerMappings declared for servlet '" + getServletName() +
               "': using default strategies from DispatcherServlet.properties");
      }
   }
}
复制代码

看下默认的 HandlerMapping 有哪些

SpringBoot中的Controller详解

 

这里我们只关心
RequestMappingHandlerMapping这个类,这个类就是处理我们Controller上的RequestMapping注解的类。

注意这里的handlerMappings,后面处理请求的时候,会从handlerMappings中选择一个最合适的HandlerMapping来处理请求

Servlet的请求处理 service

HttpServlet中的service方法源码

/**
* 这个方法只是将ServletRequest强转为HttpServletRequest
* ServletResponse强转为HttpServletResponse
*/
@Override
public void service(ServletRequest req, ServletResponse res)
    throws ServletException, IOException {

    HttpServletRequest  request;
    HttpServletResponse response;

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

protected void service(HttpServletRequest req, HttpServletResponse resp)
    throws ServletException, IOException {
    //获取方法类型
    String method = req.getMethod();
    //根据不同的方法类型调用不同的方法,从doGet进去,看子类FrameworkServlet的doGet方法
    if (method.equals(METHOD_GET)) {
        long lastModified = getLastModified(req);
        if (lastModified == -1) {
            doGet(req, resp);
        } else {
            long ifModifiedSince;
            try {
                ifModifiedSince = req.getDateHeader(HEADER_IFMODSINCE);
            } catch (IllegalArgumentException iae) {
                ifModifiedSince = -1;
            }
            if (ifModifiedSince < (lastModified / 1000 * 1000)) {
                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 {
        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方法
@Override
protected final void doGet(HttpServletRequest request, HttpServletResponse response)
      throws ServletException, IOException {
    //继续跟
   processRequest(request, response);
}

rotected 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 {
       //这里处理请求,继续跟,看子类DispatchServlet的doService方法
      doService(request, response);
   }
   catch (ServletException | IOException ex) {
      failureCause = ex;
      throw ex;
   }
   catch (Throwable ex) {
      failureCause = ex;
      throw new NestedServletException("Request processing failed", ex);
   }

   finally {
      resetContextHolders(request, previousLocaleContext, previousAttributes);
      if (requestAttributes != null) {
         requestAttributes.requestCompleted();
      }
      logResult(request, response, failureCause, asyncManager);
      publishRequestHandledEvent(request, response, startTime, failureCause);
   }
}
复制代码

DispatchServlet的doService方法

@Override
protected void doService(HttpServletRequest request, HttpServletResponse response) throws Exception {
    // 打印日志
   logRequest(request);
   //保存快照
   Map<String, Object> attributesSnapshot = null;
   if (WebUtils.isIncludeRequest(request)) {
      attributesSnapshot = new HashMap<>();
      Enumeration<?> attrNames = request.getAttributeNames();
      while (attrNames.hasMoreElements()) {
         String attrName = (String) attrNames.nextElement();
         if (this.cleanupAfterInclude || attrName.startsWith(DEFAULT_STRATEGIES_PREFIX)) {
            attributesSnapshot.put(attrName, request.getAttribute(attrName));
         }
      }
   }
    //设置属性
   request.setAttribute(WEB_APPLICATION_CONTEXT_ATTRIBUTE, getWebApplicationContext());
   request.setAttribute(LOCALE_RESOLVER_ATTRIBUTE, this.localeResolver);
   request.setAttribute(THEME_RESOLVER_ATTRIBUTE, this.themeResolver);
   request.setAttribute(THEME_SOURCE_ATTRIBUTE, getThemeSource());

   if (this.flashMapManager != null) {
      FlashMap inputFlashMap = this.flashMapManager.retrieveAndUpdate(request, response);
      if (inputFlashMap != null) {
         request.setAttribute(INPUT_FLASH_MAP_ATTRIBUTE, Collections.unmodifiableMap(inputFlashMap));
      }
      request.setAttribute(OUTPUT_FLASH_MAP_ATTRIBUTE, new FlashMap());
      request.setAttribute(FLASH_MAP_MANAGER_ATTRIBUTE, this.flashMapManager);
   }

   try {
       //处理请求核心方法
      doDispatch(request, response);
   }
   finally {
      if (!WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted()) {
         if (attributesSnapshot != null) {
            restoreAttributesAfterInclude(request, attributesSnapshot);
         }
      }
   }
}
复制代码

DispatchServlet的doDispatch方法

SpringBoot中的Controller详解

 

protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
   HttpServletRequest processedRequest = request;
   HandlerExecutionChain mappedHandler = null;
   boolean multipartRequestParsed = false;

   WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);

   try {
      ModelAndView mv = null;
      Exception dispatchException = null;

      try {
         processedRequest = checkMultipart(request);
         multipartRequestParsed = (processedRequest != request);

         // 获取当前请求的handler
         mappedHandler = getHandler(processedRequest);
         if (mappedHandler == null) {
             //404
            noHandlerFound(processedRequest, response);
            return;
         }

         // 获取处理当前请求的handlerAdapter
         HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
         
         String method = request.getMethod();
         boolean isGet = "GET".equals(method);
         if (isGet || "HEAD".equals(method)) {
            long lastModified = ha.getLastModified(request, mappedHandler.getHandler());
            if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) {
               return;
            }
         }
          //调用前置拦截器
         if (!mappedHandler.applyPreHandle(processedRequest, response)) {
            return;
         }

         // 请求对应的方法在这里被执行
         mv = ha.handle(processedRequest, response, mappedHandler.getHandler());

         if (asyncManager.isConcurrentHandlingStarted()) {
            return;
         }

         applyDefaultViewName(processedRequest, mv);
         //调用后置拦截器
         mappedHandler.applyPostHandle(processedRequest, response, mv);
      }
      catch (Exception ex) {
         dispatchException = ex;
      }
      catch (Throwable err) {
         // As of 4.3, we're processing Errors thrown from handler methods as well,
         // making them available for @ExceptionHandler methods and other scenarIOS.
         dispatchException = new NestedServletException("Handler dispatch failed", err);
      }
      processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
   }
   catch (Exception ex) {
      triggerAfterCompletion(processedRequest, response, mappedHandler, ex);
   }
   catch (Throwable err) {
      triggerAfterCompletion(processedRequest, response, mappedHandler,
            new NestedServletException("Handler processing failed", err));
   }
   finally {
      if (asyncManager.isConcurrentHandlingStarted()) {
         // Instead of postHandle and afterCompletion
         if (mappedHandler != null) {
            mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);
         }
      }
      else {
         // Clean up any resources used by a multipart request.
         if (multipartRequestParsed) {
            cleanupMultipart(processedRequest);
         }
      }
   }
}

@Nullable
protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
   if (this.handlerMappings != null) {
      for (HandlerMapping mapping : this.handlerMappings) {
      //这里的mapping就是接下来要讲到的RequestMappingHandlerMapping
         HandlerExecutionChain handler = mapping.getHandler(request);
         if (handler != null) {
            return handler;
         }
      }
   }
   return null;
}
复制代码

这里可以记录Servlet处理请求的调用链 service -> doGet -> processRequest -> doService -> doDispatch

RequestMappingHandlerMapping 做了啥

SpringBoot中的Controller详解

 

从上面的继承图可以看出
RequestMappingHandlerMapping实现了InitializingBean接口,所以初始化的时候会调用afterPropertiesSet方法。

@Override
@SuppressWarnings("deprecation")
public void afterPropertiesSet() {
    //配置
   this.config = new RequestMappingInfo.BuilderConfiguration();
   this.config.setUrlPathHelper(getUrlPathHelper());
   this.config.setPathMatcher(getPathMatcher());
   this.config.setSuffixPatternMatch(useSuffixPatternMatch());
   this.config.setTrailingSlashMatch(useTrailingSlashMatch());
   this.config.setRegisteredSuffixPatternMatch(useRegisteredSuffixPatternMatch());
   this.config.setContentNegotiationManager(getContentNegotiationManager());
    //这里是核心,会把所有controller注册进去
   super.afterPropertiesSet();
}
复制代码

接着看父类
AbstractHandlerMethodMapping的afterPropertiesSet方法

@Override
public void afterPropertiesSet() {
   initHandlerMethods();
}

protected void initHandlerMethods() {
   for (String beanName : getCandidateBeanNames()) {
      if (!beanName.startsWith(SCOPED_TARGET_NAME_PREFIX)) {
      //这里会注册controller
         processCandidateBean(beanName);
      }
   }
   handlerMethodsInitialized(getHandlerMethods());
}

protected void processCandidateBean(String beanName) {
   Class<?> beanType = null;
   try {
      beanType = obtainApplicationContext().getType(beanName);
   }
   catch (Throwable ex) {
      if (logger.isTraceEnabled()) {
         logger.trace("Could not resolve type for bean '" + beanName + "'", ex);
      }
   }
   //这里的isHandler会判断是否有Controller或RequestMapping注解
   if (beanType != null && isHandler(beanType)) {
      //这里会注册controller,接着跟
      detectHandlerMethods(beanName);
   }
}

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

   if (handlerType != null) {
      Class<?> userType = ClassUtils.getUserClass(handlerType);
      Map<Method, T> methods = MethodIntrospector.selectMethods(userType,
            (MethodIntrospector.MetadataLookup<T>) method -> {
               try {
               //这里会把RequestMapping转换为RequestMappingInfo
                  return getMappingForMethod(method, userType);
               }
               catch (Throwable ex) {
                  throw new IllegalStateException("Invalid mapping on handler class [" +
                        userType.getName() + "]: " + method, ex);
               }
            });
      if (logger.isTraceEnabled()) {
         logger.trace(formatMappings(userType, methods));
      }
      methods.forEach((method, mapping) -> {
         Method invocableMethod = AopUtils.selectInvocableMethod(method, userType);
         //注册到mappingRegistry中,后面会根据request获取对应的HandlerMethod
         registerHandlerMethod(handler, invocableMethod, mapping);
      });
   }
}
复制代码

RequestMapping转换为RequestMappingInfo

@Override
@Nullable
protected RequestMappingInfo getMappingForMethod(Method method, Class<?> handlerType) {
    //获取方法上的RequestMapping并且转换为RequestMappingInfo
   RequestMappingInfo info = createRequestMappingInfo(method);
   if (info != null) {
       //获取类上的RequestMapping并且转换为RequestMappingInfo(这里就是Controller上的RequestMapping)
      RequestMappingInfo typeInfo = createRequestMappingInfo(handlerType);
      if (typeInfo != null) {
          //将方法上的RequestMapping和类上的RequestMapping合并,这里会合并url
         info = typeInfo.combine(info);
      }
      String prefix = getPathPrefix(handlerType);
      if (prefix != null) {
         info = RequestMappingInfo.paths(prefix).options(this.config).build().combine(info);
      }
   }
   return info;
}
复制代码

这个方法会把方法上的RequestMapping转换为RequestMappingInfo,把类上的RequestMapping转换为RequestMappingInfo,然后再把两个RequestMappingInfo合并成一个(url的合并)。

HandlerMethod的注册

protected void registerHandlerMethod(Object handler, Method method, T mapping) {
   //这里直接注册到mappingRegistry中,后面也直接从mappingRegistry获取
   this.mappingRegistry.register(mapping, handler, method);
}

public void register(T mapping, Object handler, Method method) {
   if (KotlinDetector.isKotlinType(method.getDeclaringClass())) {
      Class<?>[] parameterTypes = method.getParameterTypes();
      if ((parameterTypes.length > 0) && "kotlin.coroutines.Continuation".equals(parameterTypes[parameterTypes.length - 1].getName())) {
         throw new IllegalStateException("Unsupported suspending handler method detected: " + method);
      }
   }
   this.readWriteLock.writeLock().lock();
   try {
       //创建HandlerMethod
      HandlerMethod handlerMethod = createHandlerMethod(handler, method);
      validateMethodMapping(handlerMethod, mapping);
      this.mappingLookup.put(mapping, handlerMethod);

      List<String> directUrls = getDirectUrls(mapping);
      for (String url : directUrls) {
         this.urlLookup.add(url, mapping);
      }

      String name = null;
      if (getNamingStrategy() != null) {
         name = getNamingStrategy().getName(handlerMethod, mapping);
         addMappingName(name, handlerMethod);
      }

      CorsConfiguration corsConfig = initCorsConfiguration(handler, method, mapping);
      if (corsConfig != null) {
         this.corsLookup.put(handlerMethod, corsConfig);
      }
       //这里会添加到MappingRegistry中去
      this.registry.put(mapping, new MappingRegistration<>(mapping, handlerMethod, directUrls, name));
   }
   finally {
      this.readWriteLock.writeLock().unlock();
   }
}
复制代码

这里需要注意的是,注册的Controller是直接注册的HandlerMethod,这个HandlerMethod就是对应的Controller类中具体请求对应的方法,这个对象封装了所有信息,后面获取出HandlerMethod后会通过反射调用具体的方法

进入
RequestMappingHandlerMapping的getHandler方法看下,这个方法在父类AbstractHandlerMapping中实现,这里用到了设计模式中的模版方法。

@Override
@Nullable
public final HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
    //这里获取真正的处理器,子类AbstractHandlerMethodMapping实现
   Object handler = getHandlerInternal(request);
   if (handler == null) {
      //没有的话,使用默认的
      handler = getDefaultHandler();
   }
   if (handler == null) {
      return null;
   }
   // 如果是beanName的话 从ioc容器中获取真正的实例
   if (handler instanceof String) {
      String handlerName = (String) handler;
      handler = obtainApplicationContext().getBean(handlerName);
   }
    //这里获取对应请求的拦截器链
   HandlerExecutionChain executionChain = getHandlerExecutionChain(handler, request);

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

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

   return executionChain;
}
复制代码

这里核心关注两个方法,一个是获取处理器的getHandlerInternal方法,一个是获取对应拦截器链的getHandlerExecutionChain方法

AbstractHandlerMethodMapping的getHandlerInternal方法

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

@Nullable
protected HandlerMethod lookupHandlerMethod(String lookupPath, HttpServletRequest request) throws Exception {
   List<Match> matches = new ArrayList<>();
   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()) {
      Match bestMatch = matches.get(0);
      if (matches.size() > 1) {
         Comparator<Match> comparator = new MatchComparator(getMappingComparator(request));
         matches.sort(comparator);
         bestMatch = matches.get(0);
         if (logger.isTraceEnabled()) {
            logger.trace(matches.size() + " matching mappings: " + matches);
         }
         if (CorsUtils.isPreFlightRequest(request)) {
            return PREFLIGHT_AMBIGUOUS_MATCH;
         }
         Match secondBestMatch = matches.get(1);
         if (comparator.compare(bestMatch, secondBestMatch) == 0) {
            Method m1 = bestMatch.handlerMethod.getMethod();
            Method m2 = secondBestMatch.handlerMethod.getMethod();
            String uri = request.getRequestURI();
            throw new IllegalStateException(
                  "Ambiguous handler methods mapped for '" + uri + "': {" + m1 + ", " + m2 + "}");
         }
      }
      request.setAttribute(BEST_MATCHING_HANDLER_ATTRIBUTE, bestMatch.handlerMethod);
      handleMatch(bestMatch.mapping, lookupPath, request);
      return bestMatch.handlerMethod;
   }
   else {
      return handleNoMatch(this.mappingRegistry.getMappings().keySet(), lookupPath, request);
   }
}
复制代码

从上面的方法中可以看出,最后是从mappingRegistry属性中取出的HandlerMethod,mappingRegistry在上面的
RequestMappingHandlerMapping中有详细讲解

AbstractHandlerMapping的getHandlerExecutionChain方法

protected HandlerExecutionChain getHandlerExecutionChain(Object handler, HttpServletRequest request) {
   HandlerExecutionChain chain = (handler instanceof HandlerExecutionChain ?
         (HandlerExecutionChain) handler : new HandlerExecutionChain(handler));
    //这里会拿到所有的拦截器,然后通过路径匹配到合适的拦截器
   String lookupPath = this.urlPathHelper.getLookupPathForRequest(request, LOOKUP_PATH);
   for (HandlerInterceptor interceptor : this.adaptedInterceptors) {
      if (interceptor instanceof MappedInterceptor) {
         MappedInterceptor mappedInterceptor = (MappedInterceptor) interceptor;
         if (mappedInterceptor.matches(lookupPath, this.pathMatcher)) {
            chain.addInterceptor(mappedInterceptor.getInterceptor());
         }
      }
      else {
         chain.addInterceptor(interceptor);
      }
   }
   return chain;
}
复制代码

到这就已经拿到了对应的拦截器链和响应请求对应的方法了。接下来就是调用方法了,这里就轮到HandlerAdapter出场了,如何获取
RequestMappingHandlerAdapter的方法getHandlerAdapter这里就跳过了

再回到DispatchServlet的doDispatch方法中的

//这里的ha就是RequestMappingHandlerAdapter类
//核心是RequestMappingHandlerAdapter类的handleInternal方法
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
复制代码

RequestMappingHandlerAdapter类的handleInternal方法

@Override
protected ModelAndView handleInternal(HttpServletRequest request,
      HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {

   ModelAndView mav;
   checkRequest(request);

   if (this.synchronizeOnSession) {
      HttpSession session = request.getSession(false);
      if (session != null) {
         Object mutex = WebUtils.getSessionMutex(session);
         synchronized (mutex) {
            //这里就是真正调用HandlerMethod的地方了
            mav = invokeHandlerMethod(request, response, handlerMethod);
         }
      }
      else {
          //这里就是真正调用HandlerMethod的地方了
         mav = invokeHandlerMethod(request, response, handlerMethod);
      }
   }
   else {
    //这里就是真正调用HandlerMethod的地方了
      mav = invokeHandlerMethod(request, response, handlerMethod);
   }

   if (!response.containsHeader(HEADER_CACHE_CONTROL)) {
      if (getSessionAttributesHandler(handlerMethod).hasSessionAttributes()) {
         applyCacheSeconds(response, this.cacheSecondsForSessionAttributeHandlers);
      }
      else {
         prepareResponse(response);
      }
   }

   return mav;
}
复制代码

到这里,整个调用的过程就已经到此为止了。其中的HandlerAdapter的注册、获取、处理请求反射调用HandlerMethod等以后的章节再分析。

链接:
https://juejin.cn/post/7026266185729572872



Tags:Controller   点击:()  评论:()
声明:本站部分内容及图片来自互联网,转载是出于传递更多信息之目的,内容观点仅代表作者本人,如有任何标注错误或版权侵犯请与我们联系(Email:2595517585@qq.com),我们将及时更正、删除,谢谢。
▌相关推荐
SpringBoot中的Controller注册本篇将会以Servlet为切入点,通过源码来看web容器中的Controller是如何注册到HandlerMapping中。请求来了之后,web容器是如何根据请求路径找到对...【详细内容】
2021-11-04  Tags: Controller  点击:(52)  评论:(0)  加入收藏
今天介绍我正在用的一款高效敏捷开发工具magic-api,顺便分享一点工作中使用它的心得缘起先说一下我为什么会使用这个工具?最近新启动一个项目,业务并不算复杂,那种典型的管理系...【详细内容】
2021-06-01  Tags: Controller  点击:(194)  评论:(0)  加入收藏
报错:Access to XMLHttpRequest at &#39;http://localhost:8080/SpringBootServer/testfile&#39; from origin &#39;null&#39; has been blocked by CORS policy: No &#39;Ac...【详细内容】
2020-09-03  Tags: Controller  点击:(57)  评论:(0)  加入收藏
▌简易百科推荐
近日只是为了想尽办法为 Flask 实现 Swagger UI 文档功能,基本上要让 Flask 配合 Flasgger, 所以写了篇 Flask 应用集成 Swagger UI 。然而不断的 Google 过程中偶然间发现了...【详细内容】
2021-12-23  Python阿杰    Tags:FastAPI   点击:(6)  评论:(0)  加入收藏
文章目录1、Quartz1.1 引入依赖<dependency> <groupId>org.quartz-scheduler</groupId> <artifactId>quartz</artifactId> <version>2.3.2</version></dependency>...【详细内容】
2021-12-22  java老人头    Tags:框架   点击:(11)  评论:(0)  加入收藏
今天来梳理下 Spring 的整体脉络啦,为后面的文章做个铺垫~后面几篇文章应该会讲讲这些内容啦 Spring AOP 插件 (了好久都忘了 ) 分享下 4ye 在项目中利用 AOP + MybatisPlus 对...【详细内容】
2021-12-07  Java4ye    Tags:Spring   点击:(14)  评论:(0)  加入收藏
&emsp;前面通过入门案例介绍,我们发现在SpringSecurity中如果我们没有使用自定义的登录界面,那么SpringSecurity会给我们提供一个系统登录界面。但真实项目中我们一般都会使用...【详细内容】
2021-12-06  波哥带你学Java    Tags:SpringSecurity   点击:(18)  评论:(0)  加入收藏
React 简介 React 基本使用<div id="test"></div><script type="text/javascript" src="../js/react.development.js"></script><script type="text/javascript" src="../js...【详细内容】
2021-11-30  清闲的帆船先生    Tags:框架   点击:(19)  评论:(0)  加入收藏
流水线(Pipeline)是把一个重复的过程分解为若干个子过程,使每个子过程与其他子过程并行进行的技术。本文主要介绍了诞生于云原生时代的流水线框架 Argo。 什么是流水线?在计算机...【详细内容】
2021-11-30  叼着猫的鱼    Tags:框架   点击:(21)  评论:(0)  加入收藏
TKinterThinter 是标准的python包,你可以在linx,macos,windows上使用它,你不需要安装它,因为它是python自带的扩展包。 它采用TCL的控制接口,你可以非常方便地写出图形界面,如...【详细内容】
2021-11-30    梦回故里归来  Tags:框架   点击:(26)  评论:(0)  加入收藏
前言项目中的配置文件会有密码的存在,例如数据库的密码、邮箱的密码、FTP的密码等。配置的密码以明文的方式暴露,并不是一种安全的方式,特别是大型项目的生产环境中,因为配置文...【详细内容】
2021-11-17  充满元气的java爱好者  博客园  Tags:SpringBoot   点击:(25)  评论:(0)  加入收藏
一、搭建环境1、创建数据库表和表结构create table account(id INT identity(1,1) primary key,name varchar(20),[money] DECIMAL2、创建maven的工程SSM,在pom.xml文件引入...【详细内容】
2021-11-11  AT小白在线中  搜狐号  Tags:开发框架   点击:(29)  评论:(0)  加入收藏
SpringBoot开发的物联网通信平台系统项目功能模块 功能 说明 MQTT 1.SSL支持 2.集群化部署时暂不支持retain&will类型消 UDP ...【详细内容】
2021-11-05  小程序建站    Tags:SpringBoot   点击:(55)  评论:(0)  加入收藏
最新更新
栏目热门
栏目头条