• SpringMVC之DispatchServlet初始化过程


      SpringMvc最核心的类就是前端控制器DispatchServlet,作为一个Servlet,是整个SpringMvc的入口,用于调度其他的各组件工作,如Controller、HandlerMapping、ViewResolver等,控制着整个处理用户请求的流程,本篇首先来总结一下DispatchServlet的初始化过程,及进行具体处理请求前的预准备

    作为一个Servlet的主要继承关系:HttpServletBean -- > FrameworkServlet -- > DispatchServlet,分别分析这三个类,各自所做的工作,就基本上总结了整个DispatchServlet的初始过程

    1、HttpServletBean

    重写了GenericServlet的init方法,servelt实例化时init方法会被调用:

        @Override
    	public final void init() throws ServletException {
    		if (logger.isDebugEnabled()) {
    			logger.debug("Initializing servlet '" + getServletName() + "'");
    		}
    
    		//将Servlet的初始化参数(initParam)保存在PropertyValues,并检查Servlet的初始化参数包含了所必须的参数,子类可以将在this.requiredProperties指定必须的参数
    		PropertyValues pvs = new ServletConfigPropertyValues(getServletConfig(), this.requiredProperties);
    		if (!pvs.isEmpty()) {
    			try {
                          //BeanWrapper就是对象的包裹者,主要用于对象的属性设置,这里的this,就是DispatchServlet
    				BeanWrapper bw = PropertyAccessorFactory.forBeanPropertyAccess(this);
                          //ServletContext的资源加载器,使用本质上是使用ServletContext对象来获取与当前Servlet相关的资源
    				ResourceLoader resourceLoader = new ServletContextResourceLoader(getServletContext());
                          //自定义了属性编辑器,用于编辑被包裹对象的属性
    				bw.registerCustomEditor(Resource.class, new ResourceEditor(resourceLoader, getEnvironment()));
                           //空方法留给子类自定义初始化BeanWrapper
    				initBeanWrapper(bw);
                  	        //将Servlet的初始化参数设置到BeanWrapper中
    				bw.setPropertyValues(pvs, true);
    			}
    			catch (BeansException ex) {
    				if (logger.isErrorEnabled()) {
    					logger.error("Failed to set bean properties on servlet '" + getServletName() + "'", ex);
    				}
    				throw ex;
    			}
    		}
    
    		//在FrameworkServlet中实现
    		initServletBean();
    
    		if (logger.isDebugEnabled()) {
    			logger.debug("Servlet '" + getServletName() + "' configured successfully");
    		}
    	}
    

    总结:主要使用ServletConfig配置信息初始化DispatchServlet的一些属性,如使用web.xml时配置的contextConfigLocation

    2、FrameworkServlet

    1)重写initServletBean()初始化IOC容器

    调用链:HttpServletBean.init()-->FrameworkServlet.initServletBean()

        @Override
    	protected final void initServletBean() throws ServletException {
    		getServletContext().log("Initializing Spring FrameworkServlet '" + getServletName() + "'");
    		if (this.logger.isInfoEnabled()) {
    			this.logger.info("FrameworkServlet '" + getServletName() + "': initialization started");
    		}
    		long startTime = System.currentTimeMillis();
    
    		try {
                      //初始化ioc容器(设置父子容器并刷新)
    			this.webApplicationContext = initWebApplicationContext();
                      //空方法,留给子类扩展
    			initFrameworkServlet();
    		}
    		...
    	}
    

    核心方法initWebApplicationContext():

    protected WebApplicationContext initWebApplicationContext() {
      		//根据ServletContext.getAttribute来获取到WebApplicationContext(根容器),而获取到的根容器是之前由ContextLoaderListener(ServletContextListener)的contextInitialized方法将根容器保存在了ServletContext
    		WebApplicationContext rootContext =
    				WebApplicationContextUtils.getWebApplicationContext(getServletContext());
    		WebApplicationContext wac = null;
      		//this.webApplicationContext为子容器(保存SpringMVC组件),如果我们使用的是配置类的方式即继承AbstractAnnotationConfigDispatcherServletInitializer来指定创建父子容器,那么在Servlet容器启动的时侯webApplicationContext就被创建了
    		if (this.webApplicationContext != null) {     
    			wac = this.webApplicationContext;
    			if (wac instanceof ConfigurableWebApplicationContext) {
    				ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext) wac;
    				if (!cwac.isActive()) {
    					if (cwac.getParent() == null) {
                         	          //将根容器设置为子容器(保存SpringMVC组件)的父容器
    						cwac.setParent(rootContext);
    					}
                                    //配置并且刷新容器(启动初始化容器过程),之前的父子容器只是被创建没有调用refresh
    					configureAndRefreshWebApplicationContext(cwac);
    				}
    			}
    		}
      		
    		if (wac == null) {
                     //如果子容器还没又被创建,尝试去ServletContext中以获取
    			wac = findWebApplicationContext();
    		}
    		if (wac == null) {
                     //如果子容器还为空,就通过web.xml配置的参数contextConfigLocation指定的Xml配置文件路径来创建一个XmlWebApplicationContext类型的ioc子容器,设置父子容器关系,并刷新。
    			wac = createWebApplicationContext(rootContext);
    		}
    		
    		if (!this.refreshEventReceived) {
                     //在DispatchServlet中实现
    			onRefresh(wac);
    		}
    		//this.publishContext指定是否将web容器发布在ServletContext中,默认为ture
    		if (this.publishContext) {
    			String attrName = getServletContextAttributeName();
              	    //将初始化好的ioc容器放入ServletContext中
    			getServletContext().setAttribute(attrName, wac);
    			if (this.logger.isDebugEnabled()) {
    				this.logger.debug("Published WebApplicationContext of servlet '" + getServletName() +
    						"' as ServletContext attribute with name [" + attrName + "]");
    			}
    		}
    
    		return wac;
    	}
    

    FrameworkServlet的initServletBean方法主要就是初始化IOC容器,包括一个子容器(保存springmvc组件,如Controller、ViewResolver、HandlerMapping等等)和一个父容器(保存业务逻辑组件,如service,dao),

    2)重写了HttpServelt的service方法
    @Override
    protected void service(HttpServletRequest request, HttpServletResponse response)
    		throws ServletException, IOException {
    
    	HttpMethod httpMethod = HttpMethod.resolve(request.getMethod());
    	if (HttpMethod.PATCH == httpMethod || httpMethod == null) {
          	    //处理PATCH请求
    		processRequest(request, response);
    	}
    	else {
          	    //如果不是PATCH请求,调用HttpServelt.service()
    		super.service(request, response);
    	}
    }
    

    FrameworkServlet也重写了doGet、doPost、xxx等对应处理各类型请求的方法,最终都是调用了processRequest(request, response)来处理:

    protected final void processRequest(HttpServletRequest request, HttpServletResponse response)
          throws ServletException, IOException {
    
       long startTime = System.currentTimeMillis();
       Throwable failureCause = null;
       //获取之前LocaleContext(主要作用是封装请求的 Locale 信息,主要就是语言信息)默认不存在
       LocaleContext previousLocaleContext = LocaleContextHolder.getLocaleContext();
      //创建本次请求的localeContext
       LocaleContext localeContext = buildLocaleContext(request);
    	//获取之前RequestAttributes(封装request,response)默认不存在
       RequestAttributes previousAttributes = RequestContextHolder.getRequestAttributes();
      	//创建本次请求的requestAttributes
       ServletRequestAttributes requestAttributes = buildRequestAttributes(request, response, previousAttributes);
    	//与异步请求相关的处理
       WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
       asyncManager.registerCallableInterceptor(FrameworkServlet.class.getName(), new RequestBindingInterceptor());
    	//将localeContext、requestAttributes分别保存在LocaleContextHolder、RequestContextHolder中,两者都是使用ThreadLocal与当前线程绑定
       initContextHolders(request, localeContext, requestAttributes);
    
       try {
         //真正的处理请求过程在DispatchServlet中实现
          doService(request, response);
       }
       catch (ServletException ex) {
          failureCause = ex;
          throw ex;
       }
       catch (IOException ex) {
          failureCause = ex;
          throw ex;
       }
       catch (Throwable ex) {
          failureCause = ex;
          throw new NestedServletException("Request processing failed", ex);
       }
    
       finally {
         //完成请求默认移除requestAttributes和localeContext
          resetContextHolders(request, previousLocaleContext, previousAttributes);
          if (requestAttributes != null) {
             requestAttributes.requestCompleted();
          }
    
          if (logger.isDebugEnabled()) {
             if (failureCause != null) {
                this.logger.debug("Could not complete request", failureCause);
             }
             else {
                if (asyncManager.isConcurrentHandlingStarted()) {
                   logger.debug("Leaving response open for concurrent processing");
                }
                else {
                   this.logger.debug("Successfully completed request");
                }
             }
          }
    	  //无论请求是否成功都会发布请求处理完成事件(我们可以向容器中添加相应的事件监听器)
          publishRequestHandledEvent(request, response, startTime, failureCause);
       }
    }
    

    总结:1、在Servlet初始化阶段,初始化了IOC容器

               2、在处理请求阶段,做了一些提前的准备工作

    3、DispatcherServlet

    1)重写onRefresh,初始化SpringMvc工作组件

    HttpServletBean.init()-->FrameworkServlet.initServletBean()-->FrameworkServlet.initWebApplicationContext()-->DispatcherServlet.onRefresh(ApplicationContext context)

    @Override
    protected void onRefresh(ApplicationContext context) {
      //context就是在FrameworkServlet.initWebApplicationContext()中完成初始化工作的IOC容器
       initStrategies(context);
    }
    //完成各组件的初始化
    protected void initStrategies(ApplicationContext context) {
      //初始化文件上传的处理类 initMultipartResolver(context); initLocaleResolver(context); initThemeResolver(context); initHandlerMappings(context); initHandlerAdapters(context); initHandlerExceptionResolvers(context); initRequestToViewNameTranslator(context); initViewResolvers(context); initFlashMapManager(context); }

    这里我们以处理器映射器的初始化initHandlerMappings(context)为例分析,其他组件的初始化处理也类似

    private void initHandlerMappings(ApplicationContext context) {
    		this.handlerMappings = null;
      		//this.detectAllHandlerMappings默认为true
    		if (this.detectAllHandlerMappings) {
    			//在容器中找到所有的HandlerMapping
    			Map<String, HandlerMapping> matchingBeans =
    					BeanFactoryUtils.beansOfTypeIncludingAncestors(context, HandlerMapping.class, true, false);
    			if (!matchingBeans.isEmpty()) {
                  //将所有的HandlerMapping保存在handlerMappings中
    				this.handlerMappings = new ArrayList<HandlerMapping>(matchingBeans.values());
    				// 排序
    				AnnotationAwareOrderComparator.sort(this.handlerMappings);
    			}
    		}
    		else {
    			try {
    				HandlerMapping hm = context.getBean(HANDLER_MAPPING_BEAN_NAME, HandlerMapping.class);
    				this.handlerMappings = Collections.singletonList(hm);
    			}
    			catch (NoSuchBeanDefinitionException ex) {
    				// Ignore, we'll add a default HandlerMapping later.
    			}
    		}
    
    		//如果在IOC容器中没有找到任何的HandlerMapping,获取默认的HandlerMapping
    		if (this.handlerMappings == null) {
    			this.handlerMappings = getDefaultStrategies(context, HandlerMapping.class);
    			if (logger.isDebugEnabled()) {
    				logger.debug("No HandlerMappings found in servlet '" + getServletName() + "': using default");
    			}
    		}
    	}
    

    getDefaultStrategies(context, HandlerMapping.class)获取默认的组件:

    protected <T> List<T> getDefaultStrategies(ApplicationContext context, Class<T> strategyInterface) {
      //这的key为org.springframework.web.servlet.HandlerMapping
       String key = strategyInterface.getName();
      //在defaultStrategies获取我们需要创建的组件的类型,多个的话,使用逗号隔开
       String value = defaultStrategies.getProperty(key);
       if (value != null) {
         //使用逗号为分隔符,转化成数组
          String[] classNames = StringUtils.commaDelimitedListToStringArray(value);
          List<T> strategies = new ArrayList<T>(classNames.length);
         //遍历classNames利用反射创建对象
          for (String className : classNames) {
             try {
                Class<?> clazz = ClassUtils.forName(className, DispatcherServlet.class.getClassLoader());
               //创建并添加到IOC容器中
                Object strategy = createDefaultStrategy(context, clazz);
                strategies.add((T) strategy);
             }
             catch (ClassNotFoundException ex) {
                throw new BeanInitializationException(
                      "Could not find DispatcherServlet's default strategy class [" + className +
                            "] for interface [" + key + "]", ex);
             }
             catch (LinkageError err) {
                throw new BeanInitializationException(
                      "Error loading DispatcherServlet's default strategy class [" + className +
                            "] for interface [" + key + "]: problem with class file or dependent class", err);
             }
          }
          return strategies;
       }
       else {
          return new LinkedList<T>();
       }
    }
    
    //保存了各组件接口对应的默认实现类
    private static final Properties defaultStrategies;
    //静态代码块,用于初始化defaultStrategies
    static {
       try {
         //private static final String DEFAULT_STRATEGIES_PATH = "DispatcherServlet.properties",创建了一个与DispatcherServlet.class处于同一包下的DispatcherServlet.properties文件的资源对象
          ClassPathResource resource = new ClassPathResource(DEFAULT_STRATEGIES_PATH, DispatcherServlet.class);
          //创建Properties对象,保存了组件接口的对应的默认实现类
          defaultStrategies = PropertiesLoaderUtils.loadProperties(resource);
       }
       catch (IOException ex) {
          throw new IllegalStateException("Could not load '" + DEFAULT_STRATEGIES_PATH + "': " + ex.getMessage());
       }
    }
    

    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.annotation.DefaultAnnotationHandlerMapping
    
    org.springframework.web.servlet.HandlerAdapter=org.springframework.web.servlet.mvc.HttpRequestHandlerAdapter,
       org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter,
       org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter
    
    org.springframework.web.servlet.HandlerExceptionResolver=org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerExceptionResolver,
       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
    

    各个SpringMvc工作组件的初始化过程为:首先从IOC容器中找对应接口类型的组件,如果没到,就创建一个在DispatcherServlet.properties中指定的默认组件接口实现类的实例

    2)重写doService,进入核心方法doDispatch(request, response)
    @Override
    protected void doService(HttpServletRequest request, HttpServletResponse response) throws Exception {
       if (logger.isDebugEnabled()) {
          String resumed = WebAsyncUtils.getAsyncManager(request).hasConcurrentResult() ? " resumed" : "";
          logger.debug("DispatcherServlet with name '" + getServletName() + "'" + resumed +
                " processing " + request.getMethod() + " request for [" + getRequestUri(request) + "]");
       }
    
     	//这里与RequestDispatcher.include()相关
       Map<String, Object> attributesSnapshot = null;
       if (WebUtils.isIncludeRequest(request)) {
          attributesSnapshot = new HashMap<String, Object>();
          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中
       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());
    
       FlashMap inputFlashMap = this.flashMapManager.retrieveAndUpdate(request, response);
       if (inputFlashMap != null) {
          request.setAttribute(INPUT_FLASH_MAP_ATTRIBUTE, Collections.unmodifiableMap(inputFlashMap));
       }
       request.setAttribute(OUTPUT_FLASH_MAP_ATTRIBUTE, new FlashMap());
       request.setAttribute(FLASH_MAP_MANAGER_ATTRIBUTE, this.flashMapManager);
    
       try {
         //最终调用了
          doDispatch(request, response);
       }
       finally {
          if (!WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted()) {
             // Restore the original attribute snapshot, in case of an include.
             if (attributesSnapshot != null) {
                restoreAttributesAfterInclude(request, attributesSnapshot);
             }
          }
       }
    }
    

    最终进入doDispatch(request, response),作为DispatchServlet最核心方法,调度着各工作组件进行具体的请求处理。

  • 相关阅读:
    EasyRMS录播管理服务器项目实战:windows上开机自启动NodeJS服务
    EasyPlayer RTSP Windows(with ActiveX/OCX插件)播放器支持H.265播放与抓图功能
    EasyPlayer RTSP Windows(with ActiveX/OCX插件)播放器支持H.265播放与抓图功能
    将EasyRTMP_RTSP移植到Android平台实现的RTSP拉流转推RTMP直播流功能
    将EasyRTMP_RTSP移植到Android平台实现的RTSP拉流转推RTMP直播流功能
    EasyDarwin如何支持点播和RTMP/HLS直播?EasyDSS!
    EasyDarwin如何支持点播和RTMP/HLS直播?EasyDSS!
    EasyCMS在幼儿园视频直播项目实战中以redis操作池的方式应对高并发的redis操作问题
    EasyCMS在幼儿园视频直播项目实战中以redis操作池的方式应对高并发的redis操作问题
    EasyDarwin开源流媒体服务器中一种实现对作用域内new对象自动释放的方法(值得借鉴)
  • 原文地址:https://www.cnblogs.com/qzlcl/p/11141020.html
Copyright © 2020-2023  润新知