Java-第十五部分-源码解读-doDispatch流程和九大组件

166 阅读9分钟

源码解读全文

SpringMVC请求流程

  • Tomcat根据请求路径找到对应的应用,在根据后续的uri找到对应的servlet资源
  • HandlerMapping负责保证url注册进来,并记录对应的处理方法或Bean

初始化容器完毕后,发布事件,初始化九大组件,初始化过程中,加载配置文件,通过getBean创建各大组件,在组件初始化期间调用后置处理器方法,解析Mapping信息

  • HandlerAdapter负责处理这个请求,调用处理方法

Servlet与Service

  • ServletGenericServlet只有service接口
  • HttpServlet,重写了service,并拆分成不同的请求方式 image.png
  • FrameworkServlet,重写了doGet/doPost/...,其中调用processRequest(request, response);真正处理请求

其中调用了DispatcherServletdoService方法

  • 当一个请求,进行时,调用HttpServletservice,其中调用FrameworkServlet中重写的do簇方法,在调用processRequest(request, response);时,真正调用了DispatcherServletdoService,最终调用doDispatch(request, response);

doService

  • DispatcherServlet调用
  • 保存request域中的对象保存,attributesSnapshot.put(attrName, request.getAttribute(attrName));,并保存一些内容

容器、国际化解析器、主题解析器

  • 初始化闪存管理器,为重定向共享数据FlashMap inputFlashMap = this.flashMapManager.retrieveAndUpdate(request, response);
  • 进行请求派发,派发给controllerdoDispatch(request, response);,进行真正的资源访问

doDispatch

  • 异步请求的支持WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
  • 检查是否为文件上传请求processedRequest = checkMultipart(request);,处理为新的请求,并更新标志位multipartRequestParsed = (processedRequest != request);
  • 获取handler执行链,mappedHandler = getHandler(processedRequest);,决定使用哪个handler处理当前请求
  1. 返回的实际上是HandlerExecutionChain,目标方法+拦截器
  2. 找不到则返回404noHandlerFound(processedRequest, response);
  • 获取当前方法的适配器,HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
  • 调用拦截器方法,进行前置拦截mappedHandler.applyPreHandle(processedRequest, response)
  • 进行对应的处理并获得对应的mondelAndViewmv = ha.handle(processedRequest, response, mappedHandler.getHandler());
  1. HttpRequestHandlerAdapterSimpleControllerHandlerAdapter都是调用重写的handlerRequest
  2. RequestMappingHandlerAdapter处理注解的方法,处理返回值和参数
  3. @ResponseBody在这一步执行执行期间,就已经写出内容
  • 没有指定跳转的页面,给一个默认页面applyDefaultViewName(processedRequest, mv);

通过RequestToViewNameTranslator视图翻译器,从request域中获取默认的,直接返回request请求的地址,作为默认的view地址

  • 执行拦截器的后置拦截mappedHandler.applyPostHandle(processedRequest, response, mv); image.png
  • 处理结果processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
  1. 如果有异常,处理异常,返回异常的ModelAndView,通过异常解析器进行解析
  2. 渲染,解析模型和视图render(mv, request, response);

checkMultipart

  • 使用文件上传解析器判断,根据请求头中的content-type判断this.multipartResolver.isMultipart(request)
public boolean isMultipart(HttpServletRequest request) {
   return StringUtils.startsWithIgnoreCase(request.getContentType(),
         (this.strictServletCompliance ? MediaType.MULTIPART_FORM_DATA_VALUE : "multipart/"));
}

getHandler

  • handlerMapping保存请求的映射,依次遍历默认的HandlerMapping,能否处理当前请求

策略模式,是否能处理当前请求

protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
   if (this.handlerMappings != null) {
      for (HandlerMapping mapping : this.handlerMappings) {
         HandlerExecutionChain handler = mapping.getHandler(request);
         if (handler != null) {
            return handler;
         }
      }
   }
   return null;
}

image.png

  1. BeanNameUrlHandlerMappingBean的名字作为url路径进行映射,组件名为/开始;在初始化完对应的Bean时,调用初始化前的Aware后置处理器setApplicationContext,进行处理,解析BeanName带斜杠的Handler,保存在handlerMap中;HttpRequestHandler,实现这个接口,并注解为@Controller,名字以/开始
  2. RequestMappingHandlerMappingRequest注解进行映射
  3. RouterFunctionHandlerMapping,支持函数式处理,webflux相关功能
  • RequestMappingHandlerMapping调用getHandler,最终调用HandlerMethod handlerMethod = lookupHandlerMethod(lookupPath, request);寻求处理当前请求的handlerMethod,能执行当前请求的方法
  1. 通过MappingRegistry,找到对应方法,List<T> directPathMatches = this.mappingRegistry.getMappingsByDirectPath(lookupPath); 2.lookupPath,当前访问的路径
  • 构造处理器链HandlerExecutionChain executionChain = getHandlerExecutionChain(handler, request);
  1. 将当前handler转换成拦截器,并把环境中所有拦截器加入,从容器中获取
  2. 责任链模式 image.png

getHandlerAdapter

  • 获取所有的适配器,遍历,看是否支持这个handler,也就是执行方法

初始化九大组件中,加载默认的配置文件

protected HandlerAdapter getHandlerAdapter(Object handler) throws ServletException {
   if (this.handlerAdapters != null) {
      for (HandlerAdapter adapter : this.handlerAdapters) {
         if (adapter.supports(handler)) {
            return adapter;
         }
      }
   }
   //...
}

image.png

  1. HttpRequestHandlerAdapter,处理HttpRequestHandler接口的处理方法对应BeanNameUrlHandlerMapping
  2. SimpleControllerHandlerAdapter,处理Controller接口,对应BeanNameUrlHandlerMapping
  3. RequestMappingHandlerAdapter,处理HandlerMethod接口
  4. HandlerFunctionAdapter,处理HandlerFunction,支持webflux
  • supports是否实现了某个handler接口
public boolean supports(Object handler) {
   return (handler instanceof HttpRequestHandler);
}

render

  • 视图解析器,通过视图名,解析出视图view = resolveViewName(viewName, mv.getModelInternal(), locale, request); image.png
  • 根据不同的view渲染视图view.render(mv.getModelInternal(), request, response);,通过response返回给前端

RequestMappingHandlerMapping

  • 利用InitializingBean初始化Mapping
  • 包含一个MappingRegistry,是一个封装类 image.png image.png

registerHandlerMethod

  • 解析Controller的方法,并进行访问路径的映射,准备映射中心
  • DispatcherServlet创建对象后,initServlet()期间初始化完容器,发布事件,基于事件机制调用initStrategies,初始化九大组件
  • 初始化handlerMappinginitHandlerMappings(context);,通过配置文件加载默认的HandlerMapping,通过IOC容器创建Beancontext.getAutowireCapableBeanFactory().createBean(clazz);
  • 创建Bean后,进行初始化exposedObject = initializeBean(beanName, exposedObject, mbd);invokeInitMethods(beanName, wrappedBean, mbd);其中((InitializingBean) bean).afterPropertiesSet();

RequestMappingHandlerMapping的父类AbstractHandlerMethodMapping实现了InitializingBean的接口,通过重写afterPropertiesSet进行自定义初始化 image.png

  • 其中调用initHandlerMethods();,保存所有路径和方法的映射信息

initHandlerMethods

  • 获取子web容器的所有组件getCandidateBeanNames(),遍历处理
  • 分析当前BeanHandlerMethodsdetectHandlerMethods(beanName);

提前进行判断isHandler(beanType),是否有Controller注解和RequestMapping注解

  • getMappingForMethod中为每一个方法尝试创建请求映射信息RequestMappingInfo info = createRequestMappingInfo(method);

如果不能,则为null

  • 将所有封装后的信息封装成Map<Method, T> methods
  • 遍历每一个方法进行注册,registerHandlerMethod(handler, invocableMethod, mapping); image.png
  • 最终注册到registry中,this.registry.put(mapping, new MappingRegistration<>(mapping, handlerMethod, directPaths, name, corsConfig != null));
  1. mapping实际上是路径+请求方式
  2. 会将路径和映射信息保存在pathLookup image.png

createRequestMappingInfo

  • 看当前方法是否有RequestMapping注解,如果有,对信息进行封装
private RequestMappingInfo createRequestMappingInfo(AnnotatedElement element) {
   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);
}

HandlerAdapter

  • 解析请求中的参数,并正确地调用方法,返回ModelAndView
  • 策略模式,动态策略的体现boolean supports(Object handler);,决定能否处理这个handler
  • 适配器模式,进行处理ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception;

RequestMappingHandlerAdapter初始化过程

  • 利用InitializingBean生命周期 image.png

RequestMappingHandlerAdapter执行过程

  • 反射执行方法,确定参数和返回值
  • 调用handleInternal(HttpServletRequest request, HttpServletResponse response, HandlerMethod handlerMethod)
  • 检查请求方式checkRequest(request);
  • 会话锁,每一个用户和服务器交互只有一个会话,保证高并发安全 image.png
  • 执行目标方法mav = invokeHandlerMethod(request, response, handlerMethod);

invokeHandlerMethod

  • 装饰器模式,封装成webRequestServletWebRequest webRequest = new ServletWebRequest(request, response);
  • 提前准备,准备核心组件
  1. 数据绑定器,请求数据到参数方法的映射,WebDataBinderFactory binderFactory = getDataBinderFactory(handlerMethod);,数据类型转换、错误处理
  2. 模型工厂,ModelFactory modelFactory = getModelFactory(handlerMethod, binderFactory);,交给页面的数据
  • 封装HandlerMethodServletInvocableHandlerMethod invocableMethod = createInvocableHandlerMethod(handlerMethod);,设置参数解析器和返回值处理器,无论目标返回什么,都要适配成ModelAndView

具有执行功能

  • 准备模型和视图的临时容器ModelAndViewContainer mavContainer = new ModelAndViewContainer();

保存处理过程中产生和模型视图有关的数据,在整个请求处理期间共享数据

  • 处理异步请求的准备WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
  • 做好提前准备后,分为两步
  1. 执行方法invocableMethod.invokeAndHandle(webRequest, mavContainer);
  2. 抽取ModelAndView数据,getModelAndView(mavContainer, modelFactory, webRequest);,数据在model中,页面在view

invokeAndHandle

  • 执行目标方法,获取返回值Object returnValue = invokeForRequest(webRequest, mavContainer, providedArgs);
  1. 获取方法参数列表Object[] args = getMethodArgumentValues(request, mavContainer, providedArgs);
  2. 反射执行doInvoke(args);
  • 处理返回值,this.returnValueHandlers.handleReturnValue( returnValue, getReturnValueType(returnValue), mavContainer, webRequest);

过程中会将数据放在mavContainer

getMethodArgumentValues

  • 确定方法参数值
  • 获取方法的所有参数以及相关信息MethodParameter[] parameters = getMethodParameters();
  • 遍历确认每个参数的值Object[] args = new Object[parameters.length];
  • 在已提供的参数中找args[i] = findProvidedArgument(parameter, providedArgs);

实际上是没有的

  • 查看能支持处理的参数解析器this.resolvers.supportsParameter(parameter)

27个参数解析器,负责解析参数resolveArgument

  • 进行参数解析args[i] = this.resolvers.resolveArgument(parameter, mavContainer, request, this.dataBinderFactory);
  1. RequestParamMethodArgumentResolver获取原生request的参数列表进行匹配
  2. 解析完成后,也会放到modelAndView

HandlerMethodArgumentResolverComposite

  • 策略模式,保存27个参数解析器和缓存,负责解析参数 image.png
  • 遍历所有解析器,是否能够处理该参数getArgumentResolver(parameter)
  1. 先从缓存中获取HandlerMethodArgumentResolver result = this.argumentResolverCache.get(parameter);
  2. 遍历解析器,通过类型和注解查看是否支持,resolver.supportsParameter(parameter)
  3. 找到之后,放入缓存
  • 不同的解析器,有不同的解析方式resolveArgument

handleReturnValue

  • 找到返回值处理器HandlerMethodReturnValueHandler handler = selectHandler(returnValue, returnType);

遍历所有返回值处理器,进行判断

  • 处理返回值handler.handleReturnValue(returnValue, returnType, mavContainer, webRequest);
  1. RequestResponseBodyMethodProcessor优先级比ViewNameMethodReturnValueHandler高,标注了RequestBodyString返回,优先被处理

HandlerMethodReturnValueHandlerComposite

  • 策略模式,包括15种返回处理器 image.png
  • handler.supportsReturnType(returnType)

是否中某个类的实现类

  • RequestResponseBodyMethodProcessor利用MessageVoncerter消息转换器,直接获取原生response的输出流,使用Json格式输出
  • ViewNameMethodReturnValueHandler,通过判断返回值是否为redict:开头,判断是否为重定向

getModelAndView

  • 封装成ModelAndView
  • 更新数据,用于请求转发共享数据modelFactory.updateModel(webRequest, mavContainer);

session中的数据更新到request

  • 准备model对象ModelMap model = mavContainer.getModel();
  • 封装ModelAndView mav = new ModelAndView(mavContainer.getViewName(), model, mavContainer.getStatus());
  • 重定向数据的共享RequestContextUtils.getOutputFlashMap(request).putAll(flashAttributes);

先把数据移动到request,再移动到session

SpringMVC的九大组件

  • 全是接口,可以自定义实现
// 文件上传解析器
@Nullable
private MultipartResolver multipartResolver;

// 国际化解析器
/** LocaleResolver used by this servlet. */
@Nullable
private LocaleResolver localeResolver;

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

// handler(处理器,处理请求,也就是Controller)映射,保存所有请求由谁来处理的映射关系
/** List of HandlerMappings used by this servlet. */
@Nullable
private List<HandlerMapping> handlerMappings;

// 处理器的适配器,超级反射工具,将请求反射成目标的处理方法
/** List of HandlerAdapters used by this servlet. */
@Nullable
private List<HandlerAdapter> handlerAdapters;

// 处理器的异常解析器
/** List of HandlerExceptionResolvers used by this servlet. */
@Nullable
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;

初始化流程

  • Tomcat启动,触发DispatcherServlet初始化,容器初始化
  • 发布当前上下文环境刷新完成事件,publishEvent(new ContextRefreshedEvent(this));,调用监听器SourceFilteringListeneronApplicationEvent

利用Spring的事件驱动

  • 调用FrameworkServletonApplicationEvent(event);,调用onRefresh(event.getApplicationContext());由子类实现
  • DispatcherServlet中重写onRefresh,调用initStrategies

初始化组件

  • 如果需要定制组件,只需要自定义放入组件
protected void initStrategies(ApplicationContext context) {
   initMultipartResolver(context);
   initLocaleResolver(context);
   initThemeResolver(context);
   initHandlerMappings(context);
   initHandlerAdapters(context);
   initHandlerExceptionResolvers(context);
   initRequestToViewNameTranslator(context);
   initViewResolvers(context);
   initFlashMapManager(context);
}
  • 具体初始化流程,都是一样的,先从容器中获取,
  1. 有就装配,
  2. 没有就捕获异常,设置为null,或根据默认策略进行装配
  3. 只有MultipartResolver会为null,需要导入相关的包,并进行配置
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.
      // 在`DispatcherServlet类路径下找到资源`DispatcherServlet.properties`,进行资源加载
      this.localeResolver = getDefaultStrategy(context, LocaleResolver.class);
      if (logger.isTraceEnabled()) {
         logger.trace("No LocaleResolver '" + LOCALE_RESOLVER_BEAN_NAME +
               "': using default [" + this.localeResolver.getClass().getSimpleName() + "]");
      }
   }
}

DispatcherServlet.properties

  • 读取默认配置文件,进行类加载,并创建对象 image.png image.png