资讯动态

Maven+Servlet+Tomcat:IDEA 2019搭建Java Web项目实战

发布时间:2026/10/15 12:07:28 来源:尧图企业网站定制
我第一次用IDEA 2019搭Java Web项目时犯了一个特别基础的错误直接建了一个普通Java工程然后手动把Tomcat自带的servlet-api.jar复制进项目里。折腾了一天项目确实跑通了但换目录、换电脑、换版本的时候问题全冒出来依赖乱成一团。后来老老实实改用Maven建Web工程才发现“看着多绕了一步”的做法反而能让你把第一个Servlet跑得明明白白。这篇文章要解决的问题很聚焦在IDEA 2019环境下怎么用Maven从零搭建一个Web工程手写第一个Servlet再用Tomcat把它跑起来供浏览器访问。适合刚接触Java Web、还没把Maven和Servlet这两个概念串起来的同学也适合照着视频敲完却不知道“每一步到底在做什么”的人。我会把创建项目、补全目录、写Servlet、配Tomcat以及最常见的404和依赖报错全部按实际操作顺序走一遍。1. 开工前先说清楚为什么绕道Maven反而最快1.1 两种常见的起步方式差在哪里很多新手在IDEA里新建项目时都会在“普通Java工程”和“Maven工程”之间犹豫。我最早用的是普通Java工程做法是File → New → Java然后右键项目 → Open Module Settings把Tomcat安装目录下的servlet-api.jar加到Libraries里。这种做法的好处是临时能跑坏处也很明显jar包是手工引进去的项目一旦换到其他电脑依赖信息不会自动恢复。你根本不知道自己的项目依赖了哪些版本也没法管理。打包成war部署到Tomcat时还得手动处理一堆琐事。Maven的作用简单说就是三件事告诉你依赖从哪来、项目打成什么格式、目录结构按什么规则摆放。它像工地上的物料清单和施工规范——哪些材料要买、放在哪个房间、最后建成的结构长什么样全部有标准。你用pom.xml描述需求Maven去仓库拉取再帮你编译、打包。刚开始会觉得多了一步但这一步省掉的是后面无数次手工折腾。1.2 Servlet到底是个什么角色Servlet不是Spring MVC那种大型框架它本质上就是一个运行在服务器里的Java类负责接收HTTP请求并返回响应。你可以把它类比成餐厅门口的服务员客人浏览器递过来一张点菜单HTTP请求服务员转给后厨业务代码做好菜之后再由服务员端回去HTTP响应。但Servlet不会自己凭空运行。它需要Tomcat这类Web容器来创建、调用和销毁。Tomcat负责监听端口、接收请求、找到对应的Servlet类、调用对应的方法。你作为开发者要做的只是告诉Tomcat当访问某个地址时该调用哪个类。这个“告诉”的过程在Servlet 3.0之后的版本里最简单的方式就是WebServlet注解。1.3 项目需要的最简依赖和范围一个最基础的Maven Web项目其实只需要一个Servlet依赖。dependency groupIdjavax.servlet/groupId artifactIdjavax.servlet-api/artifactId version3.1.0/version scopeprovided/scope /dependency这里的scopeprovided值得画个重点。意思是编译代码时我需要这个jar包但最终打出war包的时候不要把它打包进去。为什么因为Tomcat的lib目录里已经自带了一份servlet-api.jar。如果你再塞一遍两个同名的Servlet类同时存在轻则找不到类重则版本冲突直接启动报错。很多初学教程把这行配置直接复制过去但从不解释provided的含义结果有人把它删掉又有人把它改成compile改完各种诡异问题。这里先说透provided是Servlet开发的标准姿势。2. 在IDEA 2019里新建带Maven骨架的Web项目2.1 新建项目的完整选择路径IDEA 2019新建项目时有两条路径可以达到类似目的我这里用的是最标准的一条File → New → Project → 左侧选Maven → 勾选Create from archetype → 在列表里找到org.apache.maven.archetypes:maven-archetype-webapp→ Next。这里有个容易卡住的地方IDEA需要联网拉取archetype列表第一次打开可能会加载半天甚至列表是空的。我当时的处理是等两分钟再打开一次就出来了。如果你实在等了很久还是空的可以检查一下Maven的settings.xml是否指向了能正常下载依赖的仓库地址。这一步不涉及什么复杂原理就是让Maven能正常访问中央仓库而已。还有一点必须提前告诉你IDEA Community社区版不支持Java Web和Tomcat的完整集成工具栏里找不到Tomcat的运行入口。如果你手头是社区版建议先用Ultimate旗舰版来完成这个实验否则后面会卡在配置Tomcat那一环。2.2 坐标和项目名怎么填点击Next之后需要填写Maven坐标。我填的是GroupIdcom.exampleArtifactIdservlet-demoVersion默认1.0-SNAPSHOTGroupId一般写反向域名作用像包名用来区分组织或公司。ArtifactId是项目的实际名字对应的是你的war包最终叫什么。这里有个小细节IDEA默认会把ArtifactId当项目名Module路径也用这个所以尽量取一个能看懂的名字建议用纯小写字母和连字符不要用中文和空格。2.3 创建完成后先看一眼目录结构Finish之后IDEA会帮你生成一个最简骨架。它的目录大概长这样servlet-demo ├─ pom.xml └─ src └─ main ├─ resources └─ webapp ├─ WEB-INF │ └─ web.xml └─ index.jsp注意了这个骨架里没有我们最想要的src/main/java目录。很多新手在这里直接懵了“我明明建的是Java项目为什么不给我建Java目录”原因是webapp这个archetype默认只生成Web资源目录源码目录需要你自己创建。不要慌这是正常现象我会在下一步带你补上。如果你用Maven面板查看Dependencies可能会看到一堆文件但里面并没有javax.servlet-api的依赖。别急这也正常初始pom.xml几乎是空的依赖本来就需要我们自己加。3. 补全工程结构把骨架改造成可用的Web工程3.1 手动创建src/main/java并标记为源码目录回到项目里右键src/main目录 → New → Directory输入名字java。建好之后它现在只是个普通目录IDEA不会主动把它当成源码目录来看待所以还要做一次关键操作右键java目录 → Mark Directory as → Sources Root。这一步的含义是告诉IDEA这个目录里的.java文件要参与编译编译结果会输出到target/classes。如果你跳过标记后面写完Servlet类IDEA不会报红但编译时它会假装这个类不存在。很多人最后404找不到Servlet类原因往往就出在这里。同样的道理src/main/resources如果没被标记也需要右键 → Mark Directory as → Resources Root。这个目录以后用来放一些配置文件现在空着也没关系。3.2 在pom.xml里补齐依赖和打包配置打开项目根目录的pom.xml你会看到IDEA自动生成的坐标信息。这里要注意maven-archetype-webapp生成的pom.xml默认的packaging标签通常是war不过有些版本也可能是jar务必确认一下packaging为warWeb项目最终打包成可部署到Tomcat的war文件。packaging为jar普通Java类库这不是我们要的。我的做法是直接把pom.xml替换成下面这个最简版本同时加上了编译插件避免IDEA默认用奇怪的编译器版本导致JDK不匹配。?xml version1.0 encodingUTF-8? project xmlnshttp://maven.apache.org/POM/4.0.0 xmlns:xsihttp://www.w3.org/2001/XMLSchema-instance xsi:schemaLocationhttp://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd modelVersion4.0.0/modelVersion groupIdcom.example/groupId artifactIdservlet-demo/artifactId version1.0-SNAPSHOT/version packagingwar/packaging properties project.build.sourceEncodingUTF-8/project.build.sourceEncoding maven.compiler.source1.8/maven.compiler.source maven.compiler.target1.8/maven.compiler.target /properties dependencies dependency groupIdjavax.servlet/groupId artifactIdjavax.servlet-api/artifactId version3.1.0/version scopeprovided/scope /dependency /dependencies build finalNameservlet-demo/finalName /build /project这里我特意把JDK编译版本锁成了1.8因为IDEA 2019配合Java 8几乎是当年的黄金组合。如果你本机只装了更高版本的JDK把maven.compiler.source和maven.compiler.target改成对应的版本也行。放心上面的配置完全兼容Tomcat 8和Tomcat 9。Tomcat 8对应Servlet 3.1Tomcat 9对应Servlet 4.0但servlet-api 3.1的写法在Tomcat 9里同样能跑基础接口没变。3.3 resources目录为什么值得留一个习惯在刚开始的这个Demo里src/main/resources里什么都不用放。但请你养成习惯以后数据库连接配置、日志配置、MyBatis映射文件这些都会放在这个目录下而且经过Maven构建后它们会被原样复制到target/classes里。这比把配置文件放在其他角落要规范得多。现在空着但目录结构保留在那里后面学习框架时你会感谢这个习惯。4. 写第一个Servlet从代码层面理解一次HTTP响应4.1 Servlet类的骨架和关键导入现在真正的重头戏来了。在src/main/java下新建包com.example再新建Class类名叫HelloServlet然后让它继承HttpServlet。package com.example; 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 request, HttpServletResponse response) throws ServletException, IOException { response.setContentType(text/html;charsetUTF-8); PrintWriter writer response.getWriter(); writer.println(html); writer.println(headtitleHello Servlet/title/head); writer.println(body); writer.println(h1第一个 Servlet 跑通了/h1); writer.println(/body); writer.println(/html); } Override protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { doGet(request, response); } }为什么必须继承HttpServlet而不是随便写个普通类因为Tomcat在运行时只认继承了HttpServlet的类它会利用Java反射机制创建实例然后根据HTTP请求的类型自动调用doGet或者doPost。你不需要自己写main方法生命周期由容器代管。这里还有一个知识点HttpServlet的父类GenericServlet提供了一套完整的Servlet生命周期方法比如init()、service()、destroy()只不过我们平时只关心doGet和doPost这两个钩子方法。4.2 doGet和doPost以及响应的中文编码doGet处理浏览器通过地址栏直接访问的GET请求doPost处理表单POST提交或者Ajax POST请求。初学者最容易犯的问题是把代码写在doGet里然后表单用methodPOST提交结果发现Servlet永远不执行。为了偷懒很多Demo会在doPost里直接调doGet我这里也这么做了至少保证逻辑一致。response.setContentType(text/html;charsetUTF-8)这行千万不能漏。它的意思是告诉浏览器我返回的内容是HTML而且使用UTF-8编码。如果没有指定字符集Tomcat默认按ISO-8859-1输出中文显示出来就是乱码。你可以把这行理解成“上菜前给客人说清楚这是中餐还是西餐并且用中文菜单点单”。4.3 WebServlet注解与web.xml的取舍注解里的/hello就是访问路径。路径必须以斜杠开头写成hello是不对的Tomcat会直接报错。用注解方式配置Servlet意味着你不用再去web.xml里手动注册这种写法是Servlet 3.0以后的主流。当然maven-archetype-webapp默认生成的web.xml还是会存在的这个没关系留着不影响。旧项目需要用XML方式配置Servlet时一般在web.xml里这样写servlet servlet-nameHelloServlet/servlet-name servlet-classcom.example.HelloServlet/servlet-class /servlet servlet-mapping servlet-nameHelloServlet/servlet-name url-pattern/hello/url-pattern /servlet-mapping两套配置的最终效果完全一样但注解方式明显更简洁。我把两者的对比列一下方便你理解为什么不用写web.xml对比维度WebServlet注解web.xml配置配置位置写在类上写到XML文件里Servlet容器支持需要3.0及以上版本所有版本都支持阅读性类名与路径直接对应需要来回翻文件维护成本改类时路径跟着改改了XML还要重启生效如果你以后接手老项目看到web.xml里到处是servlet-class和servlet-mapping不用慌那只是历史版本留下的风格原理是一样的。4.4 提前踩一下编码的坑在实际的实验里我第一次访问Servlet时返回的内容一切正常但一旦表单里的中文经过POST请求传到Servlet再echo回页面就出现了乱码。Tomcat 8开始POST请求体的默认编码就是UTF-8所以大多数情况下没问题。但老版本的Tomcat并不保证这一点所以保险起见在doPost里读取参数之前加一行request.setCharacterEncoding(UTF-8)能省去很多玄学问题。Override protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { request.setCharacterEncoding(UTF-8); doGet(request, response); }5. 让项目跑起来Tomcat配置与第一次访问5.1 准备一个本机TomcatServlet自己不会跑你得先准备好一个Web容器。我用的Tomcat 9.0.x解压到一个不含中文和空格的路径比如D:\apache-tomcat-9.0.xx。解压出来的目录里几个关键目录要认一下bin启动和关闭脚本都在这里。confserver.xml等全局配置在在这里。libTomcat自带的jar包servlet-api.jar就在这。webapps放war包或部署文件夹的地方。初学者不建议去记命令行启动方式因为IDEA会帮你完成Tomcat的呼起但你至少要知道Tomcat长什么样不然出了端口错误都不知道去哪儿查。5.2 在IDEA 2019里配置Tomcat点击IDEA右上角的运行下拉框 → Edit Configurations → 左上角加号 → 找到Tomcat Server → Local。注意如果在加号列表里没有Tomcat Server这一个选项几乎可以断定你使用的是IDEA Community社区版。这不是项目配置的问题是版本功能限制。需要切换到Ultimate版才能做这一步。在弹出的页面里Name随便填比如Tomcat9然后点击Application server右边的Configure在弹出的对话框里选择你解压好的Tomcat目录IDEA会自动识别版本号。这一步完成后IDEA就能直接和Tomcat通信了。第一次操作时我还遇到过一个问题IDEA提示需要选择JRE确保你项目用的JDK和Tomcat支持的是同一套Java环境用JDK 8最省事。5.3 让Tomcat知道该运行哪个项目配好Tomcat之后还不能直接点启动因为你还没告诉Tomcat“要部署哪个项目”。切到Deployment选项卡点加号 → Artifact → 选择带war exploded字样的选项。这里我想多说一句war和war exploded的区别。war exploded可以理解成“已解压的war”IDEA会把target/classes、webapp等资源拼成一个目录结构直接给Tomcat加载好处是改代码之后可以支持热更新调试非常方便。完整war则是把一个压缩包丢给Tomcat适合最终交付部署。开发阶段选war exploded就对了。选好Artifact之后还要设置Application context。我建议直接把/servlet_demo_war_exploded这种长后缀改成/或者也填/hello取决于你想用什么根路径访问。如果你设成/那么最终的访问地址就是http://localhost:8080/hello省去一层context的干扰对初学者最友好。5.4 第一次启动看到什么才算成功点右上角绿色的运行按钮IDEA下方会弹出Run窗口里面会唰唰刷出一片Tomcat启动日志。看到类似“Server startup”字样就说明Tomcat已经起来了。这时候打开浏览器访问http://localhost:8080/hello如果一切正常页面会显示“第一个 Servlet 跑通了”。如果页面直接404先别急着查代码大概率是下面这三件事之一启动时IDEA报错端口8080被其他程序占用换到8081之后你却还在访问8080。Deployment里没有添加ArtifactTomcat根本没有加载你的项目。java目录没有标记为Sources RootServlet类没有被编译出来。我会在后面的排查部分再展开细讲。第一次跑通的时候建议多花两分钟点击IDEA运行窗口里的日志找到类似Deployment of web application这样的行能让自己直观地感受到“项目是被Tomcat加载的”。6. 从浏览器到Servlet一次请求的完整旅程6.1 做一个最简单的表单页面光能访问Servlet还不够我们来做一次真实的数据交互。把maven-archetype-webapp自动生成的index.jsp删掉在src/main/webapp下新建一个index.html内容如下!DOCTYPE html html langzh-CN head meta charsetUTF-8 title第一个Servlet表单/title /head body form actionhello methodpost label forusername用户名/label input typetext idusername nameusername placeholder请输入用户名 input typesubmit value提交 /form /body /html注意表单里的actionhello当浏览器地址是http://localhost:8080/时它会相对拼接成http://localhost:8080/hello。这是一种相对路径写法简单但依赖当前请求地址。如果你想绝对稳妥直接写actionhttp://localhost:8080/hello或action/hello也行。6.2 在Servlet里接收参数并回显修改HelloServlet的doPost让它读取表单携带的参数Override protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { request.setCharacterEncoding(UTF-8); String username request.getParameter(username); response.setContentType(text/html;charsetUTF-8); PrintWriter writer response.getWriter(); writer.println(html); writer.println(body); writer.println(h1收到参数 username /h1); writer.println(/body); writer.println(/html); }现在启动Tomcat先访问http://localhost:8080/看看表单再提交。这样一来从浏览器发起HTTP请求、表单数据以POST方式传输、Servlet通过getParameter拿到参数、再拼接成HTML返回整条链路就被打通了。6.3 后台日志帮你看到请求来了再往源码里加一行System.out.println(收到提交的用户名: username);重启Tomcat后再提交一次表单IDEA的Run窗口里会打印出这一行。这一步对排查问题特别重要如果你在页面看到了内容说明请求已经到达Servlet如果你什么都没看到就说明请求可能就没有转发到这个类排查方向会完全不同。7. 新手最容易踩的坑404、依赖失效、结构错乱7.1 “程序包javax.servlet不存在”的完整排查链路这个报错太经典了几乎每个新手都见过。我先描述一下你遇到的场景写完Servlet类IDEA的编辑器可能不报错但一编译就冒出“程序包javax.servlet不存在”。遇到这个提示按下面这条链路走很快能找到原因。打开项目根目录的pom.xml确认里面有没有我上面写的Servlet依赖。如果没有复制进去。看IDEA右侧的Maven面板展开Dependencies检查javax.servlet-api:jar:3.1.0是否出现在列表里。如果没有点面板顶部的刷新图标让IDEA重新读pom.xml并下载依赖。如果出现了依赖但编译仍然报错到File → Project Structure → Libraries里检查有没有Maven引入的jar包。确认pom.xml里scope是provided而不是系统缺失或空值。空值默认是compile虽然也能编译但和打包部署有关的语义会不太一样建议按标准写provided。实在不行File → Invalidate Caches / Restart清掉IDEA的索引缓存。这个操作经常解决一些“明明什么都没改突然就编译失败”的玄学问题。这一步要特别提醒刷新Maven依赖和清缓存是两个不同层面的操作。前者是做增量更新后者是推倒缓存重建。遇到提示“Cannot resolve javax.servlet-api:3.1.0”大概率是Maven仓库没有这个包而不是IDEA的问题。7.2 页面404的三种情况以及对症处理Tomcat启动成功但浏览器访问/hello返回404大部分原因可以归到这三类现象原因处理地址访问后出现Tomcat默认404页Servlet路径没注册上检查WebServlet注解路径是否以/开头访问根路径没反应没有部署Artifact或context没有设置对Edit Configurations → Deployment → 添加war exploded提交表单后404表单action路径与Servlet映射不匹配检查index.html里的action是否写成hello还是/hello其中最容易忽略的就是目录标记问题如果你src/main/java没有Mark Directory as Sources RootIDEA编译时不会把HelloServlet.class放进target/classesTomcat运行时自然找不到这个类。表现出来就是Tomcat启动正常但访问路径404。我在前面强调过这一步这里再说一遍因为实际遇到的人真的不少。7.3 war exploded和war开发阶段别选错很多教程会直接让你在Deployment里选war我建议你在IDEA中选war exploded。两者的区别我整理成一张表对比维度warwar exploded部署方式压缩包解压文件目录修改后生效需要重新打包配合IDEA设置可热更新调试体验较差较好最终交付适合不适合开发阶段选war exploded然后还可以在Edit Configurations中把On frame deactivation设置为Update resources这样你的HTML、图片等静态资源改完后IDEA会自动同步省去手动重启的工夫。如果想偷懒更彻底还可以在Update action下拉框里选择Redeploy或Update classes and resources但这部分设置和IDEA版本有关如果你找得到就顺手配上找不到也不影响核心实验。7.4 我的一个小技巧用Maven打包来验证一切当你觉得项目配置乱成一团或者搞不清楚到底是IDEA自动编译出了问题还是Maven配置出了问题可以直接在IDEA右侧的Maven面板里双击package生命周期或者到项目根目录执行命令行mvn clean package执行完之后去target目录看看有没有生成servlet-demo.war。如果war包能正常打出来说明你的pom.xml、目录结构、依赖声明都没有大问题剩下就是IDEA运行配置层面的问题如果打包过程报错比如提示找不到javax.servlet那问题一定出在依赖或者目录标记这一块。用这个办法能把“项目有问题”和“IDEA配置有问题”这两件事一刀切开。我在实际开发中还有一个体会以后你学Spring Boot时会发现Servlet这个概念被Spring的DispatcherServlet包在后面了但如果你当初手写过第一个Servlet理解了doGet和doPost、URL映射、HTTP请求和响应的交互那么看Spring MVC的源码时会轻松很多。基础这一步绕不过去也别嫌慢慢走一遍比快抄十遍有价值。

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

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

免费获取报价 →
↑