
用 IDEA 2023 创建一个 Servlet 项目这事看着简单但真正动手时你就会发现卡点往往不在 Servlet 代码本身而在“项目怎么建、Tomcat 怎么配、依赖怎么引”这一串前置流程上。很多新手照着老教程走结果 IDEA 版本不同、Tomcat 版本不同下一步就报错然后整个人直接懵掉。这篇文章我按自己的实际操作路径给你一条从零到一跑通 Servlet 项目的完整流程。包括环境怎么选型、IDEA 2023 里怎么建 Maven Web 工程、Tomcat 怎么接进来、第一个 Servlet 怎么写、怎么返回 JSON最后还会把请求参数处理、中文乱码、404/500 这类高频问题一起讲清楚。适合刚学 Java Web 的初学者也适合被 IDEA 折腾过、想系统梳理一遍的同学参考。1. 环境准备版本选对后面少踩一半坑1.1 我的开发环境清单先说结论再解释为什么这么选。我这次使用的组合是IDEA 2023.2Ultimate 版JDK 1.8实际用 8 还是 17 都可以建议按你们公司/学校的项目基线来Maven 3.8IDEA 内置的 Maven 也够用Tomcat 9.0.8x重点不是 Tomcat 10Servlet API 4.0javax.servlet 坐标这套组合看起来“老”但它是目前网上绝大多数教程、教材能直接对得上的版本。如果你用 Tomcat 10 或 11Servlet 包名从javax.servlet变成了jakarta.servlet很多老代码、老教程里的 import 全部要改。新手阶段没必要给自己加这个负担先用 Tomcat 9 把原理跑通后面要升级再升级。1.2 为什么选择 Maven 而不是手搭目录有些朋友学 Servlet 的时候用的是最原始的方式手动创建WEB-INF/classes目录、手动扔 jar 包、手动编译。这套流程在 2010 年前后是主流但现在再用就有点折磨自己了。用 Maven 的好处有三个依赖管理省心。javax.servlet-api、jstl、jackson这些库在pom.xml里写个坐标就自动下载不用满世界找 jar 包。目录结构约定俗成。src/main/java、src/main/resources、src/main/webapp是 IDEA 和 Maven 都认的标准布局项目一创建就是规范的。打包部署方便。mvn package直接出 war 包往 Tomcat 的webapps目录一扔就能跑。IDEA 2023 内置了对 Maven 的完整支持你不用额外装什么插件直接用它自带的 Maven 就行。1.3 关于 IDEA 版本与授权的一点建议IDEA 2023 分为 Community社区版和 Ultimate旗舰版。社区版免费但早期版本不支持 Tomcat 集成和 Java EE/Web 开发只能当普通 Java 编辑器用。Ultimate 版功能完整但需要付费订阅。网上有很多关于“激活”“破解”的内容我的态度很明确不要碰。一个是安全风险破解工具里面夹带什么你根本不知道另一个是稳定性问题IDEA 更新后破解很容易失效到时候项目正写着突然打不开哭都来不及。想省钱就用社区版配合外部 Tomcat 手动部署想省心就用官方正版或试用期JetBrains 对学生和开源开发者还有免费授权计划走正规渠道最稳妥。2. 创建 Servlet 项目IDEA 2023 里的完整操作流程2.1 用 Maven 骨架快速创建 Web 工程IDEA 2023 创建 Servlet 项目说到底是一件事创建一个“带 Web 目录的 Maven 项目”。操作路径如下。打开 IDEA选择New Project。在左侧选择Maven然后勾选Create from archetype在列出的骨架里找到maven-archetype-webapp。提示如果列表里没有这个骨架点一下Add Archetype手动填上org.apache.maven.archetypes:maven-archetype-webapp:1.4IDEA 会自动从中央仓库拉取。接下来填写GroupId和ArtifactId。GroupId一般用公司域名倒写比如com.exampleArtifactId是项目名比如servlet-demo。这俩会决定你后面package路径的根目录建议一次想好避免后面大量改包名。填完之后 IDEA 会开始下载依赖第一次可能比较慢。等右下角进度条跑完项目结构就出来了。2.2 认识生成的项目结构创建完成后src/main/webapp目录下会自动生成一个index.jsp和一个WEB-INF/web.xml。pom.xml在最外层。这和我们平时写普通 Java 项目不太一样多出来的webapp目录就是将来部署到 Tomcat 里的 Web 根目录。我自己习惯把webapp类比成“一个网站的根文件夹”。你放在webapp下的index.jsp、静态图片、CSS、JS用户通过浏览器访问项目路径时能看到而WEB-INF是一个受保护的区域浏览器直接访问不到只有 Servlet 通过forward或include才能跳转进去。先把自动生成的web.xml打开看一下里面通常是一个 Servlet 3.0 或 4.0 的配置文件头。记一下版本号后面写映射时可能要用。2.3 pom.xml 添加 Servlet 依赖在 IDEA 里打开pom.xml在dependencies标签内加上 Servlet API 和 JSP 的依赖。dependencies dependency groupIdjavax.servlet/groupId artifactIdjavax.servlet-api/artifactId version4.0.1/version scopeprovided/scope /dependency dependency groupIdjavax.servlet.jsp/groupId artifactIdjavax.servlet.jsp-api/artifactId version2.3.3/version scopeprovided/scope /dependency /dependencies注意这里的scope是provided意思是“编译和测试时需要但打包时不要打进去”。因为 Tomcat 自己就带了一套 Servlet 实现如果打进去了反而会和 Tomcat 本身的类冲突。这个细节很多人第一次不注意后面部署到服务器上各种类重复、版本冲突排查半天。2.4 配置 Tomcat 运行环境这是新人最容易卡住的一步。IDEA 里写完代码不能直接双击运行你得把项目挂到一个 Servlet 容器上。这里我用的是本地 Tomcat流程如下。先下载 Tomcat 9解压到一个没有中文和空格的路径比如D:\apache-tomcat-9.0.87。然后在 IDEA 里点右上角的Add Configuration选择Tomcat Server下的Local。在Tomcat Server Settings里选中你刚才解压的目录IDEA 会自动识别。接下来切到Deployment页签点加号选择Artifact选中项目名后面带war exploded的那一项。war exploded是“展开的 war 包”意思是用目录方式部署好处是修改代码后可以热更新不用每次重启 Tomcat。Application context我一般改成/或者/servlet-demo。这决定了你访问时的根路径。如果设成/servlet-demo那么访问 Servlet 的 URL 就是http://localhost:8080/servlet-demo/YourServlet。最后在Server页签把On frame deactivation改成Update classes and resources这样切到浏览器时自动更新资源开发体验会顺滑很多。3. 第一个 Servlet生命周期、映射方式与代码实测3.1 Servlet 生命周期到底在说什么写代码之前先花两分钟理解 Servlet 的生命周期。你可以把它类比成“开一家小吃店”init()店面装修好开门营业前的准备。整个生命周期只执行一次适合做初始化工作比如读取配置、建立数据库连接。service()客人点餐环节。每次 HTTP 请求进来都会经过它它会根据请求方法是 GET 还是 POST再分发到doGet()或doPost()。destroy()店面关门清算整个生命周期只执行一次适合释放资源。明白了这个流程你写代码时心里就有数了初始化逻辑放init主业务逻辑放doGet/doPost清理逻辑放destroy。不要什么都往doGet里塞。3.2 写一个最简单的 HelloServlet在src/main/java下新建一个包比如com.example.servlet然后创建HelloServlet类继承HttpServlet重写doGet方法。package com.example.servlet; 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; import java.io.PrintWriter; WebServlet(/hello) public class HelloServlet extends HttpServlet { Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { resp.setContentType(text/html;charsetutf-8); PrintWriter writer resp.getWriter(); writer.write(h1Hello Servlet, IDEA 2023/h1); } }这里用到了WebServlet注解直接把/hello这个路径映射到当前类。IDEA 2023 会对注解做代码提示写起来比较方便。写完这一步如果你用的是前面配置的 Tomcat直接点右上角运行按钮。浏览器访问http://localhost:8080/hello如果 Application context 是/或者http://localhost:8080/servlet-demo/hello就能看到输出内容。注意resp.setContentType(text/html;charsetutf-8)这行一定不要省。不设置字符集的话浏览器会按照默认编码解析中文字符大概率乱码。3.3 注解映射和 web.xml 映射的区别很多教材还用web.xml配置 Servlet 映射。在 Servlet 3.0 之前没有WebServlet注解必须在web.xml里写servlet和servlet-mapping标签。到了 Servlet 3.0 以后注解方式成了主流。两者各有适用场景注解方式代码量少类和映射关系一目了然适合小型项目。web.xml方式集中管理映射方便运维人员和架构师统一查看适合需要统一控制、或者不能随意改代码的场景。如果使用web.xml配置写法是这样的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.xml时要把HelloServlet类上的WebServlet注解去掉否则两处都配置启动时会因为重复映射报java.lang.IllegalArgumentException。3.4 URL 匹配规则斜杠里的学问WebServlet里的路径不是随便写的它有一套匹配优先级规则新手经常在这里踩坑。精确匹配/hello完全相等才匹配。目录匹配/api/*匹配/api开头的所有路径。扩展名匹配*.do匹配所有以.do结尾的路径。默认匹配/匹配所有未命中的请求。优先级从高到低是精确匹配 目录匹配 扩展名匹配 默认匹配。你写/的时候要特别小心它会拦截所有静态资源。比如你放了一张a.jpg如果没有单独的静态资源配置直接访问/a.jpg会被这个 Servlet 接管然后 404 或者报错。4. 进阶实操请求参数、JSON 返回与外部 API 调用4.1 获取 GET 和 POST 请求参数写 Servlet 的意义不只是输出一段 HTML更多时候是接收前端传过来的参数做业务处理再返回结果。获取参数的核心方法是request.getParameter()。WebServlet(/login) public class LoginServlet extends HttpServlet { Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { req.setCharacterEncoding(utf-8); resp.setContentType(text/html;charsetutf-8); String username req.getParameter(username); String password req.getParameter(password); PrintWriter writer resp.getWriter(); if (admin.equals(username) 123456.equals(password)) { writer.write(登录成功); } else { writer.write(用户名或密码错误); } } }这里有一个关键细节req.setCharacterEncoding(utf-8)必须放在getParameter之前调用否则 POST 请求里带的中文参数一样会乱码。GET 请求的乱码问题主要在 Tomcat 的server.xml里的URIEncoding配置不过 Tomcat 8.0 之后默认就是 UTF-8一般不用改。4.2 返回 JSON 而不是 HTML现在前后端分离很常见Servlet 更多是用来做接口返回 JSON。两步就能搞定设置Content-Type为application/json然后把字符串按 JSON 格式输出。WebServlet(/api/user) public class UserServlet extends HttpServlet { Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { resp.setContentType(application/json;charsetutf-8); String json {\name\:\张三\,\age\:25}; resp.getWriter().write(json); } }如果数据结构简单手拼 JSON 没问题。但字段一多手拼会非常容易出错。推荐引入 Jackson 库在pom.xml里加依赖。dependency groupIdcom.fasterxml.jackson.core/groupId artifactIdjackson-databind/artifactId version2.15.2/version /dependency然后定义一个普通的 Java 类用ObjectMapper转成 JSON 字符串。ObjectMapper mapper new ObjectMapper(); User user new User(张三, 25); String json mapper.writeValueAsString(user); resp.getWriter().write(json);writeValueAsString可以直接把对象序列化成 JSON 字符串返回给前端。Jackson 是 Fastjson 之外比较主流的选择稳定性和社区活跃度都靠谱。4.3 在 Servlet 里调用大模型 API 接口前面说到的“体验 servlet 调用大模型 api 接口”其实就是让 Servlet 充当后端中转层。浏览器不能直接拿着密钥去调第三方大模型接口那样会把API Key暴露在前端代码里非常危险。正确做法是 Servlet 接收前端请求再由后端去请求大模型服务拿到结果后返回给前端。代码写起来不复杂用 JDK 自带的HttpURLConnection就能实现。这里我以一个通用的第三方大模型接口为例。WebServlet(/api/chat) public class ChatServlet extends HttpServlet { Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { req.setCharacterEncoding(utf-8); resp.setContentType(application/json;charsetutf-8); String prompt req.getParameter(prompt); String apiKey System.getenv(LLM_API_KEY); // 建议从环境变量读取 // 构造请求体 String body {\prompt\:\ prompt \,\max_tokens\:100}; URL url new URL(https://your-llm-api.example.com/v1/completions); HttpURLConnection conn (HttpURLConnection) url.openConnection(); conn.setRequestMethod(POST); conn.setRequestProperty(Content-Type, application/json); conn.setRequestProperty(Authorization, Bearer apiKey); conn.setDoOutput(true); conn.getOutputStream().write(body.getBytes(utf-8)); // 读取响应 BufferedReader reader new BufferedReader( new InputStreamReader(conn.getInputStream(), utf-8)); StringBuilder result new StringBuilder(); String line; while ((line reader.readLine()) ! null) { result.append(line); } reader.close(); conn.disconnect(); resp.getWriter().write(result.toString()); } }有几点提醒你API Key不要硬编码在代码里更不要提交到 Git 仓库推荐用环境变量或配置文件管理外部接口的connectTimeout和readTimeout一定要设置否则接口卡住时你的 Servlet 线程也会一直挂着生产环境建议用线程池 连接池直接HttpURLConnection在高并发下性能不够看。5. 常见问题与排查技巧实录5.1 高频报错速查表我在带新人和自己学习的过程中遇到的典型报错基本集中在下面几类。整理成一张表方便你直接对照排查。现象可能原因解决办法浏览器 404URL 路径写错或Application context起头不对检查访问路径是否包含/项目名再核对WebServlet里的映射值浏览器 404 且 Tomcat 报错web.xml 和注解重复配置映射二选一不要同时配后端 500控制台ClassNotFoundException: javax.servletpom 里没引javax.servlet-api或 scope 写错添加依赖scope 用provided启动时端口占用8080 被其他程序占用改 Tomcat 端口或杀掉占用进程中文变成问号/乱码请求或响应编码未设置req.setCharacterEncoding(utf-8)和resp.setContentType(...charsetutf-8)java.lang.NoClassDefFoundErrorjar 包冲突或缺少依赖检查 pommvn dependency:tree查看依赖树Tomcat 闪退JAVA_HOME 未配置或 JDK 版本不匹配确认JAVA_HOME指向正确 JDK 路径5.2 依赖作用域provided 和 compile 的坑这个坑值得单独拿出来说。javax.servlet-api的scope如果写成默认的compile打包生成的 war 包里会带上servlet-api.jar。部署到 Tomcat 后Tomcat 自身的lib目录里也有一份servlet-api.jar两边的类就冲突了。常见的表现是Tomcat 启动时打印Duplicate jar警告。Servlet 类加载异常抛出ClassCastException。某些时候能跑但行为莫名其妙。如果你是跟着模板创建的 pom一定要检查javax.servlet-api的scope是否被设置成了provided。这个provided的含义就是“我编译时需要但运行时容器已经提供了不要再打包”。5.3 修改代码后不生效热部署设置IDEA 里运行 Tomcat 时默认并不会每次自动编译并更新到服务器。很多人改了代码刷新浏览器没有变化就以为是代码写错了。解决方法是把 Run Configuration 里的On frame deactivation设置为Update classes and resources。这样你从 IDEA 切到浏览器时IDEA 会自动把变更的 class 和资源推送到 Tomcat 的部署目录。注意如果新增了方法签名、改了类结构这种大改动还是需要重启 Tomcat不然会出现NoSuchMethodError之类的情况。如果你改了web.xml、pom.xml或者新增了依赖也建议手动重启别撑着热部署硬跑。5.4 浏览器直接访问 WEB-INF 下的页面这个问题我当年也踩过。把 JSP 放到WEB-INF下面然后访问http://localhost:8080/项目名/WEB-INF/index.jsp结果是 404。其实这是 Servlet 规范里的一种保护机制。WEB-INF目录对浏览器是封闭的外部请求永远无法直接拿到里面的文件。如果你想访问WEB-INF下的 JSP必须通过 Servlet 转发req.getRequestDispatcher(/WEB-INF/index.jsp).forward(req, resp);这样做的好处是页面文件对外不可见所有访问都强制走 Servlet你做登录校验、权限控制就非常方便。这是一套经典的 MVC 思路Servlet 当 ControllerJSP 当 View。几个我后来才想明白的经验写 Servlet 项目这件事难点真的不在于 API 本身而在于项目构建工具和容器之间的配合。IDEA 2023 相比之前的版本对 Java Web 的支持已经算是非常顺滑了只要你把 Maven 工程创建、Tomcat 配置、依赖声明这三件事理顺后面写代码就是水到渠成。我自己的感受是刚接触时要刻意练习“从浏览器地址栏到 Servlet 代码”的映射思维输入一个 URL请求怎么被 Tomcat 接收怎么根据web.xml或注解找到对应类怎么进入doGet/doPost方法里怎么处理参数、返回响应。这条链路每想通一个节点你就离真正理解 Java Web 更进一步。之后你再学 Spring MVC、Spring Boot会发现它们的底层就是对这套 Servlet 机制的封装和增强。把 Servlet 项目亲手从零建一遍绝对不亏。