ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

Servlet配置详解:web.xml与@WebServlet注解两种方式完全指南

Servlet配置详解:web.xml与@WebServlet注解两种方式完全指南 Servlet 这个坑我刚开始学 Java Web 的时候踩了不少。当年照着教程写了一个 Servlet死活访问不到最后发现是 web.xml 里少配了一个servlet-mapping那种感觉真的很抓狂。后来学会了注解配置才慢慢意识到Servlet 的配置方法其实就两大类传统的web.xml配置方式和现代的注解WebServlet配置方式。两种方法我都有大量实际使用经验所以这节专门把它们彻底讲明白希望能给零基础的同学省下排查的时间。这篇博文主要围绕 Servlet 的两种配置方法展开一种是老牌但依然必学的 web.xml 配置另一种是后来 Java EE 引入的注解配置。适合零基础准备入门 Java Web 的同学也适合那些会写代码但对配置总是糊里糊涂的初级开发者。看完你不仅知道怎么配还会明白为什么会有这两种方式遇到 404 的时候心里也有数。1. 先把Servlet配置这件事的背景理清楚1.1 Servlet是干什么的配置到底在解决什么问题简单说Servlet 就是运行在 Web 服务器比如 Tomcat里的一段 Java 程序它负责接收 HTTP 请求处理业务逻辑最后把响应内容返回给浏览器。但你写好一个类之后服务器不可能平白无故就知道“这个类要处理哪个网址”所以要有个“登记”的过程这个登记就是 Servlet 的配置。你可以把 Servlet 比作一个公司的接线员。公司里有很多分机号每个接线员负责某个业务。接线员自己不会说你打哪个号找谁你得在前台登记一个分机表。这个分机表就是配置。没有配置来电请求根本找不到人。对应到代码里你需要告诉 Tomcat这个 Servlet 类叫什么名字它要处理的 URL 路径是什么。不管用 web.xml 还是注解核心信息就三样类、名字、URL 映射。1.2 为什么会有两种配置方式背后的演进逻辑早期 Servlet 是 Java EE 规范的一部分当时不能像现在这样直接在代码里写注解只能在项目里的 web.xml 文件中写大段的 XML 标签。web.xml 是 Web 应用的核心配置文件里面不仅配 Servlet还可以配置过滤器、监听器、欢迎页面、错误页等等。后来从 Servlet 3.0Java EE 6开始规范加入了注解配置的支持直接用WebServlet(/hello)写在 Servlet 类上三秒钟就把映射搞定了。这是为了降低配置复杂度让大家少写一点 XML。但现实是很多旧项目、老框架、生产环境历史包袱仍然在大量使用 web.xml而且在某些特殊场景下比如需要通过配置文件动态控制映射web.xml 依然不可替代。所以两种都不该忽视都值得掌握。2. 方式一web.xml 配置文件方式2.1 找到你的 web.xml它一般住在哪如果你用的是传统的 Maven Web 项目web.xml 默认位置是src/main/webapp/WEB-INF/web.xml。如果你是从 Eclipse Dynamic Web Project 创建的项目同样是放在WebContent/WEB-INF/web.xml。这个文件不是摆设Tomcat 启动时会先读取它根据里面的配置把 Servlet 注册到容器中。有些同学问我的项目里没有 web.xml 也能跑为什么因为如果你不使用 web.xml容器会默认基于注解扫描来注册 Servlet或者你的项目用了 Spring Boot 这种内置容器和自动配置的框架。但如果你是手动部署一个 .war 包到 Tomcat并且想用传统方式那么 web.xml 就是你逃不掉的那道程序。2.2 从零写一个 Servlet然后用 web.xml 配好它我们从一个最简单的 Servlet 开始代码如下package com.example.servlet; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; public class HelloServlet extends HttpServlet { Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException { resp.setContentType(text/html;charsetUTF-8); resp.getWriter().write(h1Hello, Java Web!/h1); } }注意这个类现在还没有任何配置它只是普通类。如果直接部署就算你启动了 Tomcat访问对应的路径也是找不到的因为你没有告诉容器这个类的映射关系。接下来我在 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_4_0.xsd version4.0 servlet servlet-namehelloServlet/servlet-name servlet-classcom.example.servlet.HelloServlet/servlet-class /servlet servlet-mapping servlet-namehelloServlet/servlet-name url-pattern/hello/url-pattern /servlet-mapping /web-app这段 XML 的意思很明确首先注册一个叫做helloServlet的 Servlet它对应的类是com.example.servlet.HelloServlet。然后定义映射凡是访问/hello这个路径的请求都交给名为helloServlet的这个 Servlet 去处理。部署后启动 Tomcat访问http://localhost:8080/你的项目名/hello就能看到页面上输出 “Hello, Java Web!”。2.3 玩转核心标签servlet 和 servlet-mapping 的分工很多初学者把servlet和servlet-mapping混淆认为只写一个就够了。这就是典型的“能注册但找不到”的错误根源。这两者的分工完全不同servlet标签做的是“注册”工作相当于告诉容器有一个类具备成为 Servlet 的资格请把它创建出来。它内部的servlet-name只是一个逻辑别名你可以随意命名比如我叫myServlet但一定要和后面映射里的名字完全一致。servlet-class是这个类的全限定名必须写准确包括包名和类名拼错一个字母都会导致类加载失败。servlet-mapping标签做的是“接线”工作它把之前注册的 Servlet 与 URL 地址绑定在一起。内部的servlet-name要和servlet里的名字一模一样否则容器会把它们当成两回事。url-pattern就是访问路径它必须用斜杠/开头例如/hello、/user/login都行。如果你想拦截所有请求可以写/*但要小心这样可能拦截到静态资源。为什么一个 Servlet 可以配置多个别名和多个映射因为你可以写几个servlet都指向同一个类也可以给同一个servlet-name写多个servlet-mapping让同一个 Servlet 处理多个不同的 URL。这个在项目里很实用比如同一个 Servlet 要根据请求路径的不同走不同的分支逻辑你可以为它配置/order和/order/list两个映射。2.4 当心这些web.xml配置的坑第一个坑是servlet-class写错。不要只写HelloServlet要写完整包名com.example.servlet.HelloServlet。如果你的包名写错Tomcat 启动时不会立即报错而是你访问路径时才抛ClassNotFoundException这个时候很多人会以为是 URL 写错了实际上是配置有问题。第二个坑是 XML 里是不能出现非法字符的注意标签闭合顺序。有很多同学复制模板时少写了一个/servlet结果整个 web.xml 解析失败Tomcat 直接启动不了。这种问题看控制台日志会发现 Formats 一个类似Parse Error的信息排错时先检查尖括号是否配对。第三个坑是 web.xml 的版本 header。我用的是 Servlet 4.0 对应的 header如果你的 Tomcat 版本比较老比如 Tomcat 8 以下是 Servlet 3.1/3.0你写version4.0容器可能会拒绝解析。建议根据你使用的 Tomcat 版本选择合适的 web-app 版本或者干脆直接用 3.0 以上的 header 也兼容大多数情况。实际上很多 IDE 在建项目时会自动给你生成不要自己乱改。3. 方式二注解配置方式3.1 告别XML用WebServlet几秒完成映射我在自己的新项目里基本已经很少写 web.xml 了除非项目需要动态的、外部可修改的配置。原因很简单用 Java 注解写在类上代码和映射信息放在一起一眼就能看清这个 Servlet 处理哪个路径特别顺手。Servlet 3.0 之后你只需要在类上方加一个注解就行例如package com.example.servlet; 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(/hello) public class HelloServlet extends HttpServlet { Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException { resp.setContentType(text/html;charsetUTF-8); resp.getWriter().write(h1Hello, Java Web!/h1); } }就这么简单。WebServlet(/hello)等同于 web.xml 里servlet加servlet-mapping两段配置。部署后启动 Tomcat访问项目路径下的/hello就能看到效果。容器在启动时通过注解扫描机制发现这个类上有WebServlet于是自动把它注册成 Servlet 并完成映射。3.2 注解里的属性与进阶写法WebServlet不只是能写一个字符串它背后还支持很多属性。完整形式如下WebServlet( name helloServlet, urlPatterns {/hello, /hello2}, loadOnStartup 1, initParams { WebInitParam(name encoding, value UTF-8) } ) public class HelloServlet extends HttpServlet { // ... }name相当于 web.xml 里的servlet-name可选。urlPatterns字符串数组可以写多个 URL等价于多个servlet-mapping。value如果你只想写一个 URL可以直接WebServlet(/hello)value和urlPatterns是互斥的不要同时使用。loadOnStartup值为正数时容器启动时就该加载这个 Servlet而不是等到第一次请求才实例化。这个对需要初始化数据的 Servlet 很有用但注意若多个 Servlet 设置此属性数值小者优先加载。initParams定义初始化参数对应 web.xml 中的init-param。如果你有很多 Servlet每个都写注解显然比写一大堆 XML 标签舒服。而且当你重命名类时映射跟着代码走不容易出现改类名忘改配置的情况。3.3 注解方式真的能替换 web.xml 吗从我自己的项目经验来看一般中小型项目、学习项目、新起的单体 Web 应用用注解完全够用也更推荐。它减少了文件数量逻辑内聚代码审查时能更快看出哪个 Servlet 对应哪个 URL。但如果你是做框架开发、开发一个可以被别人部署在不同路径下的中间件或者你的需求是运维修改 URL 而不重新编译 Java 代码那 web.xml 这种外部配置更合适。这里补充一个重要细节注解和 web.xml 可以同时存在。当同一个 Servlet 既配了注解又配了 web.xml 时容器会做合并处理。如果 URL 冲突了怎么办不同容器可能有不同优先级通常 web.xml 会覆盖注解但依赖容器实现。这个属于高级场景我建议你尽量不要让两边配置重复会引起混乱。4. 两种配置方式对比与选型建议4.1 一张表看清差异为了帮你更直观地理解我把它们放在一个表格里对照看对比维度web.xml 配置注解配置配置位置单独的 XML 文件直接写在 Servlet 类上配置量每个 Servlet 至少 6 行以上一行搞定可读性映射集中但代码与配置分离代码和映射强相关容易理解动态修改修改 XML 后重启容器无需重新编译需要修改代码重新编译部署适用版本所有 Servlet 版本均可Servlet 3.0 或更高版本典型使用场景旧项目、大型企业应用、需外部配置新项目、Spring Boot 风格、快速开发有些教程会告诉你“注解是潮流web.xml 是历史垃圾”我不太赞同。在生产中我见过很多运行多年的老系统依然在 web.xml 里管理十几个 Servlet改起来也很有条理。两种方式都是工具关键是理解各自本质一份代码到一个 URL 的映射声明放在哪里最合适。4.2 什么时候坚决用web.xml什么时候用注解我先说用 web.xml 的几种情况。如果你手上的项目是从十几年前的 SSM 或者 SSH 架构演化来的web.xml 已经存在并且有很多过滤器、初始化参数那不要为了“新潮”去把它改成注解改动的风险大于收益。另外如果你需要让运维同学在不重新打包 war 的情况下调整 Servlet 的访问路径用 web.xml 是更现实的做法。注解则适合你新开的项目、练习项目、或者使用 Spring Boot 这类内嵌容器环境的项目。尤其是当你建立的 Servlet 数量很多但每个都很短时注解一行就是一个映射文件少启动扫描也快。尤其是团队规范里要求代码与配置同源时注解方式在 Code Review 上更友好。我个人的建议是学习阶段两种都要亲手实现一遍至少保证你能在半小时内分别用两种方法搭出一个能访问的 Servlet。等上手后如果项目没有外部配置要求直接用注解就行。5. 实操中常见问题与排查技巧5.1 最典型的404我配置了为什么还是访问不到这个坑出现的频率极高。遇到 404 别慌按这个顺序排查第一确认访问路径是否正确。Tomcat 的默认端口是 8080如果你的项目名是mywebServlet 映射是/hello那么完整路径应该是http://localhost:8080/myweb/hello。很多人会忘记加项目名直接在 Tomcat 根目录访问/hello自然 404。第二确认 web.xml 中的servlet-name是否完全一致。懒得多说你写helloServlet映射里也要写helloServlet大小写都要一样。不一致时控制台往往不报错但请求就是找不到映射。第三确认编译输出的 class 文件存在。打开项目的target/classesMaven或 WEB-INF/classes 目录看是否存在com/example/servlet/HelloServlet.class。如果类没有编译成功映射再对也没用。第四确认把注解方式的时候是否在类上真的写了注解并且包导入正确。WebServlet的包是javax.servlet.annotation.WebServlet如果你 import 错了编译直接失败。5.2 明明代码没问题但 Tomcat 启动时还是报错为了减少启动失败的机率这里提醒几个关键点。如果你用的是 Maven部署 .war 到 Tomcat 时要确保项目打包成了正确的 war 结构。Web 内容要放在src/main/webapp下web.xml 要放在src/main/webapp/WEB-INF下Servlet 类则在src/main/java里正常写。路径放错Tomcat 就找不到配置也会出现各种奇怪问题。另外注意框架冲突。很多同学偷懒用 Spring 的DispatcherServlet同时又自己写了原生的 Servlet。你那个原生 Servlet 如果配置了/这种全局路径会把所有请求都抓走自己的路径自然变成 404。这种问题很隐蔽。当你同时使用 Spring MVC 和原生 Servlet 时优先用更具体的路径模式比如/api/*。5.3 几个提升效率的小技巧写到这里分享点私藏的实操技巧对入门挺管用的。第一URL 映射尽量不要用中文命名。虽然有些容器支持 URL 中的中文但 Nginx 反向代理、浏览器编码、日志记录都会出现意外项目里避免这种不必要的麻烦。第二一个 Servlet 同时处理多个 URL 时在doGet或doPost里用req.getServletPath()来判断当前访问的 URL再分流处理。这个方法在使用/*通配符时特别有用。第三如果你用 IDEA 开发可以直接在 Servlet 类上按Alt Insert选择 Generate Servlet 或者直接创建 Servlet 类时勾选生成注解这样可以自动生成WebServlet比手动拼写快很多。但建议初学阶段还是手动写几遍加深印象。第四想调试初始化参数可以在 web.xml 或注解里配一个debug参数然后 loadOnStartup 启动时打印日志。很多初学者不知道 Servlet 的init()方法只会执行一次所以排查配置问题时要盯着启动日志而不是请求日志。最后的实际操作心得我从零基础一路摸爬滚打过来最大的心得是Servlet 的两种配置方式其实都不难但如果你只懂一种遇到旧项目就会很束手束脚如果你只懂注解就永远不会理解那些老项目里 web.xml 为什么那么长。两个都练熟了以后看框架源码、理解 Filter 和 Listener 的加载逻辑都会轻松很多。最后再分享一个小技巧写 web.xml 配置的时候给servlet-name起名用“类名驼峰 Servlet”的方式比如helloWorldServlet这样映射多的时候一眼能看出是哪个类。注解配置的时候URL 尽量和业务语义一致不要只用Servlet1这种。刚开始学 Java Web 时踏踏实实把两种配置方式都能默写出来后面看 Spring MVC 的 DispatcherServlet 配置就不会晕了这个基础打好了Java Web 的大门才算真正推开。
返回列表