资讯动态

Servlet配置详解:web.xml与@WebServlet注解对比

发布时间:2026/10/5 14:18:23 来源:尧图企业网站定制
很多刚接触Java Web的同学都遇到过同一个尴尬Servlet的代码写出来了doGet、doPost方法里也准备好了输出结果一启动Tomcat浏览器敲地址直接404。这时候大多数人第一反应是“代码写错了”但真正的问题往往出在另一个环节——Servlet写代码只是第一步让容器认识它、把URL映射到它身上才是决定“能不能访问”的关键。这件事在技术上有个正经名字叫Servlet配置。配置Servlet有两种主流方式一种是传统web.xml文件配置一种是Servlet 3.0之后Java官方推出的WebServlet注解配置。这篇内容就是为正处于这个阶段的零基础初学者准备的我会把两种方式从项目结构、完整写法、底层逻辑到踩坑现场都过一遍读完你不仅能配通还能知道你自己到底在配什么。1. 两种配置方式先看清楚解决什么问题1.1 Servlet为什么需要“配置”这件事Servlet本质上就是一个符合规范的Java类它继承自HttpServlet重写了doGet、doPost这些方法。但这个类跟普通Java类有个本质区别普通类是你自己new出来的而Servlet是被Tomcat这样的Servlet容器创建和管理的。Tomcat怎么知道你写了一个Servlet怎么知道哪个URL应该进入哪个Servlet的doGet这些信息得有人告诉它这就是“配置”。类比一下就是你开了一家店Servlet是店里的员工Tomcat是商场浏览器是顾客。顾客什么时候来、去谁的柜台商场必须有一个“柜台索引”才能把人分流过去。没有索引顾客来了只能前台瞎转悠最后败兴而归表现到Web里就是404。web.xml和WebServlet注解就是这份“柜台索引”的两种写法。1.2 两种方式的前世今生与适用人群web.xml是Servlet规范里最传统的配置方式。从Servlet 2.x时代开始所有Java Web项目都靠这个文件完成注册、映射、参数初始化、过滤器顺序声明等工作。它的优势是集中管理所有Servlet的映射关系在一个文件里一览无余而且支持灵活的动态配置比如用一个通配符/api/*批量捕获请求路径。WebServlet注解则是Servlet 3.0规范引入的新方案对应Java EE 6规范Tomcat 7及以上版本都能用。它的优势是开发效率高Servlet类和映射关系写在一起代码可读性好不用来回切换文件。这也是目前大多数新项目、教学项目、Spring Boot内置容器支持Spring MVC时采用的思路。对零基础初学者来说我的建议是两种都得学。web.xml能帮你理解Java Web的底层运行逻辑注解是日常开发的主流选择。面试的时候这两张牌都是高频考点而且面试官特别喜欢追问“它们能不能混用”“优先级怎么算”这些坑我后面会专门讲。2. 方式一web.xml手动登记2.1 web.xml在项目里的位置和标准骨架先解决一个最基础的问题web.xml到底放在哪。对一个标准Maven结构的Java Web项目来说web.xml的路径是src/main/webapp/WEB-INF/web.xml注意WEB-INF是Web应用里比较特殊的目录外面浏览器直接访问不到WEB-INF下的资源只有容器内部能读取。web.xml作为配置中枢放这里天然防外部篡改。对旧式IDE动态Web项目比如直接用IDEA创建Web项目选“Web Application”web.xml通常在web/WEB-INF/web.xml或者直接在项目根目录的web目录下。如果你用的是Maven默认就是src/main/webapp/WEB-INF/web.xml。新建项目时IDEA会生成一个空模板web.xml内容大概是这样的?xml version1.0 encodingUTF-8? web-app xmlnshttp://xmlns.jcp.org/xml/ns/javaee xmlns:xsihttp://www.w3.org/2001/XMLSchema-instance xsi:schemaLocationhttp://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd version3.1 /web-app这个骨架里的xmlns、xsi:schemaLocation、version三项是有讲究的它告诉Tomcat你遵循的是哪个版本的Servlet规范。比如上面写的3.1意味着你用的Servlet API版本是3.1支持注解、异步处理等特性。如果把这个版本换成2.5那就代表回到老规范很多新特性会被受限。初学者在这个地方最容易犯的错是模板里缺了这些namespace声明直接把网上老帖子的web.xml复制过来用结果报Schema错误或者启动失败。2.2 注册Servlet的完整配置写法web.xml配置一个Servlet需要写两个标签块servlet负责声明servlet-mapping负责映射。声明和映射是解耦的中间通过servlet-name关联这是web.xml配置的核心思想。来看一个最简单的HelloServlet配置?xml version1.0 encodingUTF-8? web-app xmlnshttp://xmlns.jcp.org/xml/ns/javaee xmlns:xsihttp://www.w3.org/2001/XMLSchema-instance xsi:schemaLocationhttp://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd version3.1 servlet servlet-namehelloServlet/servlet-name servlet-classcom.example.demo.HelloServlet/servlet-class load-on-startup1/load-on-startup /servlet servlet-mapping servlet-namehelloServlet/servlet-name url-pattern/hello/url-pattern /servlet-mapping /web-app对应Java端的Servlet类是这样的package com.example.demo; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; import java.io.PrintWriter; public class HelloServlet extends HttpServlet { Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { resp.setContentType(text/html;charsetUTF-8); PrintWriter out resp.getWriter(); out.println(h2Hello Servlet, web.xml配置方式/h2); } }这里面有几个关键点值得展开。第一servlet-name相当于这个Servlet的别名它不需要跟类名一致但必须保证servlet和servlet-mapping两处写的一模一样Tomcat靠这个名字串起整个映射链。实际项目里常见一眼假错误就是这里一个叫hello另一个叫helloServlet启动时虽然不报错但浏览器访问时Tomcat根本找不到对应的映射。第二servlet-class必须是全限定类名也就是包名加类名完整路径。Tomcat底层用Class.forName加载它写错一个字母或者漏了包名启动时会直接报ClassNotFoundException。第三url-pattern必须以/开头这是Servlet规范硬性要求。写成hello启动时会报java.lang.IllegalArgumentException: Invalid url-pattern容器根本起不来。2.3 web.xml方式部署与启动实测配置完成后整个项目结构大概是这样src/main/java/com/example/demo/HelloServlet.java src/main/webapp/WEB-INF/web.xml用Maven打包成war包命令是mvn clean package打包完成后把target目录下的war包复制到Tomcat的webapps目录启动Tomcat浏览器访问http://localhost:8080/项目名/hello比如war包叫web-demo.war访问地址就是http://localhost:8080/web-demo/hello为什么URL中间要带项目名因为war包部署到Tomcat后默认会以war包文件名作为上下文路径。Tomcat会为每个Web应用分配一个“上下文根”看作一个独立的站点空间。如果只用IDEA直接配合Tomcat运行上下文路径可以在IDEA的“Run/Debug Configurations”里设置Application context默认也可以设为/这样URL就变成http://localhost:8080/hello。部署后启动Tomcat控制台如果看到类似这样的日志说明配置文件被正常解析了INFO: Deploying web application archive ...然后浏览器能看到页面上打出“Hello Servlet, web.xml配置方式”说明整个链路已经通了。2.4 写web.xml最容易踩的三个坑第一个坑是servlet-name不匹配。声明和映射两个地方的name必须一字不差我的建议是直接复制粘贴不要手敲尤其不要出现大小写不一致。第二个坑是配置了Servlet但忘记servlet-mapping。这时候启动不报错但访问任何URL都找不到这个Servlet。因为servlet只完成了“注册”没有完成“绑定地址”Tomcat虽然知道有这个类但不知道它服务于哪个URL。初学者排查问题的时候习惯性只盯着servlet看很容易漏掉后面这个配置块。第三个坑是把url-pattern配成了/hello/*这种通配形式之后同时又在Servlet代码里直接用req.getParameter(id)取参数。通配符路由会捕获多级路径比如/hello/1、/hello/2/user都能匹配到参数获取方式和精确匹配的路径解析习惯不一样容易混淆。关于匹配规则我后面会在第5章结合运行机制一起讲清楚。3. 方式二WebServlet注解3.1 注解配置的最小可运行代码用注解配置Servlet是Servlet 3.0之后官方主推的“零XML”路线。代码层面简洁到让人感动直接在Servlet类上面加一个WebServlet注解写上映射路径就完事了package com.example.demo; import javax.servlet.annotation.WebServlet; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; import java.io.PrintWriter; WebServlet(urlPatterns /hello) public class HelloServlet extends HttpServlet { Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException { resp.setContentType(text/html;charsetUTF-8); PrintWriter out resp.getWriter(); out.println(h2Hello Servlet, 注解配置方式/h2); } }配置完成后如果项目里有web.xml注意web.xml的根元素版本需要是3.1代表Servlet 3.1规范Tomcat才会开启注解扫描。如果用Tomcat 7以下的老版本注解方式是不被支持的。这也是新手最容易忽略的环境前置条件。启动测试时访问地址和web.xml方式一致http://localhost:8080/项目名/hello页面输出对应内容说明注解配置已经生效。3.2 注解常用属性与多路径映射WebServlet注解里还有几个可以配的属性实际项目中经常用到。给大家列一个核心属性表属性类型作用默认值urlPatternsString[]指定一个或多个URL访问路径空数组valueString[]等同于urlPatterns两者取其一空数组nameStringServlet注册名称相当于web.xml里的servlet-name默认为类名loadOnStartupint指定容器启动时是否初始化Servlet-1即首次请求时初始化displayNameString界面显示名称此属性在没有页面的后端服务里很少用到类名initParamsWebInitParam[]初始化参数相当于web.xml里的init-param空数组来看一个实际多个映射路径和初始化参数的例子package com.example.demo; import javax.servlet.ServletConfig; import javax.servlet.ServletException; import javax.servlet.annotation.WebInitParam; import javax.servlet.annotation.WebServlet; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; import java.io.PrintWriter; WebServlet( name orderServlet, urlPatterns {/order, /order/list}, loadOnStartup 1, initParams { WebInitParam(name charset, value UTF-8), WebInitParam(name timeout, value 30) } ) public class OrderServlet extends HttpServlet { private String charset; Override public void init() throws ServletException { ServletConfig config getServletConfig(); charset config.getInitParameter(charset); } Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException { resp.setContentType(text/html;charset charset); PrintWriter out resp.getWriter(); out.println(h2order servlet, charset charset /h2); } }注意urlPatterns和value两个属性不能同时写否则会报错。它们在功能上是等价的都用来指定映射路径按照注解规范开发者只能选择其中一个使用。日常写法我更推荐显式写urlPatterns可读性更好不会让人误解成“给这个Servlet传了个什么参数”。loadOnStartup这个属性值得单独说一下。默认情况下Servlet实例是“懒加载”的也就是第一次请求到达时才创建实例并调用init方法。如果你在init方法里做了比较耗时的初始化操作比如加载配置、建立数据库连接池第一次请求就会特别慢用户感觉就像卡住了。设置loadOnStartup为正整数比如1Tomcat启动时就会预先初始化这个Servlet把耗时操作提前做掉。数字越小的优先级越高容器启动时按递增顺序加载。这是优化首次访问体验的一个很实用的小技巧。3.3 注解背后的扫描机制与生效前提很多初学者只知道加注解能用但不太清楚Tomcat是怎么找到这个注解的。这里有个关键的词叫“注解扫描”。Tomcat启动时会根据Web应用的/WEB-INF/classes目录和/WEB-INF/lib下的jar包扫描所有符合字节码规范的文件检查它们有没有标注WebServlet注解。一旦发现就动态注册为Servlet。正因为有扫描机制就有几个注意事项。第一项目里如果web.xml存在它的version必须设置为3.0及以上否则Tomcat默认不开启注解扫描。第二扫描范围过大可能拖慢启动速度尤其lib里有一堆大型jar包时能明显感觉到Tomcat启动变慢。优化手段是把不参与扫描的jar包放到容器共享目录或者使用metadata-completetrue属性告诉容器“web.xml已经把所有信息声明完整不用再扫描注解”这种做法会让注解配置直接失效适合完全依赖XML的老项目。第三同一个Servlet类上如果同时配置了web.xml和注解指向不同的路径结果不是“两个都能访问”而是造成Web应用启动失败或者行为不确定。这一点我强烈建议在实际项目中避免。你问我为什么看下一章我专门分析这两套机制之间的博弈关系。4. 两种方式的横向对比与选择建议4.1 核心维度对照表把两种配置方式放在同一张表里对比比干讲原理直观得多对比维度web.xml配置WebServlet注解配置配置位置集中一个XML文件分散在各Servlet类上可读性查看所有Servlet映射方便与Servlet代码同文件局部可读性好修改成本改配置需重启Web应用改代码重新编译部署同样要重启动态映射支持支持通配符可配合Filter统一声明也支持/api/*通配符但无法在运行期临时改映射初始化参数通过init-param配置通过WebInitParam配置与Spring/Shiro等框架整合老框架不少依赖XML方式新框架Spring Boot直接整理成自动配置适用阶段适合学习底层原理、维护老项目日常开发效率高适合新项目Servlet版本要求任意版本Servlet 3.0Tomcat 7这个表的结论比较明显新的开发场景确实注解更占优势但不代表web.xml一文不值。4.2 真实项目里的选型逻辑实际工作中怎么选我给几组场景参考。第一类你维护的是比较传统的Spring项目或者直接用Servlet/JSP的老系统。这类项目的web.xml里可能已经注册了几十个Servlet和Filter还有一堆context-param全局参数。这种情况下最佳策略是“保持原有方式新增模块优先看团队技术规范”。如果新写一个Servlet继续在web.xml里注册保证配置集中管理如果团队约定新模块统一用注解那就在类上加上WebServlet同时确保旧配置没受影响。第二类你用Spring Boot开发新服务。Spring Boot的核心思想是“约定大于配置零XML”平时连web.xml这种文件都不用创建。Servlet在Spring Boot里通常被封进可执行jar包直接通过Component加自定义ServletRegistrationBean注册或者使用内置容器自动管理。这时候如果你非要在src/main/webapp下建一个WEB-INF/web.xml反而可能干扰自动配置非常不推荐。第三类你准备参加面试或者做学习项目。建议把两种方式都实际写一遍并且在纸上画出“请求路径 → 容器匹配 → Servlet实例”的完整过程。面试官问到两种方式区别的时候先说web.xml是声明式集中管理注解是Servlet 3.0规范提供的编码式局部声明再说扫描机制和metadata-complete优先级最后补一句“两者混用时需要小心有可能导致容器无法启动”。这一套下来面试效果比背概念好得多。4.3 关于“混用”的警告具体解释一下为什么混用容易出问题。假设你在web.xml里配置了servlet servlet-namehello/servlet-name servlet-classcom.example.demo.HelloServlet/servlet-class /servlet servlet-mapping servlet-namehello/servlet-name url-pattern/hello.xml/url-pattern /servlet-mapping同时这个HelloServlet类上写了WebServlet(urlPatterns /hello)容器在启动解析时发现同一个类对应了两个不同的映射路径/hello.xml和/hello。Servlet规范对此没有给出明确的合并规则Tomcat遇到这种情况的处理方式是自动注册两边信息同时对外可访问两个路径。但这带来了隐患最典型的坑是如果你在注解里写name属性时它跟web.xml里的servlet-name冲突Tomcat启动时会报Servlet name 冲突或者提示Duplicate servlet name。因为不同组件复用了同一个注册名容器不知道该用哪个定义进行生命周期管理。稳妥做法一套Servlet只用一套注册方式。如果你必须兼顾老配置和新代码习惯那就把servlet和servlet-mapping写到web.xml里同时类上不要加任何WebServlet注解让XML作为唯一配置源。反过来如果类上已经加了注解那就把web.xml里关于这个Servlet的配置全部删掉别留一点。我的建议是学习阶段用小项目把两种方式分别吃透实战项目里跟着团队规范走。混用这种骚操作除非你得心应手否则不要碰。5. 运行机制与常见问题排查实录5.1 一次HTTP请求如何找到Servlet配置只是静态信息运行时的匹配过程才是理解配置全部意义的关键。假设浏览器地址栏输入http://localhost:8080/web-demo/helloTomcat内部做的事情有这几步。第一步根据端口号找到对应Service再根据Host虚拟主机配置匹配到webapps/web-demo这个Web应用。第二步根据请求路径/hello在当前Web应用的映射表里查url-pattern。第三步根据映射到的Servlet名找到对应的Servlet类名要么创建实例如果还没创建要么复用已有实例然后调用service()方法。第四步service()根据请求方法是GET还是POST分别把请求分发到doGet还是doPost。这个映射匹配过程是有优先级顺序的规则如下匹配类型示例优先级精确匹配/hello、/order/list最高路径前缀匹配/api/*、/user/*次之扩展名匹配*.do、*.action再次默认匹配缺省Servlet/兜底这些规则从Servlet规范直接衍生。实际工作中最烦人的问题是多个Servlet同时匹配同一个请求路径。比如你配了/hello精确匹配又配了/*通配符这时精确匹配优先级远高于通配符Tomcat不会傻到去遍历所有Servlet它按规则取最优。理解这个顺序对排查“为什么访问路径能被A Servlet处理而不是B”很有帮助。这里再补充一个初学者容易误读的概念/这个url-pattern是默认Servlvet它匹配所有未匹配到其他规则的请求但不会匹配/html/xxx.jpg这类静态资源不对其实/默认Servlet恰好就是用来处理静态资源的兜底逻辑。Tomcat自带的默认Servlet叫org.apache.catalina.servlets.DefaultServlet专门为静态资源提供支持。如果你在web.xml里扩展了一个/映射到自己的Servlet或者带了/*这种全匹配拦截会导致静态资源JS、CSS、图片全部被你的Servlet截走处理不当就会看到一串乱码或者404。这是很多接管老项目的开发者踩过的巨坑。5.2 注解不生效多半是版本和环境注解方式写好了浏览器访问却还是404。这种问题排查思路比较固定。第一步确认你的Servlet API版本。打开Maven的pom.xml看javax.servlet-api或者新版jakarta.servlet-api的版本。如果用的是3.0以下注解根本不存在。项目里写成% page %这种老写法时要注意兼容版本。Tomcat 7以上都支持Servlet 3.0/3.1只要依赖配到3.0以上即可。第二步确认web.xml里的version属性。如果web.xml版本写的2.5容器就算支持注解也不会主动扫描因为2.5规范时代还没有注解这套东西。改成3.1甚至4.0问题就没了。第三步确认类有没有被编译到WEB-INF/classes目录。有时候IDE编译设置出问题注解也扫不到。到部署目录手动看一眼如果没有对应的.class文件说明编译环节出了问题得先解决Maven或IDEA的编译配置。第四步确认WebServlet注解导入的是不是javax.servlet.annotation.WebServlet。有些老项目使用的javax.servlet包版本混乱代码里可能从别的地方引了一个完全不相关的WebServlet注解这类问题比较隐蔽要用IDE的依赖分析功能检查。5.3 易混淆概念速查最后整理几个日常问答里高频出现的概念点方便大家对照。疑问解释servlet-name可以不写吗在web.xml里必写它是Servlet在容器中的唯一注册标识注解方式下不写name属性则默认取类名url-pattern能写多个吗web.xml里一个Servlet可以配多个url-pattern注解方式的urlPatterns本身就是数组同样支持多个web.xml可以完全不写吗可以只要代码里用了注解项目完全可以没有任何web.xml文件。Servlet 3.0规范支持“零XML”Web应用loadOnStartup数字越大会怎样数字大小表示优先级顺序数值小的优先加载负数表示不预加载Servlet是线程安全的吗不是。容器默认对同一Servlet实例采用单实例多线程模型实例变量存在并发风险尽量不在Servlet里写可修改的成员变量一次请求会经过几个Servlet正常情况下一个如果配置了Filter链请求会先过Filter再进入Servlet还有一个隐含问题值得强调Servlet容器启动时如果load-on-startup配置的数字出现负数比如-1这个Servlet不会随容器启动创建而是等到首次请求再初始化。很多初学者在init方法里做了连接数据库的操作但启动Tomcat时发现日志没有任何初始化输出以为代码没生效其实只是因为懒加载策略生效了。想看初始化日志用浏览器访问一次对应路径或者把load-on-startup设成大于0的值。我个人在实际操作中的体会是配置Servlet这件事看起来只是几行XML或者一个注解但它是理解Java Web整个运行链条的最短路径。web.xml方式强迫你去面对“声明”和“映射”的分离注解方式让你体会“配置与代码合一”的现代风格两者在心里有清晰的对照后面学Spring MVC、学过滤器链、学权限框架都顺畅得多。最后再分享一个小技巧不管是web.xml还是注解配好之后先用浏览器直接访问看到页面输出后再考虑加参数、接数据库、做前后端联调。很多新手喜欢一口气把Servlet、业务逻辑、数据库全连起来结果出了404也不确定是配置的问题还是代码的问题。先把“路由”跑通再谈“业务”排查范围小心态也稳。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑