Spring源码系列-第9章-SpringMVC请求处理源码和HandlerMapping原理

文章目录

  1. 文章优先发布在Github,其它平台会晚一段时间,文章纠错与更新内容只在Github:https://github.com/youthlql/JavaYouth
  2. 转载须知:转载请注明GitHub出处,让我们一起维护一个良好的技术创作环境。
  3. 如果你要提交 issue 或者 pr 的话建议到 Github 提交。笔者会陆续更新,如果对你有所帮助,不妨Github点个Star~。你的Star是我创作的动力。

         

第9章-SpringMVC请求处理源码和HandlerMapping原理

请求的处理链路

在这里插入图片描述

  1. tomcat里面可以部署多个项目应用。/abc_test和mvc_test这种就是项目路径,用于区分多个项目
  2. 在以前的Servlet开发中,每一个路径都需要有一个Servlet来处理。比如上图所画
  3. 有了SpringMVC,整个Tomcat下面就不会有很多Servlet了,只会有一个DispatcherServlet来统一处理

Servlet继承树

在这里插入图片描述

  1. Servlet里是Service()方法处理请求
  2. GenericServlet也是Service()方法处理请求
  3. HttpServlet开始根据请求类型将处理方法分为doGet,doPost,doPut,doDelete等等
  4. HttpServletBean没有处理方法
  5. FrameworkServlet也是有处理方法分别为doGet,doPost,doPut,doDelete等等,但是这些方法最终调用的都是processRequest(HttpServletRequest request, HttpServletResponse response)
  6. DispatcherServlet处理方法是doService()

Debug调用栈

在这里插入图片描述

DispatcherServlet#doService()

    @Override
    protected void doService(HttpServletRequest request, HttpServletResponse response) throws Exception {
       logRequest(request);

       // Keep a snapshot of the request attributes in case of an include,
       // to be able to restore the original attributes after the include.
       Map<String, Object> attributesSnapshot = null; //把request域的所有属性提前保存
       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域中方便处理 Make framework objects available to handlers and view objects.
       request.setAttribute(WEB_APPLICATION_CONTEXT_ATTRIBUTE, getWebApplicationContext());
       request.setAttribute(LOCALE_RESOLVER_ATTRIBUTE, this.localeResolver); //国际化解析器
       request.setAttribute(THEME_RESOLVER_ATTRIBUTE, this.themeResolver); //主题解析器
       request.setAttribute(THEME_SOURCE_ATTRIBUTE, getThemeSource());

       if (this.flashMapManager != null) { //闪存管理器(重定向携带数据)
          FlashMap inputFlashMap = this.flashMapManager.retrieveAndUpdate(request, response);
          if (inputFlashMap != null) {
             request.setAttribute(INPUT_FLASH_MAP_ATTRIBUTE, Collections.unmodifiableMap(inputFlashMap));
          }
          request.setAttribute(OUTPUT_FLASH_MAP_ATTRIBUTE, new FlashMap());
          request.setAttribute(FLASH_MAP_MANAGER_ATTRIBUTE, this.flashMapManager);
       }

       RequestPath previousRequestPath = null;
       if (this.parseRequestPath) {
          previousRequestPath = (RequestPath) request.getAttribute(ServletRequestPathUtils.PATH_ATTRIBUTE);
          ServletRequestPathUtils.parseAndCache(request);
       }

       try {
          doDispatch(request, response); //处理派发功能
       }
       finally {
          if (!WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted()) {
             // Restore the original attribute snapshot, in case of an include.
             if (attributesSnapshot != null) {
                restoreAttributesAfterInclude(request, attributesSnapshot);
             }
          }
          ServletRequestPathUtils.setParsedRequestPath(previousRequestPath, request);
       }
    }

DispatcherServlet#doDispatch()请求派发

    //SpringMVC处理请求的核心流程
    protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
       HttpServletRequest processedRequest = request;
       HandlerExecutionChain mappedHandler = null; //handler(目标方法)的执行链
       boolean multipartRequestParsed = false; //文件上传标志
       //对异步请求的支持(Servlet3.0以后才有的,Webflux)
       WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);

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

          try {
             processedRequest = checkMultipart(request); //检查当前是否文件上传请求
             multipartRequestParsed = (processedRequest != request);

             //构造出了【目标方法+拦截器整个链路】决定使用哪个Handler处理当前请求 Determine handler for the current request.
             mappedHandler = getHandler(processedRequest);
             if (mappedHandler == null) {  //如果找不到人处理,就send 404
                noHandlerFound(processedRequest, response);
                return;
             }

             //适配器怎么找的、 Determine handler adapter for the current request.
             HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());

             // Process last-modified header, if supported by the handler.
             String method = request.getMethod();
             boolean isGet = "GET".equals(method);
             if (isGet || "HEAD".equals(method)) {
                long lastModified = ha.getLastModified(request, mappedHandler.getHandler());
                if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) {
                   return;
                }
             }

             if (!mappedHandler.applyPreHandle(processedRequest, response)) {
                return;
             }

             // Actually invoke the handler.
             mv = ha.handle(processedRequest, response, mappedHandler.getHandler());

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

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

doDispatch处理大流程图

在这里插入图片描述

DispatcherServlet#checkMultipart()

protected HttpServletRequest checkMultipart(HttpServletRequest request) throws MultipartException {
    //使用文件上传解析器来判断是否文件上传请求
   if (this.multipartResolver != null && this.multipartResolver.isMultipart(request)) {
 		// ......
   }
   // If not returned before: return original request.
   return request;
}

在这里插入图片描述

咱们这里目前连解析器都没有,所以就直接返回了

StandardServletMultipartResolver#isMultipart()

public boolean isMultipart(HttpServletRequest request) {
   //所有文件上传请求头Content-Type都会有这个
   return StringUtils.startsWithIgnoreCase(request.getContentType(), "multipart/"); 
}

DispatcherServlet九大组件

    //DispatcherServlet中的九大组件、全是接口,我们完全可以自定义实现。Spring默认也都准备好了这些组件的实现
    /** MultipartResolver used by this servlet. */ 
    @Nullable  //文件上传解析器
    private MultipartResolver multipartResolver;

    /** LocaleResolver used by this servlet. */
    @Nullable  //国际化解析器 Locale(区域信息)
    private LocaleResolver localeResolver;

    /** ThemeResolver used by this servlet. */
    @Nullable //主题解析器
    private ThemeResolver themeResolver;

    /** List of HandlerMappings used by this servlet. */
    @Nullable //Handler(处理器、能处理请求的人(Controller))的映射:【保存的就是所有请求都由谁来处理的映射关系】
    private List<HandlerMapping> handlerMappings;

    /** List of HandlerAdapters used by this servlet. */
    @Nullable //Handler处理器的适配器;是一个超级反射工具,帮我们解决参数呀,返回值这些,不需要我们很麻烦的写
    private List<HandlerAdapter> handlerAdapters;

    /** List of HandlerExceptionResolvers used by this servlet. */
    @Nullable //Handler的异常解析器,异常处理功能
    private List<HandlerExceptionResolver> handlerExceptionResolvers;

    /** RequestToViewNameTranslator used by this servlet. */
    @Nullable //把请求转成视图名(我们要跳转的页面地址)的翻译器【没啥用】
    private RequestToViewNameTranslator viewNameTranslator;

    /** FlashMapManager used by this servlet. */
    @Nullable //闪存管理器
    private FlashMapManager flashMapManager;

    /** List of ViewResolvers used by this servlet. */
    @Nullable //视图解析器(我们去哪些页面,怎么过去?)
    private List<ViewResolver> viewResolvers;

官网介绍

在这里插入图片描述

九大组件利用Spring事件机制进行初始化

上面的图我们看到九大组件有八个已经有值了,我们现在看下他们是何时有值的。怎么找就很简单,你就找这些属性的setXXX方法,最后我找到了这里

DispatcherServlet#initStrategies()

    protected void initStrategies(ApplicationContext context) { //初始化所有策略,九大组件在这里进行了初始化
       initMultipartResolver(context); //容器中有就用,没有就是null
       initLocaleResolver(context); //从容器中获取,没有用默认
       initThemeResolver(context); //从容器中获取,没有用默认
       initHandlerMappings(context); //从容器中获取,没有用默认
       initHandlerAdapters(context); //从容器中获取,没有用默认
       initHandlerExceptionResolvers(context); //从容器中获取,没有用默认
       initRequestToViewNameTranslator(context); //Spring没有浓重说他,//从容器中获取,没有用默认
       initViewResolvers(context); //从容器中获取,没有用默认
       initFlashMapManager(context); //从容器中获取,没有用默认
    }

代码几乎都是一样的逻辑,咱们就只看一个举个例子

DispatcherServlet#initLocaleResolver()

    private void initLocaleResolver(ApplicationContext context) {
       try { //容器中先来获取
          this.localeResolver = context.getBean(LOCALE_RESOLVER_BEAN_NAME, LocaleResolver.class);
          if (logger.isTraceEnabled()) {
             logger.trace("Detected " + this.localeResolver);
          }
          else if (logger.isDebugEnabled()) {
             logger.debug("Detected " + this.localeResolver.getClass().getSimpleName());
          }
       }
       catch (NoSuchBeanDefinitionException ex) {
          // We need to use the default. 容器中没有,读取默认配置文件进行加载
          this.localeResolver = getDefaultStrategy(context, LocaleResolver.class); //获取默认策略
          if (logger.isTraceEnabled()) {
             logger.trace("No LocaleResolver '" + LOCALE_RESOLVER_BEAN_NAME +
                   "': using default [" + this.localeResolver.getClass().getSimpleName() + "]");
          }
       }
    }

只要不满意九大组件的任何一个,自己可以实现指定的接口,这样就会放在容器中,SpringMVC

Debug调用栈

在这里插入图片描述

  1. Tomcat启动
  2. 触发DispatcherServlet的初始化
  3. DispatcherServlet初始化全部结束,容器会发送Spring的相关事件.
  4. 感知到容器准备好了的事件–初始化九大组件(底层是SourceFilteringListener, 把事件回调到DispatcherServlet的onRefresh方法)

DispatcherServlet#onRefresh()接收到事件开始准备初始化九大组件

    protected void onRefresh(ApplicationContext context) {
       initStrategies(context); //初始化九大组件
    }

DispatcherServlet#getDefaultStrategy() 获取默认初始化策略

    protected <T> T getDefaultStrategy(ApplicationContext context, Class<T> strategyInterface) {
       List<T> strategies = getDefaultStrategies(context, strategyInterface);
       if (strategies.size() != 1) {
          throw new BeanInitializationException(
                "DispatcherServlet needs exactly 1 strategy for interface [" + strategyInterface.getName() + "]");
       }
       return strategies.get(0);
    }

	protected <T> List<T> getDefaultStrategies(ApplicationContext context, Class<T> strategyInterface) {
		if (defaultStrategies == null) {
			try {
				// Load default strategy implementations from properties file.
				// This is currently strictly internal and not meant to be customized
				// by application developers. 去DispatcherServlet所在的类路径下找一个 DispatcherServlet.properties 资源
				ClassPathResource resource = new ClassPathResource(DEFAULT_STRATEGIES_PATH, DispatcherServlet.class);
				defaultStrategies = PropertiesLoaderUtils.loadProperties(resource); //读取properties文件
			}
			catch (IOException ex) {
				throw new IllegalStateException("Could not load '" + DEFAULT_STRATEGIES_PATH + "': " + ex.getMessage());
			}
		}

		// ......
	}

	private static final String DEFAULT_STRATEGIES_PATH = "DispatcherServlet.properties";

在这里插入图片描述

DispatcherServlet.properties

下面这些就是九大组件默认组件

# Default implementation classes for DispatcherServlet's strategy interfaces.
# Used as fallback when no matching beans are found in the DispatcherServlet context.
# Not meant to be customized by application developers.

org.springframework.web.servlet.LocaleResolver=org.springframework.web.servlet.i18n.AcceptHeaderLocaleResolver

org.springframework.web.servlet.ThemeResolver=org.springframework.web.servlet.theme.FixedThemeResolver

org.springframework.web.servlet.HandlerMapping=org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping,\
   org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping,\
   org.springframework.web.servlet.function.support.RouterFunctionMapping

org.springframework.web.servlet.HandlerAdapter=org.springframework.web.servlet.mvc.HttpRequestHandlerAdapter,\
   org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter,\
   org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter,\
   org.springframework.web.servlet.function.support.HandlerFunctionAdapter


org.springframework.web.servlet.HandlerExceptionResolver=org.springframework.web.servlet.mvc.method.annotation.ExceptionHandlerExceptionResolver,\
   org.springframework.web.servlet.mvc.annotation.ResponseStatusExceptionResolver,\
   org.springframework.web.servlet.mvc.support.DefaultHandlerExceptionResolver

org.springframework.web.servlet.RequestToViewNameTranslator=org.springframework.web.servlet.view.DefaultRequestToViewNameTranslator

org.springframework.web.servlet.ViewResolver=org.springframework.web.servlet.view.InternalResourceViewResolver

org.springframework.web.servlet.FlashMapManager=org.springframework.web.servlet.support.SessionFlashMapManager

HandlerMapping处理URL和Controller的映射关系

DispatcherServlet#doDispatch()请求派发

    //SpringMVC处理请求的核心流程
    protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
       HttpServletRequest processedRequest = request;
       HandlerExecutionChain mappedHandler = null; //handler(目标方法)的执行链
       boolean multipartRequestParsed = false; //文件上传标志
       //对异步请求的支持(Servlet3.0以后才有的,Webflux)
       WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);

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

          try {
             processedRequest = checkMultipart(request); //检查当前是否文件上传请求
             multipartRequestParsed = (processedRequest != request);

             //构造出了【目标方法+拦截器整个链路】决定使用哪个Handler处理当前请求 Determine handler for the current request.
             mappedHandler = getHandler(processedRequest);
             if (mappedHandler == null) {  //如果找不到人处理,就send 404
                noHandlerFound(processedRequest, response);
                return;
             }

             //适配器怎么找的、 Determine handler adapter for the current request.
             HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());

             // Process last-modified header, if supported by the handler.
             String method = request.getMethod();
             boolean isGet = "GET".equals(method);
             if (isGet || "HEAD".equals(method)) {
                long lastModified = ha.getLastModified(request, mappedHandler.getHandler());
                if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) {
                   return;
                }
             }

             if (!mappedHandler.applyPreHandle(processedRequest, response)) {
                return;
             }

             // Actually invoke the handler.
             mv = ha.handle(processedRequest, response, mappedHandler.getHandler());

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

             applyDefaultViewName(processedRequest, mv);
             mappedHandler.applyPostHandle(processedRequest, response, mv);
          }
          processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
       }
        
       // ......
    }

DispatcherServlet#getHandler()根据请求拿Controller

在这里插入图片描述

这里会有三个默认的HandlerMapping,就是在DispatcherServlet.properties写的那三个,在之前说九大组件初始化的时候如果我们自己没有写,就获取默认的

  1. BeanNameUrlHandlerMapping:bean的名字作为URL路径,进行映射
  2. RequestMappingHandlerMapping:@RequestMapping注解作为URL地址进行映射
    1. 默认用它,而且它里面保存了所有请求映射信息
  3. RouterFunctionMapping: 支持函数式处理以及WebFlux相关功能,未来可能这个用的多

在这里插入图片描述

BeanNameUrlHandlerMapping里找不到映射关系,就直接下一个循环了。咱们主要看RequestMappingHandlerMapping怎么处理的

RequestMappingHandlerMapping处理流程

在这里插入图片描述

RequestMappingHandlerMapping的父类有一个MappingRegistry属性保存了请求路径 ==> 请求Controller+方法的映射

F7进入mapping.getHandler(request)

AbstractHandlerMapping#getHandler()开始根据请求URL查哪个Controler能处理

    public final HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
       Object handler = getHandlerInternal(request); //HandlerMapping的registry中找映射,返回HandlerMethod,真正执行当前请求的方法
       if (handler == null) {
          handler = getDefaultHandler();
       }
       if (handler == null) {
          return null;
       }
       // Bean name or resolved handler?
       if (handler instanceof String) {
          String handlerName = (String) handler;
          handler = obtainApplicationContext().getBean(handlerName);
       }

       // Ensure presence of cached lookupPath for interceptors and others
       if (!ServletRequestPathUtils.hasCachedPath(request)) {
          initLookupPath(request);
       }
       //找到前面的目标方法以后,还要构造一个处理器链;
       HandlerExecutionChain executionChain = getHandlerExecutionChain(handler, request);

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

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

       return executionChain;
    }

在这里插入图片描述

F7进入getHandlerInternal(request)

RequestMappingInfoHandlerMapping#getHandlerInternal()

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

继续F7进super.getHandlerInternal(request)

AbstractHandlerMethodMapping#getHandlerInternal()

    protected HandlerMethod getHandlerInternal(HttpServletRequest request) throws Exception {
       String lookupPath = initLookupPath(request);
       this.mappingRegistry.acquireReadLock();
       try {
          HandlerMethod handlerMethod = lookupHandlerMethod(lookupPath, request); //寻找当前请求谁能处理
          return (handlerMethod != null ? handlerMethod.createWithResolvedBean() : null);
       }
       finally {
          this.mappingRegistry.releaseReadLock();
       }
    }

在这里插入图片描述

F7进入lookupHandlerMethod(lookupPath, request)

AbstractHandlerMethodMapping#lookupHandlerMethod()真正根据URL查Controller

在这里插入图片描述

可以看到还是从mappingRegistry里获取。我们后面再看mappingRegistry是什么时候被放入东西的,这里我们接着返回

返回到AbstractHandlerMethodMapping#getHandlerInternal()

在这里插入图片描述

这个时候是已经找到了由哪个处理器处理,接着返回

返回到AbstractHandlerMapping#getHandler()

返回到这一步,准备执行getHandlerExecutionChain(handler, request)

在这里插入图片描述

AbstractHandlerMapping#getHandlerExecutionChain()开始构造处理器链

    protected HandlerExecutionChain getHandlerExecutionChain(Object handler, HttpServletRequest request) {
       HandlerExecutionChain chain = (handler instanceof HandlerExecutionChain ?
             (HandlerExecutionChain) handler : new HandlerExecutionChain(handler));
       //把系统中所有的拦截器拿过来
       for (HandlerInterceptor interceptor : this.adaptedInterceptors) {
          if (interceptor instanceof MappedInterceptor) {
             MappedInterceptor mappedInterceptor = (MappedInterceptor) interceptor;
             if (mappedInterceptor.matches(request)) {
                chain.addInterceptor(mappedInterceptor.getInterceptor());
             }
          }
          else {
             chain.addInterceptor(interceptor); //所有拦截器加进去
          }
       }
       return chain;
    }

在这里插入图片描述

  • 咱们没写拦截器,就没有。
  • 继续往回返

返回到DispatcherServlet#getHandler()

在这里插入图片描述

这里就是责任链模式,有能处理的handler就直接返回

返回到DispatcherServlet#doDispatch()

在这里插入图片描述

自此RequestMappingHandlerMapping处理结束

RequestMappingHandlerMapping如何保存映射关系

上面我们看到了实际上是从MappingRegistry里拿到URL–>XXXController的映射关系的,那么下面就来演讲MappingRegistry是何时保存的这映射关系,又是怎样保存的

AbstractHandlerMethodMapping#registerMapping()

我们这里还是靠猜测,猜它调用哪个方法。找到了如下方法,给第一行打断点,重启。

    //分析所有的Controller;里面的每一个@RequestMapping 注解才能知道这个事情
    public void registerMapping(T mapping, Object handler, Method method) { //哪个请求由哪个方法处理会通过这个进行注册
       if (logger.isTraceEnabled()) {
          logger.trace("Register \"" + mapping + "\" to " + method.toGenericString());
       }
       this.mappingRegistry.register(mapping, handler, method);
    }

重启的时候没有发现没跑到这里,然后我就看了下这个MappingRegistry,发现它是个AbstractHandlerMethodMapping的内部类

MappingRegistry(URL=>XXXController的映射中心)
class MappingRegistry {

   private final Map<T, MappingRegistration<T>> registry = new HashMap<>();

   private final MultiValueMap<String, T> pathLookup = new LinkedMultiValueMap<>();

   private final Map<String, List<HandlerMethod>> nameLookup = new ConcurrentHashMap<>();

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

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

            Set<String> directPaths = AbstractHandlerMethodMapping.this.getDirectPaths(mapping);
            for (String path : directPaths) {
                this.pathLookup.add(path, mapping);
            }

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

            CorsConfiguration corsConfig = initCorsConfiguration(handler, method, mapping);
            if (corsConfig != null) {
                corsConfig.validateAllowCredentials();
                this.corsLookup.put(handlerMethod, corsConfig);
            }
		   // 在这里put的
            this.registry.put(mapping,
                              new MappingRegistration<>(mapping, handlerMethod, directPaths, name, corsConfig != null));
        }
        finally {
            this.readWriteLock.writeLock().unlock();
        }
    }
}

我发现上面有一个registry是个Map类型,很容易想到这应该就是保存映射的,然后我就在它的put方法那里打断点,只找到了这一个put。重启应用

Debug调用栈

在这里插入图片描述

在这里插入图片描述

意料之中,启动应用的时候从init初始化那里调用过来了

流程概述
  1. DispatcherServlet#onRefresh()开始初始化九大组件,就会开始初始化HandlerMapping
  2. 首先是创建DispatcherServlet.properties里指定的三个HandlerMapping实现类的对象。还是用createBean来创建HandlerMapping的
  3. 其中RequestMappingHandlerMapping创建完对象后,因为它实现了InitializingBean,所以会调用RequestMappingHandlerMapping#afterPropertiesSet()
  4. 接着就是拿到Web子容器的所有组件,for循环处理。看是不是有@Controller注解或者@RequestMapping注解
  5. 最后把分析到的RequestMapping信息放到HandlerMapping的registry中

RequestMappingHandlerMapping#afterPropertiesSet()

public void afterPropertiesSet() {

   this.config = new RequestMappingInfo.BuilderConfiguration();
   this.config.setTrailingSlashMatch(useTrailingSlashMatch());
   this.config.setContentNegotiationManager(getContentNegotiationManager());

   if (getPatternParser() != null) {
      this.config.setPatternParser(getPatternParser());
      Assert.isTrue(!this.useSuffixPatternMatch && !this.useRegisteredSuffixPatternMatch,
            "Suffix pattern matching not supported with PathPatternParser.");
   }
   else {
      this.config.setSuffixPatternMatch(useSuffixPatternMatch());
      this.config.setRegisteredSuffixPatternMatch(useRegisteredSuffixPatternMatch());
      this.config.setPathMatcher(getPathMatcher());
   }

   super.afterPropertiesSet();
}

AbstractHandlerMethodMapping#afterPropertiesSet()

public void afterPropertiesSet() {
   initHandlerMethods(); //初始化HandlerMethods。
}

AbstractHandlerMethodMapping#initHandlerMethods()初始化HandlerMethods

在这里插入图片描述

可以看到这里只拿了Web子容器的所有组件进行for循环

AbstractHandlerMethodMapping#processCandidateBean()

    protected void processCandidateBean(String beanName) {
       Class<?> beanType = null;
       try {
          beanType = obtainApplicationContext().getType(beanName);
       }
       catch (Throwable ex) {
          // An unresolvable bean type, probably from a lazy bean - let's ignore it.
          if (logger.isTraceEnabled()) {
             logger.trace("Could not resolve type for bean '" + beanName + "'", ex);
          }
       }
       if (beanType != null && isHandler(beanType)) {
          detectHandlerMethods(beanName); //分析当前bean的HandlerMethods
       }
    }

RequestMappingHandlerMapping#isHandler()判断当前bean是不是Handler

    protected boolean isHandler(Class<?> beanType) {
       return (AnnotatedElementUtils.hasAnnotation(beanType, Controller.class) ||
             AnnotatedElementUtils.hasAnnotation(beanType, RequestMapping.class));
    }
  1. 这里首先会过滤Bean,只有你Bean也就是类上标注了@Controller注解或者@RequestMapping注解,才会给这个类走下面的流程
  2. 那这个是什么意思呢?
  3. 平常我们写XXXController就是写一个@Controller注解就完事了【注意@RestController值是@Controller的复合注解,包含了@Controller】
  4. 实际上我们可以不写@Controller注解,直接@Component+@RequestMapping也是一样的效果

AbstractHandlerMethodMapping#detectHandlerMethods()探查容器里满足条件的方法

  1. 然后那个detectHandlerMethods就是探查容器里满足条件的方法
  2. 下面看下怎么探索的
    protected void detectHandlerMethods(Object handler) {
       Class<?> handlerType = (handler instanceof String ?
             obtainApplicationContext().getType((String) handler) : handler.getClass());

       if (handlerType != null) {
          Class<?> userType = ClassUtils.getUserClass(handlerType);
          Map<Method, T> methods = MethodIntrospector.selectMethods(userType,
                (MethodIntrospector.MetadataLookup<T>) method -> {
                   try {
                      return getMappingForMethod(method, userType); //探索当前类里面所有满足的方法,所有标注了 RequestMapping
                   }
                   catch (Throwable ex) {
                      throw new IllegalStateException("Invalid mapping on handler class [" +
                            userType.getName() + "]: " + method, ex);
                   }
                });
          if (logger.isTraceEnabled()) {
             logger.trace(formatMappings(userType, methods));
          }
          else if (mappingsLogger.isDebugEnabled()) {
             mappingsLogger.debug(formatMappings(userType, methods));
          }
          methods.forEach((method, mapping) -> {
             Method invocableMethod = AopUtils.selectInvocableMethod(method, userType);
             registerHandlerMethod(handler, invocableMethod, mapping);
          });
       }
    }

在这里插入图片描述

RequestMappingHandlerMapping#getMappingForMethod()

    protected RequestMappingInfo getMappingForMethod(Method method, Class<?> handlerType) {
       RequestMappingInfo info = createRequestMappingInfo(method); //为每一个方法尝试创建 RequestMappingInfo
       if (info != null) {
          RequestMappingInfo typeInfo = createRequestMappingInfo(handlerType);
          if (typeInfo != null) {
             info = typeInfo.combine(info);
          }
          String prefix = getPathPrefix(handlerType);
          if (prefix != null) {
             info = RequestMappingInfo.paths(prefix).options(this.config).build().combine(info);
          }
       }
       return info;
    }

RequestMappingHandlerMapping#createRequestMappingInfo(AnnotatedElement )

    @Nullable  
    private RequestMappingInfo createRequestMappingInfo(AnnotatedElement element) {
       // 找每一个标有@RequestMapping注解的方法
       RequestMapping requestMapping = AnnotatedElementUtils.findMergedAnnotation(element, RequestMapping.class);
       RequestCondition<?> condition = (element instanceof Class ?
             getCustomTypeCondition((Class<?>) element) : getCustomMethodCondition((Method) element));
       return (requestMapping != null ? createRequestMappingInfo(requestMapping, condition) : null);
    }

自此一切就明了了,原理就是上面写的流程概述。for循环组件,找@RequestMapping注解

小总结

Q:功能增强的时候什么时候用后置处理器BeanPostProcessor,什么时候用生命周期InitializingBean呢?

A:

  1. 如果是所有组件都可能会用到的增强功能,那就实现后置处理器BeanPostProcessor来增强
  2. 如果是单组件增强,最好用InitializingBean。可以看到mvc的RequestMappingHandlerMapping这里用的就是InitializingBean
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值