0%

web应用使用spring

Spring 在 Web 应用中的集成:从原理到配置

在 Java Web 开发中,Spring 框架通过与 Servlet 容器的整合,实现了 IOC 容器的自动初始化和管理。与普通 Java 应用不同,Web 应用没有 main 方法,而是通过 Servlet 容器(如 Tomcat)启动,因此需要借助 Servlet 监听器在容器启动时初始化 Spring IOC 容器。本文将详细解析 Spring 与 Web 应用的整合原理及配置方式。

Web 应用中 Spring 的核心挑战

Web 应用的生命周期由 Servlet 容器(如 Tomcat)管理,其启动流程与普通 Java 应用截然不同:

  • 普通 Java 应用:通过 main 方法手动初始化 Spring 容器(如 new AnnotationConfigApplicationContext(...));
  • Web 应用:由 Servlet 容器启动,需在容器启动阶段自动创建 Spring IOC 容器,并在整个应用生命周期中共享。

核心需求:在 Servlet 容器启动时初始化 Spring 容器,并将其存储在全局作用域(ServletContext)中,供后续的 Servlet、Filter 等组件使用。

整合原理:基于 ServletContextListener

Servlet 规范提供了 ServletContextListener 接口,用于监听 ServletContext(Web 应用全局上下文)的创建和销毁。Spring 正是利用这一机制,在 ServletContext 初始化时(即 Web 应用启动时)创建 IOC 容器。

1. ServletContextListener 接口

ServletContextListener 包含两个核心方法,分别在 Web 应用启动和关闭时触发:

1
2
3
4
5
6
7
public interface ServletContextListener extends EventListener {
// 当 Web 应用启动,ServletContext 初始化时调用
void contextInitialized(ServletContextEvent sce);

// 当 Web 应用关闭,ServletContext 销毁时调用
void contextDestroyed(ServletContextEvent sce);
}
  • ServletContextEvent:事件对象,可通过 getServletContext() 方法获取 ServletContext 实例;
  • ServletContext:Web 应用的全局上下文,整个应用共享一个实例,可用于存储全局数据(如 Spring 容器)。

2. Spring 提供的监听器:ContextLoaderListener

Spring 框架提供了 ContextLoaderListener 类,它实现了 ServletContextListener 接口,是 Web 应用中初始化 Spring 容器的核心类。其工作流程如下:

  1. Web 应用启动:Servlet 容器调用 ContextLoaderListener.contextInitialized() 方法;
  2. 创建 Spring 容器ContextLoaderListener 读取配置文件(如 applicationContext.xml),初始化 WebApplicationContext(Spring 专为 Web 环境设计的 IOC 容器);
  3. 存储容器到 ServletContext:将 WebApplicationContext 实例存入 ServletContext,键为固定值 WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE
  4. Web 应用关闭:调用 contextDestroyed() 方法,销毁 Spring 容器,释放资源。

ContextLoaderListener 的核心逻辑(简化):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public class ContextLoaderListener extends ContextLoader implements ServletContextListener {
@Override
public void contextInitialized(ServletContextEvent event) {
// 初始化 WebApplicationContext
initWebApplicationContext(event.getServletContext());
}

@Override
public void contextDestroyed(ServletContextEvent event) {
// 销毁 WebApplicationContext
closeWebApplicationContext(event.getServletContext());
ContextCleanupListener.cleanupAttributes(event.getServletContext());
}
}

Web 应用整合 Spring 的配置步骤

1. 引入依赖(Maven)

需引入 Spring Web 模块(包含 ContextLoaderListener 等类)和 Servlet API:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<!-- Spring Web 核心依赖(含 ContextLoaderListener) -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<version>4.3.29.RELEASE</version>
</dependency>

<!-- Servlet API(需与容器版本匹配) -->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>3.1.0</version>
<scope>provided</scope> <!-- 由 Servlet 容器提供,打包时不包含 -->
</dependency>

2. 配置 Spring 监听器和配置文件路径

web.xml 中配置 ContextLoaderListener 并指定 Spring 配置文件的位置:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<?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">

<!-- 1. 配置 Spring 监听器:负责初始化 Spring 容器 -->
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>

<!-- 2. 指定 Spring 配置文件路径(可选,默认找 /WEB-INF/applicationContext.xml) -->
<context-param>
<param-name>contextConfigLocation</param-name>
<!-- 支持类路径(classpath:)或 WEB-INF 路径 -->
<param-value>classpath:applicationContext.xml</param-value>
</context-param>

</web-app>
  • 默认配置文件路径:若不配置 contextConfigLocation,Spring 会默认加载 /WEB-INF/applicationContext.xml
  • 多配置文件:可通过逗号分隔多个配置文件,如 classpath:spring-dao.xml,classpath:spring-service.xml

3. 创建 Spring 配置文件(applicationContext.xml)

src/main/resources 下创建 Spring 核心配置文件,定义 Bean 和组件扫描:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">

<!-- 组件扫描:自动注册 @Service、@Component 等注解的 Bean -->
<context:component-scan base-package="com.example.web"/>

<!-- 其他 Bean 定义(如数据源、事务管理器等) -->
<!-- <bean id="userService" class="com.example.web.service.UserService"/> -->

</beans>

4. 在 Web 组件中获取 Spring 容器

初始化后,Spring 容器存储在 ServletContext 中,可在 Servlet、Filter 等 Web 组件中通过 WebApplicationContextUtils 工具类获取:

示例:在 Servlet 中获取 Spring 容器并使用 Bean
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
import org.springframework.web.context.WebApplicationContext;
import org.springframework.web.context.support.WebApplicationContextUtils;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

@WebServlet("/user")
public class UserServlet extends HttpServlet {
private UserService userService;

@Override
public void init() throws ServletException {
// 1. 获取 ServletContext
ServletContext servletContext = getServletContext();

// 2. 通过 WebApplicationContextUtils 获取 Spring 容器
WebApplicationContext context = WebApplicationContextUtils.getWebApplicationContext(servletContext);

// 3. 从容器中获取 UserService Bean
userService = context.getBean(UserService.class);
}

@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException {
// 使用 userService 处理业务
String username = userService.getUsernameById(1L);
resp.getWriter().write("Username: " + username);
}
}
示例:Service 层 Bean(被 Spring 管理)
1
2
3
4
5
6
7
8
9
import org.springframework.stereotype.Service;

@Service
public class UserService {
public String getUsernameById(Long id) {
// 模拟查询数据库
return "张三";
}
}

Spring Web 容器的特殊之处:WebApplicationContext

在 Web 应用中,Spring 容器的类型是 WebApplicationContext,它是 ApplicationContext 的子接口,专为 Web 环境扩展了以下功能:

  1. 关联 ServletContext:通过 getServletContext() 方法直接获取 ServletContext
  2. 支持主题(Theme):管理 Web 应用的主题资源(如 CSS、图片);
  3. 分层容器:在 Spring MVC 中,WebApplicationContext可分为根容器(由ContextLoaderListener创建)和Servlet 容器(由DispatcherServlet创建),实现职责分离:
    • 根容器:管理 Service、Dao 等全局 Bean;
    • Servlet 容器:管理 Controller、ViewResolver 等 MVC 相关 Bean,且可引用根容器的 Bean。

无 web.xml 配置(Servlet 3.0+ 注解驱动)

Servlet 3.0 及以上支持完全注解驱动的配置,无需 web.xml,通过 WebApplicationInitializer 接口实现 Spring 容器的初始化:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
import org.springframework.web.WebApplicationInitializer;
import org.springframework.web.context.support.AnnotationConfigWebApplicationContext;
import org.springframework.web.servlet.DispatcherServlet;

import javax.servlet.ServletContext;
import javax.servlet.ServletRegistration;

// 实现 WebApplicationInitializer 接口,Servlet 容器启动时会自动调用其 onStartup 方法
public class MyWebAppInitializer implements WebApplicationInitializer {
@Override
public void onStartup(ServletContext servletContext) {
// 1. 初始化 Spring 根容器(基于注解配置)
AnnotationConfigWebApplicationContext rootContext = new AnnotationConfigWebApplicationContext();
rootContext.register(RootConfig.class); // 注册根配置类

// 2. 通过 ContextLoaderListener 启动根容器
servletContext.addListener(new ContextLoaderListener(rootContext));

// 3. (可选)配置 Spring MVC 的 DispatcherServlet(后续 MVC 整合时使用)
AnnotationConfigWebApplicationContext mvcContext = new AnnotationConfigWebApplicationContext();
mvcContext.register(MvcConfig.class); // 注册 MVC 配置类

ServletRegistration.Dynamic dispatcher = servletContext.addServlet(
"dispatcher", new DispatcherServlet(mvcContext)
);
dispatcher.setLoadOnStartup(1);
dispatcher.addMapping("/");
}
}

// 根配置类(对应 applicationContext.xml)
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;

@Configuration
@ComponentScan(basePackages = {"com.example.service", "com.example.dao"}) // 扫描 Service、Dao
public class RootConfig {
}

// MVC 配置类(后续讲解 Spring MVC 时详细说明)
@Configuration
@ComponentScan(basePackages = "com.example.controller") // 扫描 Controller
public class MvcConfig {
}

原理:Servlet 3.0+ 容器启动时,会扫描类路径下所有实现 WebApplicationInitializer 的类,并调用其 onStartup() 方法,从而实现无 XML 配置。

总结

Spring 与 Web 应用的整合核心是通过 ContextLoaderListener 监听器,在 Servlet 容器启动时初始化 WebApplicationContext,并将其存储在 ServletContext 中,供整个 Web 应用共享。关键步骤包括:

  1. 配置 ContextLoaderListener 监听器(web.xml 或注解方式);
  2. 指定 Spring 配置文件路径(contextConfigLocation 参数);
  3. 通过 WebApplicationContextUtils 在 Web 组件中获取 Spring 容器及 Bean

欢迎关注我的其它发布渠道

表情 | 预览
Powered By Valine
v1.3.10