Spring MVC源码——Servlet WebApplicationContext

在使用Spring MVC进行Web开发时,Servlet WebApplicationContext 是一个核心概念。它是Spring MVC应用中管理Bean的容器,负责在Web环境下加载和管理Spring的Bean。理解 Servlet WebApplicationContext 的源码有助于我们更深入地掌握Spring MVC的工作原理,优化我们的应用程序。本文将深入剖析 Servlet WebApplicationContext 的源码,带你了解其内部机制。

目录#

  1. 什么是Servlet WebApplicationContext
  2. 源码结构和继承关系
  3. 初始化过程
  4. 核心方法解析
  5. 常见实践和最佳实践
  6. 示例用法
  7. 总结
  8. 参考资料

1. 什么是Servlet WebApplicationContext#

Servlet WebApplicationContext 是Spring框架为Web应用专门设计的 ApplicationContext 实现。它继承自 WebApplicationContext,在普通 ApplicationContext 的基础上,增加了对Web环境的支持。Servlet WebApplicationContext 可以与Servlet容器集成,在Servlet上下文中管理Spring的Bean,使得Spring的Bean可以在Web应用中方便地使用。

2. 源码结构和继承关系#

继承关系#

Servlet WebApplicationContext 继承自 WebApplicationContext,而 WebApplicationContext 又继承自 ApplicationContext。以下是简化的继承关系图:

ApplicationContext
    └── WebApplicationContext
        └── ServletWebApplicationContext

主要实现类#

ServletWebApplicationContext 有多个实现类,其中最常用的是 XmlWebApplicationContextAnnotationConfigWebApplicationContext

  • XmlWebApplicationContext:通过XML配置文件来加载Bean定义。
  • AnnotationConfigWebApplicationContext:通过Java注解来加载Bean定义。

3. 初始化过程#

Servlet WebApplicationContext 的初始化过程通常在Servlet容器启动时进行。以下是初始化的主要步骤:

3.1 配置上下文参数#

web.xml 中配置 ContextLoaderListenercontextConfigLocation 参数,指定Spring配置文件的位置。

<context-param>
    <param-name>contextConfigLocation</param-name>
    <param-value>/WEB-INF/applicationContext.xml</param-value>
</context-param>
 
<listener>
    <listener-class>org.springframework.web.context.ContextLoaderListener</listener>
</listener>

3.2 启动 ContextLoaderListener#

ContextLoaderListener 是一个Servlet监听器,在Servlet容器启动时会触发 contextInitialized 方法。

public class ContextLoaderListener extends ContextLoader implements ServletContextListener {
 
    @Override
    public void contextInitialized(ServletContextEvent event) {
        initWebApplicationContext(event.getServletContext());
    }
 
    @Override
    public void contextDestroyed(ServletContextEvent event) {
        closeWebApplicationContext(event.getServletContext());
    }
}

3.3 创建 Servlet WebApplicationContext#

initWebApplicationContext 方法中,会创建 Servlet WebApplicationContext 实例,并加载配置文件。

public WebApplicationContext initWebApplicationContext(ServletContext servletContext) {
    if (servletContext.getAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE) != null) {
        throw new IllegalStateException("Cannot initialize context because there is already a root application context present - " +
                "check whether you have multiple ContextLoader* definitions in your web.xml!");
    }
 
    WebApplicationContext rootContext = createWebApplicationContext(servletContext);
    servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, rootContext);
 
    return rootContext;
}

4. 核心方法解析#

4.1 getServletContext()#

该方法用于获取当前的 ServletContext 对象。

public ServletContext getServletContext() {
    return this.servletContext;
}

4.2 getServletConfig()#

该方法用于获取当前的 ServletConfig 对象。

public ServletConfig getServletConfig() {
    return this.servletConfig;
}

4.3 getNamespace()#

该方法用于获取当前 Servlet WebApplicationContext 的命名空间。

public String getNamespace() {
    return this.namespace;
}

5. 常见实践和最佳实践#

5.1 配置文件管理#

  • 使用 contextConfigLocation 参数指定多个配置文件,将不同功能的配置分开管理。
<context-param>
    <param-name>contextConfigLocation</param-name>
    <param-value>/WEB-INF/applicationContext.xml,/WEB-INF/securityContext.xml</param-value>
</context-param>

5.2 使用注解配置#

推荐使用 AnnotationConfigWebApplicationContext 代替 XmlWebApplicationContext,通过Java注解来配置Bean。

public class AppInitializer implements WebApplicationInitializer {
 
    @Override
    public void onStartup(ServletContext servletContext) throws ServletException {
        AnnotationConfigWebApplicationContext context = new AnnotationConfigWebApplicationContext();
        context.register(AppConfig.class);
 
        ServletRegistration.Dynamic dispatcher = servletContext.addServlet("dispatcher", new DispatcherServlet(context));
        dispatcher.setLoadOnStartup(1);
        dispatcher.addMapping("/");
    }
}

6. 示例用法#

以下是一个简单的Spring MVC应用示例,使用 AnnotationConfigWebApplicationContext 配置。

6.1 创建配置类#

@Configuration
@ComponentScan(basePackages = "com.example")
public class AppConfig {
    // 配置Bean
}

6.2 创建控制器#

@Controller
public class HelloController {
 
    @RequestMapping("/hello")
    @ResponseBody
    public String hello() {
        return "Hello, Spring MVC!";
    }
}

6.3 启动应用#

public class AppInitializer implements WebApplicationInitializer {
 
    @Override
    public void onStartup(ServletContext servletContext) throws ServletException {
        AnnotationConfigWebApplicationContext context = new AnnotationConfigWebApplicationContext();
        context.register(AppConfig.class);
 
        ServletRegistration.Dynamic dispatcher = servletContext.addServlet("dispatcher", new DispatcherServlet(context));
        dispatcher.setLoadOnStartup(1);
        dispatcher.addMapping("/");
    }
}

7. 总结#

通过对 Servlet WebApplicationContext 源码的分析,我们了解了它在Spring MVC应用中的重要作用和工作原理。Servlet WebApplicationContext 作为Spring MVC的核心容器,负责加载和管理Bean,与Servlet容器紧密集成。在实际开发中,我们可以根据不同的需求选择合适的配置方式,优化应用程序的性能和可维护性。

8. 参考资料#