DispatcherServlet初始化

本文探讨了Spring MVC的核心组件DispatcherServlet的初始化过程。从Servlet的生命周期出发,详细阐述了DispatcherServlet如何通过HttpServletBean的init()方法进行参数初始化,然后转化为Spring管理的bean,利用依赖注入设置参数。接着讲解了FrameworkServlet的initServletBean()方法,该方法初始化DispatcherServlet的应用上下文,并创建或查找ApplicationContext。最后,文章提到了DispatcherServlet的onRefresh()方法,用于初始化Spring MVC的各个组件。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

DispatcherServlet的初始化过程

​ Spring MVC源码的核心就是DispatcherServlet前端控制器,从客户端传过来的所有请求都需要通过DispatcherServlet来进行分发、处理,然后把响应结果返回给客户端。DispatcherServlet的本质就是一个Servlet,和Servlet有着同样的生命周期,初始化->运行->销毁,接下来我看看DispatcherServlet的初始化过程是什么样子的。

  • DispatcherServlet的继承关系

DispatcherServletDispatcherServlet的初始化过程图

**DispatcherServlet的初始化工作并没有在DispatcherServlet这个类里面进行初始化,而是在HttpServletBean这个类里面重写了init()方法。下面是HttpServletBean#init()**方法

public final void init() throws ServletException {
   if (logger.isDebugEnabled()) {
      logger.debug("Initializing servlet '" + getServletName() + "'");
   }
	/**
	 *加载web.xml中的init-param进行初始化参数
	 *ServletConfigPropertyValues是HttpServletBean的一个内部类,
	 *用于解析web.xml定义中<servlet>元素的子元素<init-param>中的参数值
	 */
   // Set bean properties from init parameters.
   PropertyValues pvs = new ServletConfigPropertyValues(getServletConfig(), this.requiredProperties);
   if (!pvs.isEmpty()) {
      try {
         //将Servlet转化成BeanWrapper,便于值的注入
         BeanWrapper bw = PropertyAccessorFactory.forBeanPropertyAccess(this);
         ResourceLoader resourceLoader = new ServletContextResourceLoader(getServletContext());
         bw.registerCustomEditor(Resource.class, new ResourceEditor(resourceLoader, getEnvironment()));
         //模板方法
         initBeanWrapper(bw);
         //把初始配置设置给DispatcherServlet
         bw.setPropertyValues(pvs, true);
      }
      catch (BeansException ex) {
         if (logger.isErrorEnabled()) {
            logger.error("Failed to set bean properties on servlet '" + getServletName() + "'", ex);
         }
         throw ex;
      }
   }

   // Let subclasses do whatever initialization they like.
   initServletBean();

   if (logger.isDebugEnabled()) {
      logger.debug("Servlet '" + getServletName() + "' configured successfully");
   }
}

1、init()方法通过实例化**ServletConfigPropertyValues**对Servlet进行参数初始化。

2、参数初始化之后,会把DispatcherServlet转化为**BeanWrapper**,当做是一个bean去管理,这样可以通过Spring的依赖注入对参数的值进行注入

​ ----->bw.setPropertyValues(pvs, true); 通过set进行注入

3、initServletBean()方法作为模板方法在子类中实现,在HttpServletBean中并没有具体实现。

  • 接下来看下initServletBean()在**FrameworkServlet**类中的重写实现
    **FrameworkServlet#initServletBean()主要是对web的上下文进行初始化,ContextLoaderListener加载的时候已经创建了WebApplicationContext**实例,这里是对这个实例的进一步补充初始化。
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 {
      //初始化WebApplicationContext
      this.webApplicationContext = initWebApplicationContext();
     //模板方法,在子类中实现 
     initFrameworkServlet();
   }
   catch (ServletException | RuntimeException ex) {
      this.logger.error("Context initialization failed", ex);
      throw ex;
   }

   if (this.logger.isInfoEnabled()) {
      long elapsedTime = System.currentTimeMillis() - startTime;
      this.logger.info("FrameworkServlet '" + getServletName() + "': initialization completed in " +
            elapsedTime + " ms");
   }
}

​ 看看initWebApplicationContext()有哪些操作,该方法主要负责初始化DispatcherServlet自己的应用上下文,首先会查看**FrameworkServlet在构造时候,是否会传入一个WebApplicationContext,如果不会就会查找,仍然没有查到,则会自己创建一个WebApplicationContext,最后会对WebApplicationContext*进行onRefresh()操作完成应用上下文的创建,onRefresh()*依然是个模板方法,让子类实现。

protected WebApplicationContext initWebApplicationContext() {
  //获取rootContext,如果在web.xml中配置了ContextLoaderListener,那么这个就是rootContext
   WebApplicationContext rootContext =
         WebApplicationContextUtils.getWebApplicationContext(getServletContext());
   WebApplicationContext wac = null;
		//如果FrameworkServlet的构造方法传入了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;
         if (!cwac.isActive()) {
           //如果context还没有被refresh,需要设置父context或者设置应用上下文的id等
            // 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
               //在没有明确的父context情况下,注入rootContext的实例
               cwac.setParent(rootContext);
            }
            configureAndRefreshWebApplicationContext(cwac);
         }
      }
   }
   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();
   }
   //最终都没有的话,就自己创建一个WebApplicationContext
   if (wac == null) {
      // No context instance is defined for this servlet -> create a local one
      wac = createWebApplicationContext(rootContext);
   }
   if (!this.refreshEventReceived) {
      // Either the context is not a ConfigurableApplicationContext with refresh
      // support or the context injected at construction time had already been
      // refreshed -> trigger initial onRefresh manually here.
     //模板方法,让子类实现
      onRefresh(wac);
   }
   if (this.publishContext) {
      // Publish the context as a servlet context attribute.
      String attrName = getServletContextAttributeName();
      getServletContext().setAttribute(attrName, wac);
      if (this.logger.isDebugEnabled()) {
         this.logger.debug("Published WebApplicationContext of servlet '" + getServletName() +
               "' as ServletContext attribute with name [" + attrName + "]");
      }
   }
   return wac;
}

​ 接下来我们看看DispatcherServlet具体实现了子类的onRefresh()方法,DispatcherServlet中的onRefresh()方法主要是为了初始化Spring MVC中各个组件。

protected void onRefresh(ApplicationContext context) {
   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);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值