欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

Spring源码分析之与WEB服务器衔接(下)

程序员文章站 2022-07-15 10:10:44
...

SpringMVC子容器的启动,也就是DispatcherServlet的初始化,它的父类HttpServletBean实现了servlet的初始化方法


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

获取配置文件配置的servlet初始配置,并设置到对应servlet的属性上,例如容器上下的配置文件路径


public ServletConfigPropertyValues(ServletConfig config, Set<String> requiredProperties)
      throws ServletException {

  Set<String> missingProps = (requiredProperties != null && !requiredProperties.isEmpty()) ?
      new HashSet<String>(requiredProperties) : null;

  Enumeration<String> en = config.getInitParameterNames();
  while (en.hasMoreElements()) {
    String property = en.nextElement();
    Object value = config.getInitParameter(property);
    addPropertyValue(new PropertyValue(property, value));
    if (missingProps != null) {
      missingProps.remove(property);
    }
  }

  // Fail if we are still missing properties.
  if (missingProps != null && missingProps.size() > 0) {
    throw new ServletException(
      "Initialization from ServletConfig for servlet '" + config.getServletName() +
      "' failed; the following required properties were missing: " +
      StringUtils.collectionToDelimitedString(missingProps, ", "));
  }
}

初始化servlet类


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

初始化web上下文容器,首先从servlet上下文中获取父容器


public static WebApplicationContext getWebApplicationContext(ServletContext sc) {
  return getWebApplicationContext(sc, WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE);
}

判断servlet的web容器是否存在


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

没有的话从servlet上下文中查找


protected WebApplicationContext findWebApplicationContext() {
  String attrName = getContextAttribute();
  if (attrName == null) {
    return null;
  }
  WebApplicationContext wac =
      WebApplicationContextUtils.getWebApplicationContext(getServletContext(), attrName);
  if (wac == null) {
    throw new IllegalStateException("No WebApplicationContext found: initializer not registered?");
  }
  return wac;
}

最后创建新的web上下文


protected WebApplicationContext createWebApplicationContext(WebApplicationContext parent) {
  return createWebApplicationContext((ApplicationContext) parent);
}

获取上下文类型,默认为XmlWebApplicationContext

private Class<?> contextClass = DEFAULT_CONTEXT_CLASS;
public Class<?> getContextClass() {
  return this.contextClass;
}

初始化上下文环境,设置父容器,设置配置路径

protected WebApplicationContext createWebApplicationContext(ApplicationContext parent) {
  Class<?> contextClass = getContextClass();
  if (this.logger.isDebugEnabled()) {
    this.logger.debug("Servlet with name '" + getServletName() +
        "' will try to create custom WebApplicationContext context of class '" +
        contextClass.getName() + "'" + ", using parent context [" + parent + "]");
  }
  if (!ConfigurableWebApplicationContext.class.isAssignableFrom(contextClass)) {
    throw new ApplicationContextException(
        "Fatal initialization error in servlet with name '" + getServletName() +
        "': custom WebApplicationContext class [" + contextClass.getName() +
        "] is not of type ConfigurableWebApplicationContext");
  }
  ConfigurableWebApplicationContext wac =
      (ConfigurableWebApplicationContext) BeanUtils.instantiateClass(contextClass);

  wac.setEnvironment(getEnvironment());
  wac.setParent(parent);
  wac.setConfigLocation(getContextConfigLocation());

  configureAndRefreshWebApplicationContext(wac);

  return wac;
}

配置id,servlet上下文配置,命名空间,添加上下文监听器,执行后置处理以及设置的上下文全局初始化器的初始化,最后启动上下文。


protected void configureAndRefreshWebApplicationContext(ConfigurableWebApplicationContext wac) {
  if (ObjectUtils.identityToString(wac).equals(wac.getId())) {
    // The application context id is still set to its original default value
    // -> assign a more useful id based on available information
    if (this.contextId != null) {
      wac.setId(this.contextId);
    }
    else {
      // Generate default id...
      wac.setId(ConfigurableWebApplicationContext.APPLICATION_CONTEXT_ID_PREFIX +
          ObjectUtils.getDisplayString(getServletContext().getContextPath()) + "/" + getServletName());
    }
  }

  wac.setServletContext(getServletContext());
  wac.setServletConfig(getServletConfig());
  wac.setNamespace(getNamespace());
  wac.addApplicationListener(new SourceFilteringListener(wac, new ContextRefreshListener()));

  // The wac environment's #initPropertySources will be called in any case when the context
  // is refreshed; do it eagerly here to ensure servlet property sources are in place for
  // use in any post-processing or initialization that occurs below prior to #refresh
  ConfigurableEnvironment env = wac.getEnvironment();
  if (env instanceof ConfigurableWebEnvironment) {
    ((ConfigurableWebEnvironment) env).initPropertySources(getServletContext(), getServletConfig());
  }

  postProcessWebApplicationContext(wac);
  applyInitializers(wac);
  wac.refresh();
}

当web容器启动完成后,发送刷新事件,设置接收标志

private class ContextRefreshListener implements ApplicationListener<ContextRefreshedEvent> {

  @Override
  public void onApplicationEvent(ContextRefreshedEvent event) {
    FrameworkServlet.this.onApplicationEvent(event);
  }
}

public void onApplicationEvent(ContextRefreshedEvent event) {
  this.refreshEventReceived = true;
  onRefresh(event.getApplicationContext());
}

判断是否收到web上下文初始化完成事件,往servlet上下文中添加该servlet对应的上下文信息

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 + "]");
  }
}

 

相关标签: Spring