SpringMVC源码解析之二:一次请求的完整旅行源码分析

SpringMVC源码解析之二:一次请求的完整旅行源码分析

一次客户请求在SpringMVC框架中,所经历的完整执行流程分析。

DispatcherServlet的初始化源码分析

在SpringMVC中,所有请求都要通过前端控制器DispatcherServlet来进行处理,因为它是一个servlet了,其生命周期当然也由servlet容器(如tomcat)来管理。在它提供服务之前,必须先初始化。

在进入源码之前,先看下DispatcherServlet的继承关系:

在这里插入图片描述

DispatcherServlet的初始化逻辑位于:org.springframework.web.servlet.HttpServletBean#init()方法中:

public final void init() throws ServletException {
   
   //获取DispatcherServlet的初始化配置信息(比如使用在xml配置时的contextConfigLocation)
   PropertyValues pvs = new ServletConfigPropertyValues(getServletConfig(), this.requiredProperties);
   
   //如果有配置,则通过BeanWrapper工具设置到DispatcherServlet的对应属性上。
   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);
      }
      ...
   }
		
	 //调用子类的initServletBean方法继续初始化,典型的模板方法模式
   // Let subclasses do whatever initialization they like.
   initServletBean();

   ...
}

initServletBean方法的逻辑又org.springframework.web.servlet.FrameworkServlet#initServletBean方法提供:

protected final void initServletBean() throws ServletException {
   
   try {
   		//初始化WebApplicationContext,这个就是DispatcherServlet位于的WebApplicationContext
      this.webApplicationContext = initWebApplicationContext();
      //继续给子类机会来执行其他的初始化动作,目前是空方法
      initFrameworkServlet();
   }
   ...
}

看下WebApplicationContext是如何初始出来的?org.springframework.web.servlet.FrameworkServlet#initWebApplicationContext方法:

protected WebApplicationContext initWebApplicationContext() {
	//看看ServletContext的属性“WebApplicationContext.ROOT”是否存在。
	//如果在web.xml中配置了ContextLoaderListener,则servlet容器初始化后会回调其contextInitialized方法,这里面会初始化一个WebApplicationContext出来,并设置到ServletContext的属性上。
   WebApplicationContext rootContext =
         WebApplicationContextUtils.getWebApplicationContext(getServletContext());
   WebApplicationContext wac = null;
	
//如果当前的DispatcherServlet中已经有webApplicationContext了(即自己给servlet注入一个我们自己创建的webApplicationContext)。
   if (this.webApplicationContext != null) {
      // A context instance was injected at construction time -> use it
      wac = this.webApplicationContext;
      if (wac instanceof ConfigurableWebApplicationContext) {
         ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext) wac;
         //如果已经有的webApplicationContext还没有激活
         if (!cwac.isActive()) {
            // The context has not yet been refreshed -> provide services such as
            // setting the parent context, setting the application context id, etc
            if (cwac.getParent() == null) {
               // The context instance was injected without an explicit parent -> set
               // the root application context (if any; may be null) as the parent
               //将当前DispatcherServlet中已经有webApplicationContext的父容器设置为ServletContext中的那个。
               cwac.setParent(rootContext);
            }
            //配置已有的这个,并刷新容器。
            configureAndRefreshWebApplicationContext(cwac);
         }
      }
   }
   
   //如果DispatcherServlet中没有webApplicationContext,则看看ServletContext中是不是有已经注册了的,且是已经初始化好的,只需要拿过来用就行. 这儿使用通过给DispatcherServlet配置contextAttribute属性来找。
   if (wac == null) {
      // No context instance was injected at construction time -> see if one
      // has been registered in the servlet context. If one exists, it is assumed
      // that the parent context (if any) has already been set and that the
      // user has performed any initialization such as setting the context id
      wac = findWebApplicationContext();
   }
   
   //如果还是没有,则自己创建一个新的
   if (wac == null) {
      // No context instance is defined for this servlet -> create a local one
      wac = createWebApplicationContext(rootContext);
   }

	//看看onRefresh是不是已经调用过了,如果没有,这调用一下。
	//这里面会初始化DispatcherServlet用到的一些列组件。
   if (!this.refreshEventReceived) {
      onRefresh(wac);
   }

	//如果需要的话,将WebApplicationContext发布到ServletContext的属性中
   if (this.publishContext) {
      // Publish the context as a servlet context attribute.
      String attrName = getServletContextAttributeName();
      getServletContext().setAttribute(attrName, wac);
   }

   return wac;
}

这里面对我们来说比较重要的就是onRefresh,它又会调用到其子类即org.springframework.web.servlet.DispatcherServlet#onRefresh方法:

protected void onRefresh(ApplicationContext context) {
	//初始化springmvc用到的组件
   initStrategies(context);
}

protected void initStrategies(ApplicationContext context) {
		initMultipartResolver(context);  
		initLocaleResolver(context);
		initThemeResolver(context);
		initHandlerMappings(context);  //初始化HandlerMappings
		initHandlerAdapters(context);		//初始化HandlerAdapters
		initHandlerExceptionResolvers(context);
		initRequestToViewNameTranslator(context);
		initViewResolvers(context);		//初始化ViewResolvers
		initFlashMapManager(context);
	}

这一堆的初始化方法模式都非常相似,也比较简单,我们就只展开initHandlerMappings和initHandlerAdapters这两个方法.

org.springframework.web.servlet.DispatcherServlet#initHandlerMappings方法:

private void initHandlerMappings(ApplicationContext context) {
   this.handlerMappings = null;
		
	//是否检测所有的HandlerMapping,默认yes	
   if (this.detectAllHandlerMappings) {
      //从Dispather的容器以及父容器中找出所有的HandlerMapping类型,赋值给handlerMappings属性,并进行排序。
      // Find all HandlerMappings in the ApplicationContext, including ancestor contexts.
      Map<String, HandlerMapping> matchingBeans =
            BeanFactoryUtils.beansOfTypeIncludingAncestors(context, HandlerMapping.class, true, false);
      if (!matchingBeans.isEmpty()) {
         this.handlerMappings = new ArrayList<>(matchingBeans.values());
         // We keep HandlerMappings in sorted order.
         AnnotationAwareOrderComparator.sort(this.handlerMappings);
      }
   }
   
   //如果不支持检测所有的HandlerMapping,则就找beanName为“handlerMapping”的bean,赋值给handlerMappings属性
   else {
      try {
         HandlerMapping hm = context.getBean(HANDLER_MAPPING_BEAN_NAME, HandlerMapping.class);
         this.handlerMappings = Collections.singletonList(hm);
      }
   }
		
		
	//如果容器中没有找到的话,则使用默认策略,默认策略文件定义在DispatcherServlet同包下的DispatcherServlet.properties文件中,默认定义的是BeanNameUrlHandlerMapping和RequestMappingHandlerMapping。
   if (this.handlerMappings == null) {
      this.handlerMappings = getDefaultStrategies(context, HandlerMapping.class);
   }
}

org.springframework.web.servlet.DispatcherServlet#initHandlerAdapters方法:

private void initHandlerAdapters(ApplicationContext context) {
   this.handlerAdapters = null;

   if (this.detectAllHandlerAdapters) {
      //找所有的HandlerAdapter类型,并赋值给handlerAdapters属性,并排序
      Map<String, HandlerAdapter> matchingBeans =
            BeanFactoryUtils.beansOfTypeIncludingAncestors(context, HandlerAdapter.class, true, false);
      if (!matchingBeans.isEmpty()) {
         this.handlerAdapters = new ArrayList<>(matchingBeans.values());
         AnnotationAwareOrderComparator.sort(this.handlerAdapters);
      }
   }
   
   else {
      try {
      	//找名字为handlerAdapter,并赋值给handlerAdapters属性
         HandlerAdapter ha = context.getBean(HANDLER_ADAPTER_BEAN_NAME, HandlerAdapter.class);
         this.handlerAdapters = Collections.singletonList(ha);
      }
      ...
   }

   //如果容器中没有找到的话,则使用默认策略,即 HttpRequestHandlerAdapter、SimpleControllerHandlerAdapter、RequestMappingHandlerAdapter
   if (this.handlerAdapters == null) {
      this.handlerAdapters = getDefaultStrategies(context, HandlerAdapter.class);
   }
}

其他方法类似,这儿就不赘述了。

到此,DispatcherServlet就初始化好了,其内部的组件也都设置完成,可以提供服务了。

DispatcherServlet的service源码分析

从DispatcherServlet的继承关系上,servlet的service方法最终会调用到FrameworkServlet#processRequest方法,这是一个模板方法,封装了处理请求的一般逻辑,其doService方法由子类DispatcherServlet来实现。

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

   long startTime = System.currentTimeMillis();
   Throwable failureCause = null;
	
	//Locale的相关处理
   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 {
   		//具体的处理逻辑,由子类实现
      doService(request, response);
   }
   ...

   finally {
      resetContextHolders(request, previousLocaleContext, previousAttributes);
      if (requestAttributes != null) {
         requestAttributes.requestCompleted();
      }

      ...
			//发布请求处理事件(ServletRequestHandledEvent),使用的是DispatcherServlet的WebApplicationContext。
      publishRequestHandledEvent(request, response, startTime, failureCause);
   }
}

跟入DispatcherServlet#doService:

protected void doService(HttpServletRequest request, HttpServletResponse response) throws Exception {

   ....
	
	//设置MVC框架用到的一些属性到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());
	
 	...

   try {
   		//具体干活的方法,最核心的方法,封装了MVC的整体执行流程
      doDispatch(request, response);
   }
   ...
}

最核心的DispatcherServlet#doDispatch方法:

protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
   ...
   
      try {
      	//对multipartRequest的处理,使用到了multipartResolver组件。
         processedRequest = checkMultipart(request);
         multipartRequestParsed = (processedRequest != request);
				
        //获取请求对应的handler,使用到了HandlerMapping组件。这儿返回的是HandlerExecutionChain,里面封装了要应用的拦截器。
         mappedHandler = getHandler(processedRequest);
         
         //没有找到Handler时的处理,默认是404
         if (mappedHandler == null) {
            noHandlerFound(processedRequest, response);
            return;
         }
				
				//找到Handler对应的HandlerAdapter。
         // Determine handler adapter for the current request.
         HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
				
				 //对Get或者Head请求时的last-modified处理
         ...

					//应用拦截器的前置拦截,遍历调用拦截器的preHandle方法。如果没有通过拦截器,直接返回
         if (!mappedHandler.applyPreHandle(processedRequest, response)) {
            return;
         }
				
				//通过HandlerAdapter调用到真实的处理器上(即我们写的业务方法)
         // Actually invoke the handler.
         mv = ha.handle(processedRequest, response, mappedHandler.getHandler());			
					
				...
				//应用默认的ViewName,?
         applyDefaultViewName(processedRequest, mv);
         
         //应用拦截器的后置拦截,遍历调用拦截器的postHandle方法。可以看到,如果抛了一次,postHandle方法是不执行的。
         mappedHandler.applyPostHandle(processedRequest, response, mv);
      }
      catch (Exception ex) {
         dispatchException = ex;
      }
      ....
      
      //结果处理,这里面会使用viewResolver来解析视图,然后调用视图的方法来进行结果渲染。
      processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
   }
   catch (Exception ex) {
   		//抛异常是触发拦截器的完成后操作,遍历调用拦截器的afterCompletion方法。
      triggerAfterCompletion(processedRequest, response, mappedHandler, ex);
   }
   ...
}

可以看到,doDispatcher方法这是SpringMVC对请求处理流程的整体封装,但里面的具体工作又交由具体的组价负责处理。接下来对关键的几个组件分析下源码。

HandlerMapping组件

DispatcherServlet#getHandler方法,获取适当的HandlerExecutionChain:

protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
   if (this.handlerMappings != null) {
      //遍历所有的HandlerMapping,依次调用getHandler方法,返回第一个不为null的HandlerExecutionChain
      for (HandlerMapping hm : this.handlerMappings) {
        ...
         HandlerExecutionChain handler = hm.getHandler(request);
         if (handler != null) {
            return handler;
         }
      }
   }
   //没找到返回null
   return null;
}

上场的第一个关键组件是HandlerMapping组件,它定义了一个HandlerExecutionChain getHandler(HttpServletRequest request)方法,用于根据请求来解析出对应的Handler执行链。

先来看下SpringMVC给我提供了哪些具体的实现:

在这里插入图片描述

先介绍下SimpleUrlHandlerMapping这个最简单的实现,它通过显式方式来注册url和handler的映射关系。

因为AbstractHandlerMapping继承至ApplicationObjectSupport(它实现了ApplicationContextAware接口),因此spring容器在初始AbstractHandlerMapping类型的bean时会调用setApplicationContext方法,对于SimpleUrlHandlerMapping来说,spring容器在初始化它是会回调initApplicationContext方法:

public void initApplicationContext() throws BeansException {
   super.initApplicationContext();
   //将配置的url和handler的映射注册到AbstractUrlHandlerMapping的handlerMap中(这是最关键的属性)
   registerHandlers(this.urlMap);
}

其注册过程也比较简单,registerHandlers方法

protected void registerHandlers(Map<String, Object> urlMap) throws BeansException {
   ...
      urlMap.forEach((url, handler) -> {
         //如果url不是/开头,则加一个
         if (!url.startsWith("/")) {
            url = "/" + url;
         }
         //去除handler中的空格,这个handler是spring bean的名称
         if (handler instanceof String) {
            handler = ((String) handler).trim();
         }
         //调用父类AbstractUrlHandlerMapping的注册方法
         registerHandler(url, handler);
      });
   
}

跟入AbstractUrlHandlerMapping#registerHandler方法:

protected void registerHandler(String urlPath, Object handler) throws BeansException, IllegalStateException {
   ...
   Object resolvedHandler = handler;

   //如果handler是spring beanName,并且不是懒初始化的且是单例的,则通过getBean方法来创建具体的bean出来
   if (!this.lazyInitHandlers && handler instanceof String) {
      String handlerName = (String) handler;
      ApplicationContext applicationContext = obtainApplicationContext();
      if (applicationContext.isSingleton(handlerName)) {
         resolvedHandler = applicationContext.getBean(handlerName);
      }
   }

	//如果这个url已经注册了一个handler了,并且新的handler和当前要注册的还不一样,则抛出异常。如果一样,那么不管
   Object mappedHandler = this.handlerMap.get(urlPath);
   if (mappedHandler != null) {
      if (mappedHandler != resolvedHandler) {
         throw new IllegalStateException(
               "Cannot map " + getHandlerDescription(handler) + " to URL path [" + urlPath +
               "]: There is already " + getHandlerDescription(mappedHandler) + " mapped.");
      }
   }
   
   //注册一个新的url和handler
   else {
   		//注册RootHandler
      if (urlPath.equals("/")) {
        ...
         setRootHandler(resolvedHandler);
      }
      //注册DefaultHandler(默认handler是找不到handler时,使用默认的)
      else if (urlPath.equals("/*")) {
        ...
         setDefaultHandler(resolvedHandler);
      }
      else {
      	//加到map中,完成注册
         this.handlerMap.put(urlPath, resolvedHandler);
         ...
      }
   }
}

初始化好了之后,就可以调用它的getHandler方法了,同样,Spring惯用的模板方法模式又来了,调用入口在AbstractHandlerMapping#getHandler方法上:

public final HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
   //调用子类的具体实现
   Object handler = getHandlerInternal(request);
   //如果子类中没有找到handler,则使用默认的handler
   if (handler == null) {
      handler = getDefaultHandler();
   }
   //如果默认的handler没有配置,则返回null(即没有能够处理该请求的handler)
   if (handler == null) {
      return null;
   }
   
   //如果找到的handler是字符串,则当做spring beanName来实例化一个对象出来。
   // Bean name or resolved handler?
   if (handler instanceof String) {
      String handlerName = (String) handler;
      handler = obtainApplicationContext().getBean(handlerName);
   }
	
		//将handler包装成HandlerExecutionChain返回,将匹配该请求的拦截器封装进去。
   HandlerExecutionChain executionChain = getHandlerExecutionChain(handler, request);
   ...
   return executionChain;
}

再次,对于SimpleUrlHandlerMapping来说,getHandlerInternal方式位于AbstractUrlHandlerMapping#getHandlerInternal:

protected Object getHandlerInternal(HttpServletRequest request) throws Exception {
   //从request中解析出请求路径(比如 /hello.html)
   String lookupPath = getUrlPathHelper().getLookupPathForRequest(request);
   //通过请求路径查找handler
   Object handler = lookupHandler(lookupPath, request);
   
   //没找到
   if (handler == null) {
     
      Object rawHandler = null;
      //如果请求路径是"/",则返回RootHandler
      if ("/".equals(lookupPath)) {
         rawHandler = getRootHandler();
      }
      //如果还没匹配上,则返回DefaultHandler
      if (rawHandler == null) {
         rawHandler = getDefaultHandler();
      }
      
      //对找到的handler,如果是String,则当做springbean的名称,然后实例化一个对象出来。
      if (rawHandler != null) {
         // Bean name or resolved handler?
         if (rawHandler instanceof String) {
            String handlerName = (String) rawHandler;
            rawHandler = obtainApplicationContext().getBean(handlerName);
         }
         validateHandler(rawHandler, request);
         handler = buildPathExposingHandler(rawHandler, lookupPath, lookupPath, null);
      }
   }
  ...
   return handler;
}

继续AbstractUrlHandlerMapping#lookupHandler方法:

protected Object lookupHandler(String urlPath, HttpServletRequest request) throws Exception {
	//从之前注册的handlerMap中精确查找
   Object handler = this.handlerMap.get(urlPath);
   
   //找到了直接返回
   if (handler != null) {
      // Bean name or resolved handler?
      if (handler instanceof String) {
         String handlerName = (String) handler;
         handler = obtainApplicationContext().getBean(handlerName);
      }
      validateHandler(handler, request);
      return buildPathExposingHandler(handler, urlPath, urlPath, null);
   }
	
	//没找到,使用匹配查找模式
   List<String> matchingPatterns = new ArrayList<>();
   //遍历所有注册的url,通过PathMatcher来看请求的路径和注册的路径是否匹配,匹配上的话就加入到matchingPatterns中。
   for (String registeredPattern : this.handlerMap.keySet()) {
      if (getPathMatcher().match(registeredPattern, urlPath)) {
         matchingPatterns.add(registeredPattern);
      }
      ...
   }

	//通过对所有匹配的注册url进行比较排序之后,取得第一个元素就是最匹配的注册的url。
   String bestMatch = null;
   Comparator<String> patternComparator = getPathMatcher().getPatternComparator(urlPath);
   if (!matchingPatterns.isEmpty()) {
      matchingPatterns.sort(patternComparator);
      bestMatch = matchingPatterns.get(0);
   }
   
   //获取最佳匹配的handler
   if (bestMatch != null) {
      handler = this.handlerMap.get(bestMatch);
      if (handler == null) {
         if (bestMatch.endsWith("/")) {
            handler = this.handlerMap.get(bestMatch.substring(0, bestMatch.length() - 1));
         }
         ...
      }
      // Bean name or resolved handler?
      if (handler instanceof String) {
         String handlerName = (String) handler;
         handler = obtainApplicationContext().getBean(handlerName);
      }
      validateHandler(handler, request);
      String pathWithinMapping = getPathMatcher().extractPathWithinPattern(bestMatch, urlPath);
      //更多健壮性和兼容性处理。
      ...
      return buildPathExposingHandler(handler, bestMatch, pathWithinMapping, uriTemplateVariables);
   }

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

到此,分析了最简单的SimpleUrlHandlerMapping初始化和调用的逻辑,处理匹配模式查找有点复杂以外,其他的都比较简单。

接下来了解下BeanNameUrlHandlerMapping实现,它比SimpleUrlHandlerMapping要高级一点的是,它是自动发现和注册url和handler的映射关系的,和SimpleUrlHandlerMapping从继承结构上来说几乎一致,只是它的初始化有些不同:

org.springframework.web.servlet.handler.AbstractDetectingUrlHandlerMapping#initApplicationContext方法:

public void initApplicationContext() throws ApplicationContextException {
   super.initApplicationContext();
   //自动检测和注册handler
   detectHandlers();
}

protected void detectHandlers() throws BeansException {
		ApplicationContext applicationContext = obtainApplicationContext();
		
		//获取spring容器中的所有beanname
		String[] beanNames = (this.detectHandlersInAncestorContexts ?
				BeanFactoryUtils.beanNamesForTypeIncludingAncestors(applicationContext, Object.class) :
				applicationContext.getBeanNamesForType(Object.class));

		//遍历所有beanName
		for (String beanName : beanNames) {
			//找到当前这个beanName的所有可以做url的名称(它自己和它的别名),由子类实现。
			String[] urls = determineUrlsForHandler(beanName);
			if (!ObjectUtils.isEmpty(urls)) {
				//注册url和handler(此时是beanName)的映射
				registerHandler(urls, beanName);
			}
			...
		}
	}

org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping#determineUrlsForHandler方法:

protected String[] determineUrlsForHandler(String beanName) {
   //找到当前这个beanName的所有可以做url的名称,即它和它的别名中,以"/"开头的名字都算。
   List<String> urls = new ArrayList<>();
   if (beanName.startsWith("/")) {
      urls.add(beanName);
   }
   String[] aliases = obtainApplicationContext().getAliases(beanName);
   for (String alias : aliases) {
      if (alias.startsWith("/")) {
         urls.add(alias);
      }
   }
   return StringUtils.toStringArray(urls);
}

当完成了自动监测和注册映射之后,getHandler的操作和SimpleUrlHandlerMapping就一模一样了,没有任何区别。

HandlerMapping另外一个分支实现是RequestMappingHandlerMapping,这个才是我们最最常用的,它通过@RequestMapping注解的方式,来声明要处理的请求路径以及对应的handler(方法)。

因为RequestMappingHandlerMapping实现了InitializingBean接口,因此它的初始化逻辑位于afterPropertiesSet方法中:

public void afterPropertiesSet() {
	//初始化HandlerMethods,这里面会找到所有的@RequestMapping注解方法,然后将配置的url和方法注册到mappingRegistry中。
   initHandlerMethods();
}

protected void initHandlerMethods() {
		
		//spring容器中的所有beanNames
		String[] beanNames = (this.detectHandlerMethodsInAncestorContexts ?
				BeanFactoryUtils.beanNamesForTypeIncludingAncestors(obtainApplicationContext(), Object.class) :
				obtainApplicationContext().getBeanNamesForType(Object.class));
	
	
		for (String beanName : beanNames) {
			if (!beanName.startsWith(SCOPED_TARGET_NAME_PREFIX)) {
				Class<?> beanType = null;
				try {
					beanType = obtainApplicationContext().getType(beanName);
				}
				...
				//isHandler方法检查下beanType是不是要进一步解析的handler(常规是有Controller或者RequestMapping注解的)
				if (beanType != null && isHandler(beanType)) {
    			//检查方法,并注册满足条件的方法。
					detectHandlerMethods(beanName);
				}
			}
		}
		//一个扩展点,目前实现是空的。
		handlerMethodsInitialized(getHandlerMethods());
	}

org.springframework.web.servlet.handler.AbstractHandlerMethodMapping#detectHandlerMethods方法:

protected void detectHandlerMethods(final Object handler) {
   Class<?> handlerType = (handler instanceof String ?
         obtainApplicationContext().getType((String) handler) : handler.getClass());

   if (handlerType != null) {
      final Class<?> userType = ClassUtils.getUserClass(handlerType);
      //MethodIntrospector.selectMethods模板方法,作用是选择满足条件的方法,并返回一个map,key为方法,value为回调方法的返回值。
      Map<Method, T> methods = MethodIntrospector.selectMethods(userType,
            (MethodIntrospector.MetadataLookup<T>) method -> {
               try {
               		//回调方法的返回类型是RequestMappingInfo,由RequestMappingHandlerMapping实现,它包装了@RequestMapping的相关信息。
                  return getMappingForMethod(method, userType);
               }
               ...
            });
     	
     	//对选出来的方法进行注册
      methods.forEach((method, mapping) -> {
         Method invocableMethod = AopUtils.selectInvocableMethod(method, userType);
         //将handler,对应的方法,以及注解的信息 放到 mappingRegistry中。
         registerHandlerMethod(handler, invocableMethod, mapping);
      });
   }
}

org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping#getMappingForMethod:

protected RequestMappingInfo getMappingForMethod(Method method, Class<?> handlerType) {
   //解析@RequestMapping注解,创建RequestMappingInfo对象
   RequestMappingInfo info = createRequestMappingInfo(method);
   ...
   return info;
}

private RequestMappingInfo createRequestMappingInfo(AnnotatedElement element) {
		//在方法上找到@RequestMapping注解
		RequestMapping requestMapping = AnnotatedElementUtils.findMergedAnnotation(element, RequestMapping.class);
		RequestCondition<?> condition = (element instanceof Class ?
				getCustomTypeCondition((Class<?>) element) : getCustomMethodCondition((Method) element));
			//通过注解信息来构建RequestMappingInfo对象。
		return (requestMapping != null ? createRequestMappingInfo(requestMapping, condition) : null);
	}
	
protected RequestMappingInfo createRequestMappingInfo(
			RequestMapping requestMapping, @Nullable RequestCondition<?> customCondition) {
		
		RequestMappingInfo.Builder builder = RequestMappingInfo
				.paths(resolveEmbeddedValuesInPatterns(requestMapping.path()))
				.methods(requestMapping.method())
				.params(requestMapping.params())
				.headers(requestMapping.headers())
				.consumes(requestMapping.consumes())
				.produces(requestMapping.produces())
				.mappingName(requestMapping.name());
		if (customCondition != null) {
			builder.customCondition(customCondition);
		}
		return builder.options(this.config).build();
	}	

org.springframework.web.servlet.handler.AbstractHandlerMethodMapping.MappingRegistry#register方法:

public void register(T mapping, Object handler, Method method) {
   this.readWriteLock.writeLock().lock();
   try {
   		//构建HandlerMethod,它包装了handler和method,便于调用。
      HandlerMethod handlerMethod = createHandlerMethod(handler, method);
      assertUniqueMethodMapping(handlerMethod, mapping);

      //往mappingLookup这个map中放入mapping, handlerMethod的映射关系
      this.mappingLookup.put(mapping, handlerMethod);
			
			//如果注解中配置的是精确url,这注册精确url和mapping的映射关系(从这儿的注册关系可看出,查找的时候先查找urlLookup得到mapping,在查找mappingLookup得到handlerMethod)
      List<String> directUrls = getDirectUrls(mapping);
      for (String url : directUrls) {
         this.urlLookup.add(url, mapping);
      }

      ...
			//registry这个map中再保存mapping 和 MappingRegistration的关系。
      this.registry.put(mapping, new MappingRegistration<>(mapping, handlerMethod, directUrls, name));
   }
   finally {
      this.readWriteLock.writeLock().unlock();
   }
}

初始化好之后,来简单分析下getHandler的实现,对于RequestMappingHandlerMapping,会调用到org.springframework.web.servlet.handler.AbstractHandlerMethodMapping#getHandlerInternal方法上:

protected HandlerMethod getHandlerInternal(HttpServletRequest request) throws Exception {
   //得到请求路径
   String lookupPath = getUrlPathHelper().getLookupPathForRequest(request);
   
   this.mappingRegistry.acquireReadLock();
   try {
   		//找请求路径对应的HandlerMethod
      HandlerMethod handlerMethod = lookupHandlerMethod(lookupPath, request);
      return (handlerMethod != null ? handlerMethod.createWithResolvedBean() : null);
   }
   finally {
      this.mappingRegistry.releaseReadLock();
   }
}

继续org.springframework.web.servlet.handler.AbstractHandlerMethodMapping#lookupHandlerMethod:

protected HandlerMethod lookupHandlerMethod(String lookupPath, HttpServletRequest request) throws Exception {
   List<Match> matches = new ArrayList<>();
   //这个就是在urlLookup中去找(精确查找),找出来的是RequestMappingInfo
   List<T> directPathMatches = this.mappingRegistry.getMappingsByUrl(lookupPath);
   if (directPathMatches != null) {
      addMatchingMappings(directPathMatches, matches, request);
   }
   //如果没有找到,则在mappingLookup中找匹配的。
   if (matches.isEmpty()) {
      // No choice but to go through all mappings...
      addMatchingMappings(this.mappingRegistry.getMappings().keySet(), matches, request);
   }

	//找到了匹配上的,那么从中选择一个最合适的。
   if (!matches.isEmpty()) {
      Comparator<Match> comparator = new MatchComparator(getMappingComparator(request));
      matches.sort(comparator);

      Match bestMatch = matches.get(0);
      ...
      handleMatch(bestMatch.mapping, lookupPath, request);
      return bestMatch.handlerMethod;
   }
   
   //没有找到
   else {
      return handleNoMatch(this.mappingRegistry.getMappings().keySet(), lookupPath, request);
   }
}

源码中非常多细节没有展开分析,有条件的话,你最好debug走一遍代码。

到此,我们就将常用的HandlerMapping组件分析完了,可以看出原理简单,但是工程实践起来还是比较复杂的,特别是RequestMappingHandlerMapping的实现,为了开发者的体验更好,使用更简单,Spring做了大量的简化开发的工作,不过这也带来了spring框架的复杂性。

HandlerAdapter组件

HandlerAdapter,顾名思义,就是Handler的适配器,这样无论Handler的表现形式是什么,比如实现了Controller接口,实现了HttpRequestHandler接口,实现了Servlet,甚至就是一个常规方法,都可以通过对应的适配器,让dispatcherServlet可以以同样的方式调用。

HandlerAdapter的继承关系,SpringMVC提供了4种适配器实现,分别对应Controller接口,实现了HttpRequestHandler接口,实现了Servlet,和@RequestMapping注解的方法。

在这里插入图片描述

在核心的模板方法doDispatch中,重要的第二步是HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());,获取Handler对应的HandlerAdapter:

protected HandlerAdapter getHandlerAdapter(Object handler) throws ServletException {
   if (this.handlerAdapters != null) {
   		//遍历注册的handlerAdapters,找到第一个支持当前handler的适配器。
      for (HandlerAdapter ha : this.handlerAdapters) {
         ...
         if (ha.supports(handler)) {
            return ha;
         }
      }
   }
  ...
}

简单的看下org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter#supports方法:

public boolean supports(Object handler) {
	//如果handler类型是Controller,则返回true。
   return (handler instanceof Controller);
}

其他3个Adapter的实现类似,不展开。

找到HandlerAdapter之后,接下来就是通过它来进行调用了,先忽略拦截器的相关处理,后面单独说明。

mv = ha.handle(processedRequest, response, mappedHandler.getHandler());

还是以SimpleControllerHandlerAdapter举例:

org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter#handle方法:

public ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler)
      throws Exception {
	//直接进行类型转换后,调用对应接口的处理方法。
   return ((Controller) handler).handleRequest(request, response);
}

HttpRequestHandlerAdapter 和 SimpleServletHandlerAdapter 的调用实现与 SimpleControllerHandlerAdapter没什么两样。但RequestMappingHandlerAdapter的调用逻辑就比较复杂了:

org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter#handleInternal方法:

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

   ModelAndView mav;
   checkRequest(request);

		//是否要在同步块中执行,默认false
   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 {
			//调用handlerMethod(即我们的业务处理方法)
      mav = invokeHandlerMethod(request, response, handlerMethod);
   }

   ...

   return mav;
}

具体的handerMethod调用逻辑:

protected ModelAndView invokeHandlerMethod(HttpServletRequest request,
      HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {

   ServletWebRequest webRequest = new ServletWebRequest(request, response);
   try {
   		//web参数绑定
      WebDataBinderFactory binderFactory = getDataBinderFactory(handlerMethod);
      ModelFactory modelFactory = getModelFactory(handlerMethod, binderFactory);
			
			//封装一个ServletInvocableHandlerMethod,并设置参数解析器、参数发现器,预植模型数据等
      ServletInvocableHandlerMethod invocableMethod = createInvocableHandlerMethod(handlerMethod);
      if (this.argumentResolvers != null) {
         invocableMethod.setHandlerMethodArgumentResolvers(this.argumentResolvers);
      }
      if (this.returnValueHandlers != null) {
         invocableMethod.setHandlerMethodReturnValueHandlers(this.returnValueHandlers);
      }
      invocableMethod.setDataBinderFactory(binderFactory);
      invocableMethod.setParameterNameDiscoverer(this.parameterNameDiscoverer);

      ModelAndViewContainer mavContainer = new ModelAndViewContainer();
      mavContainer.addAllAttributes(RequestContextUtils.getInputFlashMap(request));
      modelFactory.initModel(webRequest, mavContainer, invocableMethod);
      mavContainer.setIgnoreDefaultModelOnRedirect(this.ignoreDefaultModelOnRedirect);

			//异步处理...
      ....
			
			//发起调用
      invocableMethod.invokeAndHandle(webRequest, mavContainer);
      ...

			//封装ModelAndView返回
      return getModelAndView(mavContainer, modelFactory, webRequest);
   }
   finally {
      webRequest.requestCompleted();
   }
}

跟入org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod#invokeAndHandle方法:

public void invokeAndHandle(ServletWebRequest webRequest, ModelAndViewContainer mavContainer,
      Object... providedArgs) throws Exception {

   Object returnValue = invokeForRequest(webRequest, mavContainer, providedArgs);
   setResponseStatus(webRequest);
		
	//调用结果为null处理
   if (returnValue == null) {
      if (isRequestNotModified(webRequest) || getResponseStatus() != null || mavContainer.isRequestHandled()) {
         mavContainer.setRequestHandled(true);
         return;
      }
   }
   else if (StringUtils.hasText(getResponseStatusReason())) {
      mavContainer.setRequestHandled(true);
      return;
   }
	
   ...
   try {
   		//使用returnValueHandlers对结果处理后返回
      this.returnValueHandlers.handleReturnValue(
            returnValue, getReturnValueType(returnValue), mavContainer, webRequest);
   }
   ...
}

继续跟入org.springframework.web.method.support.InvocableHandlerMethod#invokeForRequest方法:

public Object invokeForRequest(NativeWebRequest request, @Nullable ModelAndViewContainer mavContainer,
      Object... providedArgs) throws Exception {
	
	//获取方法参数Object[]
   Object[] args = getMethodArgumentValues(request, mavContainer, providedArgs);
  	
  	//通过反射调用业务处理方法
   Object returnValue = doInvoke(args);
   
   return returnValue;
}

org.springframework.web.method.support.InvocableHandlerMethod#doInvoke方法:

protected Object doInvoke(Object... args) throws Exception {
   ReflectionUtils.makeAccessible(getBridgedMethod());
   try {
      return getBridgedMethod().invoke(getBean(), args);
   }
   ...
}

因为我们的业务方法参数是完全不可预知的,并且在我的业务方法上,想接受啥参数,只需要定义一个参数名就好了,SpringMVC会自动将我们想要的参数给传进来,这儿有点意思的是方法参数的获取和封装,org.springframework.web.method.support.InvocableHandlerMethod#getMethodArgumentValues方法:

private Object[] getMethodArgumentValues(NativeWebRequest request, @Nullable 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];
      //初始话参数名称发现器,默认为DefaultParameterNameDiscoverer, 它里面包装其他的ParameterNameDiscoverer来具体处理
      // 1. StandardReflectionParameterNameDiscoverer JDK 8才支持的,通过-parameters参数编译后的class文件中保存有参数名信息,直接获取
      //2. LocalVariableTableParameterNameDiscoverer JDK8之前,编译的class文件,参数的名称在方法上是丢失了的,但是在class文件的局部变量表中还存有方法的参数名称,这个就是LocalVariableTable去获取方法名称。
      parameter.initParameterNameDiscovery(this.parameterNameDiscoverer);
      
      ...
      
      if (this.argumentResolvers.supportsParameter(parameter)) {
         try {
         		//解析出具体的参数值。
         		//HandlerMethodArgumentResolver 这个接口也是一个非常庞大的家族,SpringMVC为我们提供了20多种实现,来支持各种各样的参数解析(比如@RequestParam注解的解析器、PathVariable参数解析器等等),太多,不展开。
            args[i] = this.argumentResolvers.resolveArgument(
                  parameter, mavContainer, request, this.dataBinderFactory);
            continue;
         }
        ....
      }
     ...
   }
   return args;
}

ViewResolver组件

在调用完业务方法之后,DispatcherServlet会拿到一个ModelAndView结果,接下来就需要通过ViewResolver组件来解析出View对象。

org.springframework.web.servlet.DispatcherServlet#processDispatchResult方法:

private void processDispatchResult(HttpServletRequest request, HttpServletResponse response,
      @Nullable HandlerExecutionChain mappedHandler, @Nullable ModelAndView mv,
      @Nullable Exception exception) throws Exception {

   boolean errorView = false;

	//如果前面逻辑中存在异常,这儿进行统一的异常处理。
   if (exception != null) {
      if (exception instanceof ModelAndViewDefiningException) {
         mv = ((ModelAndViewDefiningException) exception).getModelAndView();
      }
      else {
         Object handler = (mappedHandler != null ? mappedHandler.getHandler() : null);
         //通过HandlerExceptionResolver来解析异常,返回异常的ModelAndView对象。
         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);
      ...
   }
   
   ...

	//触发拦截器的afterCompletion方法
   if (mappedHandler != null) {
      mappedHandler.triggerAfterCompletion(request, response, null);
   }
}

跟入org.springframework.web.servlet.DispatcherServlet#render方法:

protected void render(ModelAndView mv, HttpServletRequest request, HttpServletResponse response) throws Exception {
  ...

   View view;
   String viewName = mv.getViewName();
   if (viewName != null) {
      //使用ViewResolver,将viewName解析为View对象。
      view = resolveViewName(viewName, mv.getModelInternal(), locale, request);
     ...
   }
   else {
      //直接返回的就是View对象,则直接使用,不需要解析。
      view = mv.getView();
     	...
   }

   // Delegate to the View object for rendering.
   ...
   try {
      if (mv.getStatus() != null) {
         response.setStatus(mv.getStatus().value());
      }
      //调用View的render方法,实现最终的渲染。
      view.render(mv.getModelInternal(), request, response);
   }
   ...
}

解析方法org.springframework.web.servlet.DispatcherServlet#resolveViewName:

protected View resolveViewName(String viewName, @Nullable Map<String, Object> model,
      Locale locale, HttpServletRequest request) throws Exception {

   if (this.viewResolvers != null) {
   		//从注册的ViewResolver中,第一个解析出来的View不为null就直接返回。
      for (ViewResolver viewResolver : this.viewResolvers) {
         View view = viewResolver.resolveViewName(viewName, locale);
         if (view != null) {
            return view;
         }
      }
   }
   return null;
}

我们以 BeanNameViewResolver 这个ViewResolver为例,看下它的解析方法,org.springframework.web.servlet.view.BeanNameViewResolver#resolveViewName:

public View resolveViewName(String viewName, Locale locale) throws BeansException {
   ApplicationContext context = obtainApplicationContext();
   //一些前置检查
   ...
   //直接通过bean的名称获取对应的View对象。
   return context.getBean(viewName, View.class);
}

对于View来说,不同的模板引擎提供了不同的实现,我们已自带的InternalResourceView为例(通常处理jsp页面渲染),看看它的render方法,该方法位于它的父类AbstractView中:

org.springframework.web.servlet.view.AbstractView#render方法:

public void render(@Nullable Map<String, ?> model, HttpServletRequest request,
      HttpServletResponse response) throws Exception {
	
	//合并所有的变量,包括request中的各种属性,调用返回的模型属性等
		Map<String, Object> mergedModel = createMergedOutputModel(model, request, response);
   //对response做一些渲染前的准备。
   prepareResponse(request, response);
   //渲染,调用子类方法
   renderMergedOutputModel(mergedModel, getRequestToExpose(request), response);
}

对于InternalResourceView之类来说,渲染调用的是:org.springframework.web.servlet.view.InternalResourceView#renderMergedOutputModel方法:

protected void renderMergedOutputModel(
      Map<String, Object> model, HttpServletRequest request, HttpServletResponse response) throws Exception {

	//把模型数据暴露到request的属性中
   // Expose the model object as request attributes.
   exposeModelAsRequestAttributes(model, request);

   // Expose helpers as request attributes, if any.
   exposeHelpers(request);

	//获取跳转的url
   // Determine the path for the request dispatcher.
   String dispatcherPath = prepareForRendering(request, response);

	//使用RequestDispatcher进行服务端跳转
	// Obtain a RequestDispatcher for the target resource (typically a JSP).
   RequestDispatcher rd = getRequestDispatcher(request, dispatcherPath);
  ...
  
   else {
      ...
      //进行forward跳转。
      rd.forward(request, response);
   }
}

到此,我们分析了SpringMVC一次典型的请求处理流程,主体逻辑在DispatcherServlet#doDispatch方法中,它是一个执行框架,具体干活的委托给HandlerMapping、HandlerAdapter、和ViewResolver组件进行具体的处理。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值