返回介绍

11.3.2 DispatcherServlet 的初始化

发布于 2025-04-22 22:09:17 字数 5019 浏览 0 评论 0 收藏

通过上面的实例我们了解到,在 servlet 初始化阶段会调用其 init 方法,所以我们首先要查看在 DispatcherServlet 中是否重写了 init 方法。我们在其父类 HttpServletBean 中找到了该方法。

public final void init() throws ServletException {

  if (logger.isDebugEnabled()) {

   logger.debug("Initializing servlet '" + getServletName() + "'");

 }

  try {

  //解析 init-param 并封装只 pvs 中

   PropertyValues pvs = new ServletConfigPropertyValues(getServletConfig(),

  this.requiredProperties);

 //将当前的这个 Servlet 类转化为一个 BeanWrapper,从而能够以 Spring 的方式来对 init-param

 的值进行注

   BeanWrapper bw = PropertyAccessorFactory.forBeanPropertyAccess(this);

   ResourceLoader resourceLoader = new ServletContextResourceLoader

  (getServletContext());

  //注册自定义属性编辑器,一旦遇到 Resource 类型的属性将会使用 ResourceEditor 进行解析

  bw.registerCustomEditor(Resource.class, new ResourceEditor(resourceLoader,

 this.environment));

  //空实现,留给子类覆盖

  initBeanWrapper(bw);

 //属性注入

   bw.setPropertyValues(pvs, true);

 }

  catch (BeansException ex) {

   logger.error("Failed to set bean properties on servlet '" + getServletName()

   + "'", ex);

   throw ex;

 }

//留给子类扩展

 initServletBean();

  if (logger.isDebugEnabled()) {

   logger.debug("Servlet '" + getServletName() + "' configured successfully");

 }

}

DipatcherServlet 的初始化过程主要是通过将当前的 servlet 类型实例转换为 BeanWrapper 类型实例,以便使用 Spring 中提供的注入功能进行对应属性的注入。这些属性如 contextAttribute、contextClass、nameSpace、contextConfigLocation 等,都可以在 web.xml 文件中以初始化参数的方式配置在 servlet 的声明中。DispatcherServlet 继承自 FrameworkServlet,FrameworkServlet 类上包含对应的同名属性,Spring 会保证这些参数被注入到对应的值中。属性注入主要包含以下几个步骤。

1.封装及验证初始化参数

ServletConfigPropertyValues 除了封装属性外还有对属性验证的功能。

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

throws ServletException {

  Set<String> missingProps = (requiredProperties != null && !requiredProperties.

  isEmpty()) ?

  new HashSet<String>(requiredProperties) : null;

  Enumeration en = config.getInitParameterNames();

  while (en.hasMoreElements()) {

   String property = (String) 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.getServlet

    Name() +

    "' failed; the following required properties were missing: " +

    StringUtils.collectionToDelimitedString(missingProps, ", "));

 }

}

从代码中得知,封装属性主要是对初始化的参数进行封装,也就是 servlet 中配置的<init-param>中配置的封装。当然,用户可以通过对 requiredProperties 参数的初始化来强制验证某些属性的必要性,这样,在属性封装的过程中,一旦检测到 requiredProperties 中的属性没有指定初始值,就会抛出异常。

2.将当前 servlet 实例转化成 BeanWrapper 实例

PropertyAccessorFactory.forBeanPropertyAccess 是 Spring 中提供的工具方法,主要用于将指定实例转化为 Spring 中可以处理的 BeanWrapper 类型的实例。

3.注册相对于 Resource 的属性编辑器

属性编辑器,我们在上文中已经介绍并且分析过其原理,这里使用属性编辑器的目的是在对当前实例( DispatcherServlet )属性注入过程中一旦遇到 Resource 类型的属性就会使用 ResourceEditor 去解析。

4.属性注入

BeanWrapper 为 Spring 中的方法,支持 Spring 的自动注入。其实我们最常用的属性注入无非是 contextAttribute、contextClass、nameSpace、contextConfigLocation 等属性。

5.servletBean 的初始化

在 ContextLoaderListener 加载的时候已经创建了 WebApplicationContext 实例,而在这个函数中最重要的就是对这个实例进行进一步的补充初始化。

继续查看 initServletBean()。父类 FrameworkServlet 覆盖了 HttpServletBean 中的 initServlet Bean 函数,如下:

protected final void initServletBean() throws ServletException {

  getServletContext().log("Initializing Spring FrameworkServlet '" + getServlet

  Name() + "'");

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

 }

}

上面的函数设计了计时器来统计初始化的执行时间,而且提供了一个扩展方法 initFrameworkServlet() 用于子类的覆盖操作,而作为关键的初始化逻辑实现委托给了 initWebApplicationContext()。

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。
列表为空,暂无数据
    我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。