# Spring 整合 Servlet

虽然在简单的 Servlet 去使用 Spring 并不常见,但是通过这个例子我们还是可以去见识一下 Spring 的作用。

  • Service、Dao 等单例对象的创建需要委托给 Spring IoC 容器;

  • Servlet 虽然也是单例对象,但是它是由 Servlet 容器(例如 tomcat)创建的,而不是由 Spring IoC 容器。

Spring 整合 Servlet,除了要引用 spring-cotext 之外,还要引用 spring-web注意,不是 spring-webmvc ,它是 spring mvc 的包。

<dependency> 
    <groupId>org.springframework</groupId>
    <artifactId>spring-context</artifactId>
    <version>${spring.version}</version> <!-- 5.1.17.RELEAS -->
</dependency>

<dependency> 
    <groupId>org.springframework</groupId>
    <artifactId>spring-web</artifactId>
    <version>${spring.version}</version> <!-- 5.1.17.RELEAS -->
</dependency>

整合的整体流程和原理:

  1. 利用 Servlet 的『监听』机制,我们向 tomcat 注册了一个由 spring-web 包所提供的 ContextLoaderListener 监听器。

  2. ContextLoaderListener 监听器会去监听 ServletContext 的创建与销毁。ServletContext 对象被 Servlet 容器创建出来了,后 ContextLaderListener 会去找 Java Web 的一个名为 contextConfigLocation 的“环境变量”,根据这个”环境变量“的值,去指定位置找配置文件,或配置类。

  3. Spring 根据找到的配置文件、或配置类去创建单例对象,放入 Spring IoC 容器中。

  4. ContextLoaderListener 会将 Spring IoC 容器『塞进』Java Web 项目的 applicationContext 中,作为它的一个 Attribute 。

  5. 我们可以在我们自己的 Servlet 中找到 Java Web 项目的 applicationContext,从中 getAttribute,拿到 Spring IoC 容器对象。

  6. 根据我们自己的需要,从 Spring IoC 容器中 getBean,随后执行业务逻辑代码。

  • 配置文件版的 web.xml

    <?xml version="1.0" encoding="UTF-8"?>
    <web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
          xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
          xsi:schemaLocation="
        http://xmlns.jcp.org/xml/ns/javaee
        http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"
          version="3.1">
    
      <display-name>Archetype Created Web Application</display-name>
    
      <!-- 确定配置文件位置:
           classpath: 简单来说,就是你的项目中的 resources 目录。-->
      <context-param>
         <param-name>contextConfigLocation</param-name>
         <param-value>classpath:application-context.xml</param-value>
      </context-param>
    
      <listener>
          <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
      </listener>
    
    </web-app>
    
  • 在 Servlet 中获得 Spring IoC 容器

    private ApplicationContext context;
    
    @Override
    public void init() throws ServletException {
        // 获取方式一
        // this.context = (ApplicationContext) this.getServletContext().getAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE);
    
        // 获取方式二
        this.context = WebApplicationContextUtils.getWebApplicationContext(this.getServletContext());
    
        // 在获得 Spring IoC 容器之后,再进一步获得容器中的单例的 Service 对象。
        this.departmentService = context.getBean(DepartmentService.class);
    }