Spring Framework源码解析——ServletConfigAware
版权声明
- 本文原创作者:谷哥的小弟
- 作者博客地址:http://blog.csdn.net/lfdfhl
一、概述
ServletConfigAware
是 Spring 框架提供的一个 回调接口(Callback Interface),用于在 Bean 初始化过程中自动注入 javax.servlet.ServletConfig
对象。作为 Spring Aware 接口体系的重要组成部分,它使容器管理的 Bean 能够感知并访问当前 Servlet 的配置信息。
与 ServletContextAware
不同,ServletConfigAware
的作用范围更窄——它仅适用于与特定 Servlet 实例关联的上下文环境,通常在 Spring MVC 的 DispatcherServlet
子容器中生效。其设计目标是让 Bean 能够安全、规范地获取该 Servlet 的初始化参数(<init-param>
),从而实现更细粒度的配置定制。
二、接口定义与定位
package org.springframework.web.context;import javax.servlet.ServletConfig;
import org.springframework.beans.factory.Aware;public interface ServletConfigAware extends Aware {void setServletConfig(ServletConfig servletConfig);
}
- 继承自
org.springframework.beans.factory.Aware
:表明这是一个 Spring 感知接口; - 唯一方法
setServletConfig(...)
:由 Spring 容器在 Bean 初始化阶段调用,传入当前 Servlet 的ServletConfig
实例。
关键特性:
- 仅在 Web 环境中有效;
- 仅在与特定 Servlet 关联的
WebApplicationContext
中生效(如DispatcherServlet
创建的子容器);- 由容器自动调用,开发者无需手动触发。
三、工作机制:Aware 机制与 ApplicationContextAwareProcessor
与 ServletContextAware
类似,ServletConfigAware
的注入也由 ApplicationContextAwareProcessor
这一 BeanPostProcessor
统一处理。
3.1 核心调用链
- Bean 实例化完成;
- 属性填充完成后,进入
postProcessBeforeInitialization
阶段; ApplicationContextAwareProcessor
检测 Bean 是否实现ServletConfigAware
;- 若是,则从当前
ApplicationContext
中获取ServletConfig
并注入。
3.2 ApplicationContextAwareProcessor
相关源码
// org.springframework.context.support.ApplicationContextAwareProcessor@Override
@Nullable
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {// ... 其他 Aware 判断if (bean instanceof ServletConfigAware) {ServletConfig sc = this.applicationContext instanceof WebApplicationContext ?((WebApplicationContext) this.applicationContext).getServletConfig() : null;if (sc != null) {((ServletConfigAware) bean).setServletConfig(sc); // ← 注入}}return bean;
}
关键点:
- 仅当
applicationContext
是WebApplicationContext
且其getServletConfig()
返回非null
时,才执行注入;- 根容器(由
ContextLoaderListener
创建)通常不持有ServletConfig
,因此ServletConfigAware
在根容器中无效;- 子容器(由
DispatcherServlet
创建)持有ServletConfig
,因此在此类容器中有效。
四、ServletConfig
的来源:FrameworkServlet
的角色
在 Spring Web MVC 中,DispatcherServlet
继承自 FrameworkServlet
,而 FrameworkServlet
负责在 Servlet 初始化时创建子 WebApplicationContext
并设置 ServletConfig
。
// org.springframework.web.servlet.FrameworkServlet@Override
protected final void initServletBean() throws ServletException {// ...try {this.webApplicationContext = initWebApplicationContext();initFrameworkServlet();}// ...
}protected WebApplicationContext initWebApplicationContext() {// 获取根上下文(由 ContextLoaderListener 创建)WebApplicationContext rootContext =WebApplicationContextUtils.getWebApplicationContext(getServletContext());WebApplicationContext wac = null;if (this.webApplicationContext != null) {wac = this.webApplicationContext;if (wac instanceof ConfigurableWebApplicationContext) {ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext) wac;if (!cwac.isActive()) {if (cwac.getParent() == null) {cwac.setParent(rootContext);}configureAndRefreshWebApplicationContext(cwac);}}}else {wac = createWebApplicationContext(rootContext);}// 关键:将当前 Servlet 的 ServletConfig 设置到子上下文中onRefresh(wac);if (this.publishContext) {String attrName = getServletContextAttributeName();getServletContext().setAttribute(attrName, wac);}return wac;
}// FrameworkServlet.configureAndRefreshWebApplicationContext(...)
protected void configureAndRefreshWebApplicationContext(ConfigurableWebApplicationContext wac) {// ...wac.setServletConfig(getServletConfig()); // ← 设置 ServletConfigwac.setServletContext(getServletContext());// ...
}
结论:
DispatcherServlet
在初始化时,会调用wac.setServletConfig(getServletConfig())
;- 因此,其创建的子
WebApplicationContext
实现了getServletConfig()
返回有效实例;- 该子容器中的 Bean 实现
ServletConfigAware
即可获得注入。
五、使用场景与典型示例
在 web.xml
中为 DispatcherServlet
配置参数:
<servlet><servlet-name>dispatcher</servlet-name><servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class><init-param><param-name>api.version</param-name><param-value>v2</param-value></init-param><load-on-startup>1</load-on-startup>
</servlet>
在 Controller 或 Service 中获取:
@Controller
public class ApiController implements ServletConfigAware {private String apiVersion;@Overridepublic void setServletConfig(ServletConfig config) {this.apiVersion = config.getInitParameter("api.version");}@GetMapping("/version")public String getVersion() {return "API Version: " + apiVersion;}
}
注意:该 Controller 必须定义在
dispatcher-servlet.xml
(或对应 Java 配置)中,不能定义在根容器(如applicationContext.xml
)中,否则setServletConfig
不会被调用。
六、与 ServletContextAware
的对比
特性 | ServletContextAware | ServletConfigAware |
---|---|---|
作用范围 | 整个 Web 应用(全局) | 单个 Servlet 实例 |
配置来源 | <context-param> | <init-param> |
有效容器 | 根容器 + 子容器 | 仅子容器(如 DispatcherServlet 容器) |
WebApplicationContext 方法 | getServletContext() | getServletConfig() |
典型用途 | 全局路径、共享资源 | Servlet 特定配置(如 API 版本、编码) |
架构意义:
ServletContext
是 应用级上下文;ServletConfig
是 组件级上下文;- Spring 通过两个 Aware 接口分别支持两种粒度的配置注入。
七、生命周期与调用时机
ServletConfigAware.setServletConfig()
的调用时机与所有 Aware 接口一致:
Bean 实例化↓
属性填充(@Autowired、@Value 等)↓
Aware 接口回调(包括 ServletConfigAware)↓
@PostConstruct / InitializingBean.afterPropertiesSet()↓
Bean 可用
重要约束:
- 此时其他 Bean 可能尚未初始化,不应调用其业务方法;
- 仅用于保存
ServletConfig
引用或读取初始化参数。
八、设计思想与架构意义
8.1 精细化配置支持
- 允许同一 Web 应用中部署多个
DispatcherServlet
,每个使用不同的<init-param>
; - 对应的子容器中的 Bean 可自动感知所属 Servlet 的配置;
- 支持多版本 API、多租户等高级场景。
8.2 与 Servlet 规范对齐
- 尊重 Java EE/Servlet 规范的设计(
ServletConfig
是标准接口); - 无需引入 Spring 特有概念,降低学习成本。
8.3 解耦与可测试性
- 相比于硬编码
getServletConfig()
,通过接口注入更易于单元测试(可 mock); - 符合依赖倒置原则(DIP)。
九、注意事项与最佳实践
9.1 注意事项
- 仅在子容器中生效:若 Bean 定义在根容器(如
ContextLoaderListener
加载的配置),setServletConfig
不会被调用; - 不要在根容器中实现该接口:会导致
ServletConfig
为null
,可能引发 NPE; - 避免存储非线程安全资源:
ServletConfig
本身是线程安全的,但从中获取的对象需谨慎处理; - 现代替代方案:在 Spring Boot 中,更推荐使用
@Value
或@ConfigurationProperties
。
9.2 Spring Boot 中的替代方案
在基于注解或 Spring Boot 的应用中,通常不再使用 web.xml
,而是通过配置类或属性文件管理:
@RestController
public class MyController {@Value("${my.servlet.api.version:v1}")private String apiVersion;
}
前提:需将 Servlet 初始化参数映射到 Spring
Environment
(Spring Boot 自动完成内嵌容器的参数绑定)。
十、总结
ServletConfigAware
是 Spring Web 集成体系中一个精巧而实用的扩展点,它通过标准的回调机制,将 Servlet 规范中的 ServletConfig
对象安全、规范地注入到 Spring 管理的 Bean 中。其生效依赖于 FrameworkServlet
对子 WebApplicationContext
的正确配置,并由 ApplicationContextAwareProcessor
在 Bean 生命周期的恰当时机完成注入。
尽管在现代 Spring Boot 应用中,直接实现 ServletConfigAware
的场景逐渐减少,但其背后的设计思想——支持组件级配置感知、尊重 Servlet 规范、实现精细化容器隔离——仍然是 Spring Web 架构的重要基石。理解该接口的实现原理,有助于深入掌握 Spring MVC 的容器层次结构、Aware 机制以及 Web 集成的底层逻辑。
ServletConfigAware
用于注入当前 Servlet 的ServletConfig
;- 仅在
DispatcherServlet
等创建的子容器中生效; - 由
ApplicationContextAwareProcessor
在postProcessBeforeInitialization
阶段调用; - 依赖
FrameworkServlet
在初始化时调用wac.setServletConfig(...)
; - 适用于读取
<init-param>
,不适用于根容器 Bean; - 现代应用中可被
@Value
或配置属性替代,但原理相通。