SpringMVC请求流程源码分析(springmvc请求处理)

  本篇文章为你整理了SpringMVC请求流程源码分析(springmvc请求处理)的详细内容,包含有spring mvc请求流程 springmvc请求处理 springmvc工作流程源码 springmvc处理http请求和响应 SpringMVC请求流程源码分析,希望能帮助你了解 SpringMVC请求流程源码分析。

  删除 src/main/webapp/WEB-INF/web.xml 配置文件。从tomcat的示例工程中找一份web.xml替换,这里推荐从\webapps\ROOT\WEB-INF中拿,并且在其中添加context的监听器和servlet配置,配置如下。

  

 !--Context 加载监听器 -- 

 

   listener

   listener-class org.springframework.web.context.ContextLoaderListener /listener-class

   /listener

   servlet

   servlet-name dispatcherServlet /servlet-name

   servlet-class org.springframework.web.servlet.DispatcherServlet /servlet-class

   init-param

   param-name contextConfigLocation /param-name

   param-value classpath:application.xml /param-value

   /init-param

   !--Web服务器一旦启动,Servlet就会实例化创建对象,然后初始化(预备创建对象)--

   load-on-startup 1 /load-on-startup

   /servlet

   servlet-mapping

   servlet-name dispatcherServlet /servlet-name

   url-pattern / /url-pattern

   /servlet-mapping

  

 

  在 resources 目录中创建springmvc.xml文件,并添加如下配置:

  

 ?xml version="1.0" encoding="UTF-8"? 

 

   beans xmlns="http://www.springframework.org/schema/beans"

   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

   xmlns:context="http://www.springframework.org/schema/context"

   xmlns:mvc="http://www.springframework.org/schema/mvc"

   xsi:schemaLocation="http://www.springframework.org/schema/beans

   http://www.springframework.org/schema/beans/spring-beans.xsd

   http://www.springframework.org/schema/context

   http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/mvc https://www.springframework.org/schema/mvc/spring-mvc.xsd"

   !--配置spring包扫描路径,被@Component、@Controller、@Service、@Repository标注的类都会交由Spring托管一个Bean--

   context:component-scan base-package="com.ybe.*"/

   !--配置视图解析器--

   bean

   property name="prefix" value="/"/

   property name="suffix" value=".jsp" /property

   /bean

   /beans

  

 

  在 src/main/webapp/WEB-INF/下添加 applicationContext.xml文件,配置如下:

  

 ?xml version="1.0" encoding="UTF-8"? 

 

   beans xmlns="http://www.springframework.org/schema/beans"

   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

   xmlns:context="http://www.springframework.org/schema/context"

   xmlns:mvc="http://www.springframework.org/schema/mvc"

   xsi:schemaLocation="http://www.springframework.org/schema/beans

   http://www.springframework.org/schema/beans/spring-beans.xsd

   http://www.springframework.org/schema/context

   http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/mvc https://www.springframework.org/schema/mvc/spring-mvc.xsd"

   /beans

  

 

  创建Controller类,代码如下:

  

package com.ybe.controller;

 

  import org.springframework.stereotype.Controller;

  import org.springframework.web.bind.annotation.RequestMapping;

  @Controller

  public class HelloController {

   @RequestMapping("/hello")

   public String helloWorld(){

   System.out.println("hello world");

   return "index";

  

 

  2.工程配置

  
配置浏览器地址,点击上图左上角的 + 号,选择Tomcat Server选项后,点击 Deployment 选项,点击 右边的 + 号。
 

  
3.启动工程

  二、SpringMVC启动过程

  ​ SpringMVC是依赖Java的Web容器技术,整个springmvc的启动过程是建立在Servlet技术基础上的。SpringMVC借助Web容器和Servelt的生命周期进行了扩展。父容器的初始化在 ContextLoaderListener 类中initWebApplicationContext方法进行,子容器的初始化在 DispatcherServlet 中init方法中进行。

  1.父容器启动过程

  ​ 如果web.xml中配置了ContextLoaderListener监听器,则web容器启动的时候先会调用监听器ContextLoaderListener的initWebApplicationContext方法。整个过程如下图:
 

  ​ initWebApplicationContext中的整个过程就是创建了一个spring容器(父容器),并且根据springApplication.xml的配置内容往Spring容器中注入Bean对象。最后把spring容器(this.context对象)放入serveltContext 的属性中。

  2.子容器启动过程(SpringMvc容器)

  ​ DispatcherServlet 是在web.xml配置文件中配置的Servlet类,是SpringMVC的请求分发核心类。所有的请求都由DispatcherServlet去分发处理。

  ​ DispatcherServlet 的继承关系如下图:
 

  ​ 上图可知DispatcherServlet 继承了HttpServletBean。在HttpServletBean中重写了init(),Web容器启动的时候会根据配置文件中定义的Servlet进行创建,并且会根据配置项(load-on-startup)觉定在什么时候调用Servlet的init方法,init方法在整个Servlet的生命周期中只会调用一次。初始化init方法的主体实现过程如下:
 

  1.WebApplicationContextUtils.getWebApplicationContext(getServletContext()) 从ServletContext中获取属性为WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE的对象,即Spring父容器对象。

  2.wac.addApplicationListener(new SourceFilteringListener(wac, new ContextRefreshListener())),给子容器添加应用监听器,该监听器在后面的finishRefresh()方法中进行触发,方法里面封装了初始化SpringMVC中九大组件的逻辑。

  3.publishEvent(new ContextRefreshedEvent(this))发布Context刷新事件,会触发SourceFilteringListener监听器,最终进行initStrategies的调用。

  3.九大组件的初始化

  initStrategies是SpringMVC中九大组件的初始化方法其中9个方法对应9个组件的初始化,本文中只讲映射器和适配器的创建过程,initStrategies代码如下:

  

// 初始化 MultipartResolver:主要用来处理文件上传.如果定义过当前类型的bean对象,那么直接获取,如果没有的话,可以为null

 

  initMultipartResolver(context);

  // 初始化 LocaleResolver:主要用来处理国际化配置,基于URL参数的配置(AcceptHeaderLocaleResolver),基于session的配置(SessionLocaleResolver),基于cookie的配置(CookieLocaleResolver)

  initLocaleResolver(context);

  // 初始化 ThemeResolver:主要用来设置主题Theme

  initThemeResolver(context);

  // 初始化 HandlerMapping:映射器,用来将对应的request跟controller进行对应

  initHandlerMappings(context);

  // 初始化 HandlerAdapter:处理适配器,主要包含Http请求处理器适配器,简单控制器处理器适配器,注解方法处理器适配器

  initHandlerAdapters(context);

  // 初始化 HandlerExceptionResolver:基于HandlerExceptionResolver接口的异常处理

  initHandlerExceptionResolvers(context);

  // 初始化 RequestToViewNameTranslator:当controller处理器方法没有返回一个View对象或逻辑视图名称,并且在该方法中没有直接往response的输出流里面写数据的时候,spring将会采用约定好的方式提供一个逻辑视图名称

  initRequestToViewNameTranslator(context);

  // 初始化 ViewResolver: 将ModelAndView选择合适的视图进行渲染的处理器

  initViewResolvers(context);

  // 初始化 FlashMapManager: 提供请求存储属性,可供其他请求使用

  initFlashMapManager(context);

  

 

  1.处理器映射器的初始化

  1.initHandlerMappings 初始化映射器,在此方法中第一步会获取容器中实现了HandlerMapping的Bean对象,如果有则用自定义的HandlerMapping实现类作为this.handlerMappings的值;如果没有自定义类,则获取SpringMVC预先定义好的策略类。代码流程如下:
 

  2.ClassPathResource resource = new ClassPathResource(DEFAULT_STRATEGIES_PATH, DispatcherServlet.class);获取DispatcherServlet.properties资源文件,位置在spring-webmvc中,路径resources/org/springframework/web/servlet/DispatcherServlet.properties,该资源文件中定义了SpringMVC组件的默认实现策略类,具体内容如下:

  

# Default implementation classes for DispatcherServlets 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 预制的策略类有 BeanNameUrlHandlerMapping、RequestMappingHandlerMapping、RouterFunctionMapping,其中 RequestMappingHandlerMapping 是我们常用的 HandlerMapping对象。

  3.RequestMappingHandlerMapping 的初始化,因为RequestMappingHandlerMapping 实现了InitializingBean接口,所以在容器中初始化完之后会执行afterPropertiesSet方法,其中会调用super.afterPropertiesSet();父类为AbstractHandlerMethodMapping。此方法中会调用initHandlerMethods(),代码如下:

  

protected void initHandlerMethods() {

 

   // 遍历 Bean ,逐个处理

   for (String beanName : getCandidateBeanNames()) {

   // 排除目标代理类,AOP 相关,可查看注释

   if (!beanName.startsWith(SCOPED_TARGET_NAME_PREFIX)) {

   // 处理 Bean

   processCandidateBean(beanName);

   // 初始化处理器的方法们,目前没有特殊业务逻辑,只是打印日志

   handlerMethodsInitialized(getHandlerMethods());

  

 

  4.processCandidateBean方法中会判断BeanName的Bean是否有Controller.class或者RequestMapping.class注解来生成具体的HandlerMethod对象。

  2.处理器适配器的初始化

  1.initHandlerAdapters 适配器初始化,过程和映射器相似。从上面的内容可知HandlerAdapter与之的策略类有 HttpRequestHandlerAdapter、SimpleControllerHandlerAdapter、RequestMappingHandlerAdapter、HandlerFunctionAdapter。其中 RequestMappingHandlerAdapter 是我们常用的处理器适配器。

  2.RequestMappingHandlerAdapter实现了InitializingBean接口,在容器中初始化完之后会调用afterPropertiesSet方法,在此方法中会初始化参数解析器、绑定参数解析器、返回值解析器。此方法代码如下:

  

@Override

 

  public void afterPropertiesSet() {

   // Do this first, it may add ResponseBody advice beans

   // 初始化注释了@ControllerAdvice的类的相关属性

   initControllerAdviceCache();

   // 初始化 argumentResolvers 属性

   if (this.argumentResolvers == null) {

   List HandlerMethodArgumentResolver resolvers = getDefaultArgumentResolvers();

   this.argumentResolvers = new HandlerMethodArgumentResolverComposite().addResolvers(resolvers);

   // 初始化 initBinderArgumentResolvers 属性

   if (this.initBinderArgumentResolvers == null) {

   List HandlerMethodArgumentResolver resolvers = getDefaultInitBinderArgumentResolvers();

   this.initBinderArgumentResolvers = new HandlerMethodArgumentResolverComposite().addResolvers(resolvers);

   // 初始化 returnValueHandlers 属性

   if (this.returnValueHandlers == null) {

   List HandlerMethodReturnValueHandler handlers = getDefaultReturnValueHandlers();

   this.returnValueHandlers = new HandlerMethodReturnValueHandlerComposite().addHandlers(handlers);

  

 

  4.拦截器的初始化

  因为所有的HandlerMapping预制的策略类都继承了AbstractHandlerMapping ,而AbstractHandlerMapping 实现了ApplicationContextAware接口,所以在具体的HandlerMapping策略类初始化完之后会调用initApplicationContext方法,该方法中具体实现了拦截器的创建,代码如下:

  

protected void initApplicationContext() throws BeansException {

 

   // 空实现,交给子类实现,用于注册自定义的拦截器到interceptors中,目前暂无子类实现

   extendInterceptors(this.interceptors);

   // 扫描已注册的MappedInterceptor的Bean们,添加到adaptedInterceptors中

   detectMappedInterceptors(this.adaptedInterceptors);

   // 将interceptors初始化成 HandlerInterceptor类型,添加到adaptedInterceptors中

   initInterceptors();

  

 

  三、SpringMVC请求过程

  1.请求流程图

  2.业务描述

  1.请求进来后会调用FrameworkServlet的service()方法,该方法中会调用(HttpServlet) super.service 方法,其中会调用doXXX()方法,而doXXX()方法在FrameworkServlet重写,每个doXXX()方法中又会调用processRequest(request, response)方法,processRequest中会调用doService(),doService()中又会调用doDispatch(),整个业务处理逻辑定义在doDispatch方法中。

  2.循环遍历在启动过程中创建的handlerMapping处理器映射器集合查找对应处理器,传入参数为 request 请求对象,返回的是HandlerExecutionChain类型的对象,代码如下

  

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;

  

 

  在getHandler方法中如果找到具体的handler对象(HandlerMethod类型),会继续封装handler对象为一个executionChain处理器链链对象(HandlerExecutionChain类型),代码如下:

  

Object handler = getHandlerInternal(request);

 

  if (handler == null) {

   handler = getDefaultHandler();

  // 如果handler为null 即返回null。说明当前的处理器映射器不匹配。

  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);

  

 

  getHandlerInternal是多态方法,具体的实现类中有不同的实现方式。查找具体的HandlerMethod逻辑比较复杂,请自行查看源码。

  3.循环遍历在启动过程中创建的handlerAdapters处理器适配器集合查找对应处理器适配器,传入参数为handler处理器对象,返回的是RequestMappingHandlerAdapter类型的处理器适配器,代码如下:

  

protected HandlerAdapter getHandlerAdapter(Object handler) throws ServletException {

 

   if (this.handlerAdapters != null) {

   //循环判断哪个适配器能处理传入的处理器

   for (HandlerAdapter adapter : this.handlerAdapters) {

   if (adapter.supports(handler)) {

   return adapter;

   throw new ServletException("No adapter for handler [" + handler +

   "]: The DispatcherServlet configuration needs to include a HandlerAdapter that supports this handler");

  

 

  4.循环执行mappedHandler(HandlerExecutionChain拦截器链)对象的拦截器preHandle方法。

  5.处理器适配器调用处理方法,最终会执行ServletInvocableHandlerMethod.invokeAndHandle()方法,此方法中会调用invokeForRequest,invokeForRequest中会先拿到Controller中方法的具体参数值,再执行该方法,最后会返回ModelAndView对象。

  6.循环执行mappedHandler(HandlerExecutionChain拦截器链)对象的拦截器postHandle方法。

  7.processDispatchResult方法中会先查找找到具体的视图引擎,代码如下:

  

protected View resolveViewName(String viewName, @Nullable Map String, Object model,

 

   Locale locale, HttpServletRequest request) throws Exception {

   if (this.viewResolvers != null) {

   for (ViewResolver viewResolver : this.viewResolvers) {

   View view = viewResolver.resolveViewName(viewName, locale);

   if (view != null) {

   return view;

   return null;

  

 

  ​ 然后渲染视图内容,底层其实就是请求的转发,代码如下:

  

public void render(@Nullable Map String, ? model, HttpServletRequest request,

 

   HttpServletResponse response) throws Exception {

   if (logger.isDebugEnabled()) {

   logger.debug("View " + formatViewName() +

   ", model " + (model != null ? model : Collections.emptyMap()) +

   (this.staticAttributes.isEmpty() ? "" : ", static attributes " + this.staticAttributes));

   //合并返回结果,将 Model 中的静态数据和请求中的动态数据进行合并

   Map String, Object mergedModel = createMergedOutputModel(model, request, response);

   prepareResponse(request, response);

   //进行渲染

   renderMergedOutputModel(mergedModel, getRequestToExpose(request), response);

  

 

  8.循环执行mappedHandler(HandlerExecutionChain拦截器链)对象的拦截器afterCompletion方法。

  以上就是SpringMVC请求流程源码分析(springmvc请求处理)的详细内容,想要了解更多 SpringMVC请求流程源码分析的内容,请持续关注盛行IT软件开发工作室。

郑重声明:本文由网友发布,不代表盛行IT的观点,版权归原作者所有,仅为传播更多信息之目的,如有侵权请联系,我们将第一时间修改或删除,多谢。

留言与评论(共有 条评论)
   
验证码: