4.3 web.xml 与 context.xml 配置详解 本节摘要:每个 Web 应用有两份登记表——web.xml 是部署描述符,向容器声明应用有哪些组件、按什么规则响应;context.xml 是上下文配置,向容器申请运行资源与专属行为。本节精读两份文件的核心元素、注解配置与传统描述符的优先级关系、以及一个应用从部署到卸载的完整生命周期状态机,并解释 reloadable 参数的真实行为。 住户入住的最后一步是登记。4.2 造应用骨架时 web.xml 只写了欢迎页,本节把它写全;context.xml 里那个数据源定义也在此讲透。这两份文件是应用与容器的契约,第 5 章的 Realm、JNDI 与第 6 章的过滤器配置全部落在这两份表上。 两份登记表各管一摊 先分工。web.
本节摘要:每个 Web 应用有两份登记表——web.xml 是部署描述符,向容器声明应用有哪些组件、按什么规则响应;context.xml 是上下文配置,向容器申请运行资源与专属行为。本节精读两份文件的核心元素、注解配置与传统描述符的优先级关系、以及一个应用从部署到卸载的完整生命周期状态机,并解释 reloadable 参数的真实行为。
住户入住的最后一步是登记。4.2 造应用骨架时 web.xml 只写了欢迎页,本节把它写全;context.xml 里那个数据源定义也在此讲透。这两份文件是应用与容器的契约,第 5 章的 Realm、JNDI 与第 6 章的过滤器配置全部落在这两份表上。
先分工。web.xml 回答"应用是什么":有哪些 Servlet 与过滤器、欢迎页与错误页是谁、会话多久超时、安全性约束怎么设。context.xml 回答"应用要什么":数据源、环境变量、专属阀门、跨上下文访问开关。一份声明行为,一份申请资源——记住这个分工,配置就不会放错文件。
web.xml 的元素有严格顺序要求(Servlet 规范定义了顺序),核心一段长这样。
<?xml version="1.0" encoding="UTF-8"?> <web-app xmlns="https://jakarta.ee/xml/ns/jakartaee" version="6.0"> <!-- 会话默认三十分钟过期 --> <session-config> <session-timeout>30</session-timeout> </session-config> <!-- 声明一个 Servlet 并配初始化参数 --> <servlet> <servlet-name>orderQuery</servlet-name> <servlet-class>demo.OrderQueryServlet</servlet-class> <init-param> <param-name>pageSize</param-name> <param-value>20</param-value> </init-param> <load-on-startup>1</load-on-startup> </servlet> <servlet-mapping> <servlet-name>orderQuery</servlet-name> <url-pattern>/order/query</url-pattern> </servlet-mapping> <!-- 过滤器与映射 第6章主角 此处先登记 --> <filter> <filter-name>encoding</filter-name> <filter-class>demo.EncodingFilter</filter-class> </filter> <filter-mapping> <filter-name>encoding</filter-name> <url-pattern>/*</url-pattern> </filter-mapping> <!-- 欢迎页与错误页 --> <welcome-file-list> <welcome-file>index.html</welcome-file> </welcome-file-list> <error-page> <error-code>404</error-code> <location>/404.html</location> </error-page> </web-app>
五个区块覆盖九成日常需求:会话、Servlet 声明与映射、过滤器、欢迎页、错误页。load-on-startup 值越小越早初始化,配 1 的含义是"启动时立即实例化而不是等第一个请求"——首请求慢的优化手段之一。
注解是描述符的轻量替身。同样一个 Servlet,注解三行搞定。
// 注解配置:与上面的 XML 声明等价 import jakarta.servlet.annotation.WebServlet; @WebServlet(name = "orderQuery", urlPatterns = "/order/query", initParams = @WebInitParam(name = "pageSize", value = "20")) public class OrderQueryServlet extends jakarta.servlet.http.HttpServlet { // 业务代码省略 }
两种写法优先级规则要记牢:同名冲突时描述符赢。web.xml 里出现了 servlet-name 相同的声明,注解即被忽略(web-app 的 metadata-complete 属性设为 true 则全部注解失效)。团队协作里常见策略是业务组件用注解、全局行为(安全约束、错误页)用描述符,各取所长。
context.xml 的招牌能力是资源定义——JNDI 数据源是最常见的一种,5.3 会讲应用侧怎么取用,这里先把定义端写规范。
<!-- 应用的 META-INF/context.xml 或 conf 目录同名片段 --> <Context reloadable="false"> <!-- 全局唯一的数据源名字 类型必须是容器实现类 --> <Resource name="jdbc/orderdb" auth="Container" type="javax.sql.DataSource" driverClassName="org.postgresql.Driver" url="jdbc:postgresql://db-internal:5432/orders" username="order_app" password="从环境变量或密钥管理读取" maxTotal="50" maxIdle="10" maxWaitMillis="8000"/> <!-- 应用专属环境项 相当于容器托管的配置常量 --> <Environment name="feature/quickPay" value="on" type="java.lang.String" override="false"/> </Context>
Resource 一段八个属性,三个最要紧:name 是应用查找用的 JNDI 名字,全局唯一;maxTotal 是连接池上限,它应当与 3.2 的线程数算术联动——50 个连接配 200 个线程,高峰期会有 150 个线程在等连接,池子大小要按下游容量反推;maxWaitMillis 拿不到连接时最多等八秒然后抛异常,宁可快速失败也不要无限排队,这是故障隔离的基本功。
reloadable 这个属性值得单独辟谣。它的字面意思是"WEB-INF 里的类或描述符变化时自动重载应用",看似方便,实现却是整个 Context 丢弃重建:会话序列化、类加载器换新,开销大且伴生内存碎片。这个开关只适合本机开发,生产一律 false——热更新走 4.2 的受控发布流程,而不是靠它。
登记完成,看应用从生到死的状态流转:部署中、启动中、运行、停止、卸载。部署中阶段容器读两份登记表、扫描注解、建立类加载器;启动中阶段依次触发 5.3 会讲的监听器事件、初始化过滤器与启用了 load-on-startup 的 Servlet;运行阶段对外服务,期间可能因管理端指令进入停止态(类还在,不接请求);卸载是彻底拆除,会话销毁、监听器收到销毁事件。
用 manager 的文本接口可以把这个状态机"开盖"看一遍。
# 列出全部应用的状态 再停掉再启动一个应用 curl -u deploy:secret http://localhost:8080/manager/text/list curl -u deploy:secret http://localhost:8080/manager/text/stop?path=/order curl -u deploy:secret http://localhost:8080/manager/text/start?path=/order
OK - Listed applications for virtual host [localhost] /order:running:0:demo order app OK - Stopped application at context path [/order] OK - Started application at context path [/order]
三条输出对应状态机的三个迁移。理解状态机的实际收益在排障:日志里一个应用反复出现在部署记录里,说明有什么东西在触发重载——reloadable 开着、发布脚本在循环拷包、或 autoDeploy 误判更新,三种原因对应三种处置,第 7 章的部署故障一节会拿真实日志对号入座。
⚠️ 常见坑:把 web.xml 的顺序不当回事。元数据完整但元素顺序错误时,部分版本会直接拒绝启动,报错信息是元素名而非"顺序错误",新手极易被带偏。抄模板时保持元素顺序不动,只改值。
应用登记完毕,请求马上要穿过管道上的关卡——第 5 章讲 Valve、Realm、Listener、JNDI 这四位"路上的执法者与后勤官"。