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 应用启动和关闭时触发:
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 容器的核心类。其工作流程如下:
- Web 应用启动:Servlet 容器调用
ContextLoaderListener.contextInitialized()方法; - 创建 Spring 容器:
ContextLoaderListener读取配置文件(如applicationContext.xml),初始化WebApplicationContext(Spring 专为 Web 环境设计的 IOC 容器); - 存储容器到 ServletContext:将
WebApplicationContext实例存入ServletContext,键为固定值WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE; - Web 应用关闭:调用
contextDestroyed()方法,销毁 Spring 容器,释放资源。
ContextLoaderListener 的核心逻辑(简化):
public class ContextLoaderListener extends ContextLoader implements ServletContextListener {
public void contextInitialized(ServletContextEvent event) {
// 初始化 WebApplicationContext
initWebApplicationContext(event.getServletContext());
}
public void contextDestroyed(ServletContextEvent event) {
// 销毁 WebApplicationContext
closeWebApplicationContext(event.getServletContext());
ContextCleanupListener.cleanupAttributes(event.getServletContext());
}
}
Web 应用整合 Spring 的配置步骤
1. 引入依赖(Maven)
需引入 Spring Web 模块(包含 ContextLoaderListener 等类)和 Servlet API:
<!-- 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 配置文件的位置:
<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 和组件扫描:
<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
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;
public class UserServlet extends HttpServlet {
private UserService userService;
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);
}
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException {
// 使用 userService 处理业务
String username = userService.getUsernameById(1L);
resp.getWriter().write("Username: " + username);
}
}
示例:Service 层 Bean(被 Spring 管理)
import org.springframework.stereotype.Service;
public class UserService {
public String getUsernameById(Long id) {
// 模拟查询数据库
return "张三";
}
}
Spring Web 容器的特殊之处:WebApplicationContext
在 Web 应用中,Spring 容器的类型是 WebApplicationContext,它是 ApplicationContext 的子接口,专为 Web 环境扩展了以下功能:
- 关联 ServletContext:通过
getServletContext()方法直接获取ServletContext; - 支持主题(Theme):管理 Web 应用的主题资源(如 CSS、图片);
- 分层容器:在 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 容器的初始化:
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 {
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;
// 扫描 Service、Dao
public class RootConfig {
}
// MVC 配置类(后续讲解 Spring MVC 时详细说明)
// 扫描 Controller
public class MvcConfig {
}
原理:Servlet 3.0+ 容器启动时,会扫描类路径下所有实现 WebApplicationInitializer 的类,并调用其 onStartup() 方法,从而实现无 XML 配置。
总结
Spring 与 Web 应用的整合核心是通过 ContextLoaderListener 监听器,在 Servlet 容器启动时初始化 WebApplicationContext,并将其存储在 ServletContext 中,供整个 Web 应用共享。关键步骤包括:
- 配置
ContextLoaderListener监听器(web.xml或注解方式); - 指定 Spring 配置文件路径(
contextConfigLocation参数); - 通过
WebApplicationContextUtils在 Web 组件中获取 Spring 容器及 Bean