Spring MVC源码学习之DispatcherServlet初始化

Servlet生命周期(回顾)

1.servlet容器在收到第一个http请求之后会初始化对应的servlet调用init()方法

初始化的servlet是单例的,在web.xml在配置多少个servlet就会有多少个servlet实例

在web.xml中配置servlet时配置<load-on-startup>1</load-on-startup>会在容器启动 时初始化该servlet实例。默认值为 0 。

2. service()方法

servlet通过调用service() 处理客户端的请求

3.destory()方法

servlet 通过调用destory()方法终止。

最终调用该方法后会被标记为可回收,被虚拟机垃圾回收器回收。

DispatcherServlet 的初始化过程

该类是springmvc框架的前端控制器,分发处理客户端的请求。该类的与其他类的相关关系图如下图,HttpServletBean继承HttpServlet,并重写了init()方法。

1.HttpServletBean

从上图可以知道DispatcherServlet 的初始化是从HttpServletBean 的init()方式开始的。因为在使用Springmvc时会在web.xml 文件中配置<load-on-startup>1</load-on-startup> ,Servlet 容器会在启动的时候初始化该DispatcherServlet, 根据java 的类初始化过程,会调用父类的构造器,先初始化父类。以此类推。下面是HttpServletBean 的init()的实现:

public final void init() throws ServletException {
   if (logger.isDebugEnabled()) {
      logger.debug("Initializing servlet '" + getServletName() + "'");
   }

   // Set bean properties from init parameters.
   try {
      PropertyValues pvs = new ServletConfigPropertyValues(getServletConfig(), this.requiredProperties);
      BeanWrapper bw = PropertyAccessorFactory.forBeanPropertyAccess(this);
      ResourceLoader resourceLoader = new ServletContextResourceLoader(getServletContext());
      bw.registerCustomEditor(Resource.class, new ResourceEditor(resourceLoader, getEnvironment()));
      initBeanWrapper(bw);
      bw.setPropertyValues(pvs, true);
   }
   catch (BeansException ex) {
      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");
   }
}

从该方法中可以看出,调用了 initServletBean() 方法 。该注释上表示,该方法是让子类做任何他想做的任何初始化的事情。initServletBean() 方法是一个空的实现,由子类去实现

/**
 * Subclasses may override this to perform custom initialization.
 * All bean properties of this servlet will have been set before this
 * method is invoked.
 * <p>This default implementation is empty.
 * @throws ServletException if subclass initialization fails
 */
protected void initServletBean() throws ServletException {
}

方法的注释:子类也许会重写这个方法来实现只定义的初始化,在这个方法执行之前所有关于这个servlet的属性设置已经被完成。

2.FrameworkServlet

FrameworkServlet重写了initServletBean 方法:

/**
 * Overridden method of {@link HttpServletBean}, invoked after any bean properties
 * have been set. Creates this servlet's WebApplicationContext.
 */
@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 {
      this.webApplicationContext = initWebApplicationContext();
      initFrameworkServlet();
   }
   catch (ServletException ex) {
      this.logger.error("Context initialization failed", ex);
      throw ex;
   }
   catch (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()方法,看initWebApplicationContext()的实现

/**
 * Initialize and publish the WebApplicationContext for this servlet.
 * <p>Delegates to {@link #createWebApplicationContext} for actual creation
 * of the context. Can be overridden in subclasses.
 * @return the WebApplicationContext instance
 * @see #FrameworkServlet(WebApplicationContext)
 * @see #setContextClass
 * @see #setContextConfigLocation
 */
protected WebApplicationContext initWebApplicationContext() {
   WebApplicationContext rootContext =
         WebApplicationContextUtils.getWebApplicationContext(getServletContext());
   WebApplicationContext wac = null;

   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()) {
            // 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
               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();
   }
   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;
}

该方法的主要作用是初始化并发布该servlet 的应用上下文,然后调用onRefresh(wac)方法强制refresh上下文。onRefresh(ApplicationContext context)方法是一个空实现,由DispatcherServlet 去实现。如下:

/**
 * This implementation calls {@link #initStrategies}.
 */
@Override
protected void onRefresh(ApplicationContext context) {
   initStrategies(context);
}

该方法调用了initStrategies(ApplicationContext context) 如下:

/**
 * Initialize the strategy objects that this servlet uses.
 * <p>May be overridden in subclasses in order to initialize further strategy objects.
 */
protected void initStrategies(ApplicationContext context) {
   initMultipartResolver(context);
   initLocaleResolver(context);
   initThemeResolver(context);
   initHandlerMappings(context);
   initHandlerAdapters(context);
   initHandlerExceptionResolvers(context);
   initRequestToViewNameTranslator(context);
   initViewResolvers(context);
   initFlashMapManager(context);
}

最终会调用initStrategies()去初始化DispatcherServlet对象所需要用到的策略对象。

 

 

 

 

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值