1. 项目整体思路与关键选择
1.1 这一篇到底要解决什么问题
Java Web这个词,很多新手第一反应是“要学一堆框架”,但实际走一遍会发现,最劝退的往往不是语法,而是“环境怎么搭起来”。我见过不少朋友在B站看教程,视频里三分钟敲完一个Hello World,自己装JDK、配置Tomcat、导入依赖却折腾一整天,最后连个报错都看不懂。这篇实践记录想做的,就是把你按在一张椅子上,手把手从零走完Java Web环境搭建的全流程,再亲手创建一个最简单的Web项目,让它真正跑起来。
文章会覆盖三个主要内容:一是JDK、Maven、Tomcat这些核心组件的安装和配置逻辑;二是用IDEA创建并运行一个传统Servlet项目;三是从传统方式平滑过渡到Spring Boot,理解两者之间的关系。整个过程不需要你有框架基础,但至少你在Java语法上已经会写简单的类和方法,知道什么是main方法、什么是接口。这样整个环境搭建才有意义——否则工具装好了,写不出代码,还是会卡在下一关。
1.2 技术选型:先学传统Servlet,再学Spring Boot
很多人会直接问:现在企业都在用Spring Boot,我为什么还要先搭Servlet环境,多绕一段路?我的回答是:绕路反而更快。
传统Servlet是Java Web的底层标准,Spring Boot本质上还是在用Servlet容器来处理HTTP请求,只是把配置方式简化了,把Tomcat嵌进去了,把各种组件自动装配起来了。如果你连一个Servlet怎么被请求、怎么返回响应都不清楚,直接去学Spring Boot的@Controller,会感觉像魔法——你调通了接口,但不知道是谁在背后调你的方法,更不知道URL是怎么映射到代码的。而先手写一次Servlet部署到Tomcat,你会非常直观地看到:浏览器发请求到端口,Tomcat把请求交给Web应用里的类,方法执行完把字符串写回响应。这个心智模型一旦建立,后面学任何Web框架都会快很多。
所以在环境搭建阶段,我的建议是使用一套能同时支持传统Servlet和Spring Boot的组合:JDK 17 + Maven 3.9 + Tomcat 10 + IntelliJ IDEA Community版(社区版免费,足够我们用)。如果你的电脑配置不高,IDEA用2022版或者2023版也没问题,关键是JDK版本要和后面要装的Tomcat版本配套。这个版本搭配并不是唯一的,但我在实践里验证过,放在2025年依然稳定。
1.3 环境组件版本搭配的逻辑
很多新手喜欢直接装最新版,结果打开IDEA发现提示“JDK版本不兼容”,或者Tomcat一启动就报UnsupportedClassVersionError。其实版本搭配有明确的逻辑:JDK的版本决定了编译出的class文件格式,Tomcat和之前用的Java代码必须运行在支持这个class版本的JVM上。
我选择的方案是:JDK 17,这是目前长期支持版本中最稳妥的,语法特性也够新,很多公司已经迁移到17或者21。Tomcat选择10.1.x,因为从Tomcat 10开始,Servlet API的包名从javax.servlet变成了jakarta.servlet,这是很多旧教程里报错“找不到javax.servlet”的根源。Maven则用3.9.x,它兼容JDK 8到21,不会踩版本坑。IDEA用2023.2以上,社区版就能支持Maven和Tomcat集成。
如果你手里的电脑已经装了JDK 8,也不是不能用,但建议安装Tomcat 9而不是Tomcat 10,否则包名对不上。如果你用Spring Boot 3.x,则必须JDK 17以上。下面我会按JDK 17 + Tomcat 10的路线展开,每一步都会把配置原因讲清楚。
2. 环境搭建实操:从JDK到Maven再到IDEA
2.1 JDK安装与JAVA_HOME配置,别再双击之后就完事
安装JDK不复杂,但很多人栽在环境变量上。我习惯从Oracle官网或者Adoptium下载MSI安装包,双击完事。安装路径尽量别带空格外加中文,比如C:\Program Files\Java\jdk-17这种是允许的,但如果你自己改到D:\Java Dev\jdk-17,后面命令行用空格容易遇到一些工具解析问题,建议就直接用默认路径或者简单的D:\Java\jdk-17。
安装之后必须配置三个环境变量:JAVA_HOME指向JDK根目录,PATH里加上%JAVA_HOME%\bin,确认没有多余的空格。很多人只配了PATH没有配JAVA_HOME,后面跑Maven或者某些脚本时会找不到JDK,因为很多工具是通过JAVA_HOME去定位Java的。
配置完不要急着开心,打开命令行窗口输入java -version和javac -version。如果出现java命令能用但javac提示找不到,说明PATH里的%JAVA_HOME%\bin没有生效,检查一下你是不是只改了用户变量而命令行还沿用着之前打开的环境变量窗口。这种情况我遇到过至少三次,解决方法很简单:关闭所有命令行窗口重新开一个。Windows的环境变量变更不会实时同步到已运行的进程里。
2.2 Maven下载与本地仓库配置,直接决定你后面会不会想摔键盘
Maven是Java项目里管理依赖、编译打包的工具。简单理解:以前你要自己下载jar包手动丢到lib目录,还要担心版本冲突;Maven用一个pom.xml文件描述需要哪些依赖,然后自动从中央仓库下载,统一管理。
从Apache官网下载Maven 3.9.x版本的apache-maven-3.9.6-bin.zip,解压到D:\Maven\apache-maven-3.9.6。同样配置MAVEN_HOME指向这个目录,PATH加%MAVEN_HOME%\bin。命令行输入mvn -v验证版本。
这里必须强调一个配置:本地仓库。Maven默认会把下载的依赖放到C:\Users\你的用户名\.m2\repository,如果C盘空间紧张或者系统重装,这些依赖就全没了。我在D:\MavenRepository建了个文件夹,然后编辑Maven安装目录下conf\settings.xml,找到<localRepository>标签,把注释取消,把内容改成<localRepository>D:/MavenRepository</localRepository>。这个路径建议用正斜杠,Windows下反斜杠会转义出问题。
另外强烈建议把Maven镜像换成阿里云或腾讯云的镜像仓库。原版中央仓库在国外,国内下载Spring、MyBatis这类依赖经常卡在原地,而换镜像后基本秒下。在settings.xml的<mirrors>节点里加入一个镜像配置,mirrorOf写central,url填https://maven.aliyun.com/repository/public。这一步能救你的命,后面创建Spring Boot项目时深有体会。
2.3 IntelliJ IDEA安装与永久版破解的误区
这里必须说一句:如果你只是想学习,IDEA Community版完全够用。你可能会听说社区版不支持Java Web开发,那是老黄历了。现在社区版支持Maven、支持Tomcat集成,支持创建普通Java项目,只是缺少一些企业特性比如Spring Initializr的图形化创建方式。但我们可以用在线方式创建Spring Boot项目,后面我会讲。
去JetBrains官网下载Community版安装包。安装时注意勾选“创建桌面快捷方式”和“添加到PATH”。启动后你会看到欢迎界面,直接选择“New Project”建一个空Java项目,或者用IDEA打开刚才解压的代码目录。
很多人被“破解”两个字引流到各种论坛,到处看激活码,其实完全没有必要。社区版免费开源,功能对于入门绰绰有余。把精力花在代码上,别浪费在工具激活上,这是我这几年最大的感悟。
2.4 为什么Tomcat要单独装,而不是直接在IDEA里下载
Tomcat是Servlet容器,可以把它理解为一个专门跑Java Web应用的“小服务器”。IDEA本身不带Tomcat,需要我们自己下载并且配置好。可以去Apache Tomcat官网下载apache-tomcat-10.1.20-windows-x64.zip,解压到D:\Tomcat\apache-tomcat-10.1.20。不需要配置环境变量,IDEA里直接指定目录就行。
这里有一个冷知识:Tomcat启动前必须确保JDK环境正常,因为Tomcat本身是一个Java程序,它的startup.bat脚本会调用java命令。如果你前面JAVA_HOME没配好,Tomcat双击启动会闪退,或者报“Neither the JAVA_HOME nor the JRE_HOME environment variable is defined”。所以配置顺序一定是先JDK,再Tomcat,遇到问题先回头看JDK是否正常。
3. 第一个Web项目:从结构到让浏览器显示一串文字
3.1 手动创建标准Java Web目录结构
很多教程会让你用IDEA的“Java Enterprise”模板创建Web项目,但模板会帮你生成一堆不认识的代码,反而掩盖了Web应用的本质。我建议先手动建目录,自己体会标准结构。
在IDEA里新建一个普通Java项目,项目名MyFirstWeb。然后在src/main下新建java目录,再新建webapp目录。在webapp下新建WEB-INF文件夹,然后创建web.xml。这里有一个容易错的地方:WEB-INF和web.xml是Web应用必须有的配置目录,相当于Web应用的“注册表”,Tomcat启动时会先去这里读取配置。
最终结构应该是:
MyFirstWeb ├── pom.xml ├── src │ └── main │ ├── java │ │ └── com.example.web │ │ └── HelloServlet.java │ └── webapp │ ├── WEB-INF │ │ └── web.xml │ └── index.html我们先用Maven管理项目,所以pom.xml要声明打包方式是war,因为Web应用需要打包成war格式部署到Tomcat。在pom.xml里加上Servlet API依赖,注意依赖的scope是provided——因为Tomcat本身已经自带Servlet API,如果你打成war再带一份,反而会产生类冲突。
3.2 编写一个最简单的Servlet,把HTTP响应写出去
新建HelloServlet.java,代码如下:
package com.example.web; import jakarta.servlet.ServletException; import jakarta.servlet.annotation.WebServlet; import jakarta.servlet.http.HttpServlet; import jakarta.servlet.http.HttpServletRequest; import jakarta.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;charset=UTF-8"); PrintWriter out = resp.getWriter(); out.println("<html><body><h1>Hello, Java Web!</h1>"); out.println("<p>这是我手动创建的第一个Servlet。</p></body></html>"); } }注意包名是jakarta.servlet,不是javax.servlet,如果你用的是Tomcat 9,就要改成javax.servlet。这一个单词的差别,能让你抓狂半小时。@WebServlet注解可以直接把URL映射到/hello,不需要在XML里配置映射。如果你好奇传统写法,我一会儿在FAQ里说。
doGet方法负责处理HTTP GET请求。这里resp.setContentType("text/html;charset=UTF-8")必须写,否则浏览器默认用ISO-8859-1编码解析页面,中文就会变乱码。这是新手最容易忽略的问题之一。
同时改一下index.html,随便放一张简单页面,用来验证静态资源。这样你的Web应用既有静态页面,也有动态Servlet响应,一个项目把两种请求都演示了。
3.3 在IDEA里配置Tomcat,并解决“没有Artifact”的问题
在IDEA中配置Tomcat有两种方式:一种是直接使用Tomcat插件,另一种是通过Maven的tomcat7-maven-plugin。后者虽然是老办法,但不需要额外配置,而且社区版也支持。我这里推荐用IDEA外置Tomcat的方式,因为更贴近实际部署流程。
点击顶部菜单Run->Edit Configurations,点左上角+,选择Tomcat Server->Local。注意,如果你没有看到Tomcat Server选项,说明你的IDEA没安装“Java Web”这个组件。解决方法是:在IDEA的File -> Settings -> Plugins里搜索“Java EE”插件,勾选安装并重启IDEA。
然后在Application server下拉框右侧点Configure,选择你的Tomcat目录。在Deployment标签页点+添加“Artifact”。如果Artifact列表是空的,是因为IDEA还没识别你的项目为Web项目。此时需要右键你的项目,选择Add Framework Support,勾选Web Application,然后确认Web resource directory指向了src/main/webapp。这一步很容易漏,漏了就找不到Artifact。
配置好后,Deployment里会多出一个MyFirstWeb:war exploded条目,这表示以解压目录方式部署,好处是修改代码后不用重新打包,IDEA会直接把变化同步到Tomcat的工作目录。之后再设置Application context为/myweb,这样访问地址就是http://localhost:8080/myweb/hello。
具体参数如下:
| 配置项 | 值 |
|---|---|
| Tomcat版本 | 10.1.20 |
| JDK版本 | 17 |
| Application context | /myweb |
| HTTP port | 8080 |
| 打开浏览器 | 不勾选(避免每次自动弹窗口) |
| 更新资源时的操作 | Update resources |
| 框架框架类更新时 | Restart server(热部署不成熟) |
3.4 启动Tomcat总报错?先看这些细节
点击右上角绿色运行按钮后,控制台会出现一堆日志,但很多新手看到的是一堆红色异常。最常见的无非三种:一是端口被占用,二是加载不了jakarta.servlet,三是不知名重启。
如果是端口占用,日志会写Port 8080 was already in use,这时你需要找到谁占用了8080端口。打开命令行输入netstat -ano | findstr 8080,显示一个PID和进程名,然后去任务管理器把这个进程结束。我之前遇到是某个虚拟机的宿主机服务占用,关掉就好。
如果是ClassNotFoundException: jakarta.servlet.http.HttpServlet,说明你的pom.xml里没加Servlet API依赖,或者加了但scope写成了compile。编译期需要依赖,运行时却可能被Tomcat自带类覆盖,所以要把scope改成provided。
如果是Context [] startup failed的通用报错,点开日志上面的异常堆栈,看最开头的Caused by,大部分问题都藏在里面。别被一大篇日志吓住,定位根因的思路是:先看Caused by后面的第一行,再看它指向哪个文件哪一行。
4. 从传统Servlet到Spring Boot的过渡
4.1 理解Spring Boot到底解决什么问题
传统Servlet项目其实已经把Web应用的原理讲清楚了:请求进来,Tomcat解析,分发到Servlet,写完响应返回。但你会发现,真要开发一个生产级项目,要写太多琐碎的配置:一堆servlet映射、过滤器、监听器,还要自己管理数据库连接池、事务。Spring Boot的思路就是“约定大于配置”,它把这套基础设施全自动装配好了。
比如你用Spring Boot创建Web项目,只要启动一个main方法,里面自动内嵌了Tomcat,不用再部署war包。写一个接口只要一个@RestController加@GetMapping,就能直接返回JSON。对新人来说,这看起来更简单,但如果没有前面手动配置Tomcat的经验,你可能根本不知道那个内嵌的Tomcat是干嘛的,出了错也无从下手。
所以我的建议是:先让传统Servlet痛一次,体验一下手动配置的麻烦,再上手Spring Boot,你才能真正理解它的价值。这个顺序不能倒。
4.2 快速创建一个Spring Boot Web项目
如果你用的是社区版IDEA,没有Spring Initializr按钮,可以直接去https://start.spring.io网页上在线生成了一个项目压缩包。选择Web依赖,其他默认,然后下载解压,用IDEA打开即可。如果你用的专业版IDEA,直接在New Project里点Spring Initializr就行。
项目生成后,找到DemoApplication.java,里面有一个main方法。然后在同包下新建一个控制器:
package com.example.demo; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RestController; @RestController public class HelloController { @GetMapping("/hello") public String hello() { return "Hello from Spring Boot!"; } }直接运行DemoApplication的main方法,看到Started DemoApplication之后,浏览器访问http://localhost:8080/hello,就能看到你返回的字符串。这个过程里没有Tomcat外置目录,没有war包,没有部署步骤——因为内嵌的Tomcat已经被Spring Boot自动启动了。
热部署方面,Spring Boot的模板引擎(如Thymeleaf)可以在开发时自动刷新,但如果你只是改了普通Java方法,默认要重启才能生效。如果你想省事,可以加一个spring-boot-devtools依赖,改完代码后它会自动重启,但重启也需要几秒。我个人建议新手先不加这个,因为你刚学会跑通流程,频繁重启反而能让你养成“改代码必重启”的意识。
4.3 两种方式的区别对照表
| 项目对比 | 传统Servlet + Tomcat | Spring Boot |
|---|---|---|
| Tomcat来源 | 外置安装,需手动配置 | 内嵌在应用里,直接启动 |
| 配置方式 | web.xml + 注解 | application.properties + 自动装配 |
| 打包部署 | 打包成war放到Tomcat/webapps | 打包成jar直接java -jar |
| 学习难度 | 偏向底层原理,配置繁琐 | 快速上手,但底层被隐藏 |
| 适合场景 | 理解原理、老项目维护 | 现代企业项目开发主流 |
这张表并不表示谁好谁坏,而是说它们是同一个问题在不同阶段的解答。入门时走左边的路,进入实际开发时走右边的路,两条路你都走过,心里才不会有黑盒。
5. 我踩过的坑:环境搭建与部署的七宗罪
5.1 端口占用频繁踩雷
Tomcat默认端口8080,很多开发工具像phpstudy、nginx、还有其它微服务也会占用8080。如果你确认没有别的程序,但端口还是被占用,可以修改IDEA里Tomcat配置的HTTP port,比如改成8081,注意改完之后访问地址也要变。另外Tomcat的日志里第一次启动成功后,会有一行Tomcat started on port 8080,如果没看到这行,基本就是启动失败。
还有一种隐性情况:你用IDEA多次运行同一个Tomcat配置,之前没关闭实例还残留在后台。这种时候任务管理器里能看到多个java.exe,把它们全部结束再重新启动。
5.2 中文乱码,从控制台到页面一次到位
乱码有几种来源。控制台乱码通常是IDEA的编码设置问题:File -> Settings -> Editor -> File Encodings,把Global Encoding、Project Encoding、Default encoding for properties files全部设为UTF-8。另外在Help -> Edit Custom VM Options里加一行-Dfile.encoding=UTF-8,重启IDEA,控制台中文就能正常显示。
页面乱码通常是resp.setContentType没设置,或者HTML文件的charset没写。Tomcat 10下,请求参数的编码也需要注意,在过滤器中设置req.setCharacterEncoding("UTF-8")。但作为入门,建议先确保页面响应正确,具体参数乱码等用到表单再深入研究。
命令行下用mvn clean package打包时如果发现乱码,可以在pom.xml里给maven-compiler-plugin配置<encoding>UTF-8</encoding>,这样编译时读源码也会用UTF-8,避免因为操作系统默认编码不同导致的编译期乱码。
5.3 Maven依赖下载龟速甚至失败
前面提到的阿里云镜像基本能解决90%的下载问题。如果还是失败,常见原因有:本地仓库里残留了损坏的jar包,Maven不会自动重新下载。比如依赖下载到一半断了,留下一个.lastUpdated后缀文件。解决方式是在IDEA的Maven工具窗口里点Reload All Maven Projects,如果还失败,就手动删掉本地仓库对应目录,再重新加载。这个方法比自己百度“Maven下载失败”然后复制各种帖子有效得多。
另外,Maven在解析依赖时会把中央仓库的索引下载到本地,如果网络不稳定会导致索引更新失败。这时候可以在IDEA里取消“Always update snapshots”之类的设置,减少索引请求频率。
5.4 修改代码后页面不更新,热部署到底怎么开
传统Servlet + Tomcat在IDEA里默认是“更新资源和类”时,如果配置成Update resources,那么改HTML和CSS会实时生效;但改Java类,需要重启Tomcat,除非你配置成“Update classes and resources”并且使用JRebel之类的热部署插件。对新手来说,最稳妥的方案是:每次都停掉Tomcat再重启,反正项目小,启动只要两三秒。别为了省几秒搞来一堆工具,反而容易出问题。
Spring Boot更智能一点,只要加了spring-boot-devtools,在IDEA里开启“Build project automatically”,改完代码再切回浏览器,应用会自动重启。但注意,DevTools的重启不是热部署,它本质上是自动重启JVM,只是不用你手动点。它跟JRebel这类真正的热加载不一样,别指望改一个方法签名不重启就能生效。
5.5 小问题速查表
| 现象 | 可能原因 | 解决办法 |
|---|---|---|
Neither the JAVA_HOME nor the JRE_HOME | JDK环境变量未配置 | 配置JAVA_HOME并重启命令行 |
UnsupportedClassVersionError | JDK版本和Tomcat/编译版本不匹配 | 保证JDK 17与Tomcat 10对应 |
NoClassDefFoundError: jakarta/servlet | 缺Servlet API依赖或作用域不对 | 添加jakarta.servlet-api依赖,scope为provided |
Address already in use: 8080 | 端口被占用 | 结束占用进程或修改端口 |
Failed to execute goal maven-compiler-plugin | 源码编码或JDK版本问题 | 设置UTF-8,检查maven-compiler-plugin的source/target |
| 页面全白,日志无异常 | 启动成功但请求路径不对 | 确认Application context和Servlet映射路径是否一致 |
这些坑不只出现在入门阶段,哪怕你工作两三年,依然可能在环境切换时碰到。最重要的是学会看日志和定位Caused by,而不是复制报错全文到搜索引擎。工具是死的,排查思路才是活的东西。
6. 最后分享一点我的实操体会
环境搭建这件事,看起来很low,但它筛选掉了大多数人。很多人学Java Web失败,不是在算法题上,也不是在复杂的并发编程上,而是卡在“Tomcat起不来”和“依赖下载不下来”这种最基础的环境问题上,然后心态崩了,觉得自己不适合编程。我在带人入门时反复强调一句:环境问题只有一个解法——拆解步骤,不要跳步。装JDK就只想着JDK,别一边装一边焦虑后面怎么办。一个组件装完了,验证一个组件;一份依赖出错,就删掉重新下载。看似慢,其实是最快的路。
我自己当年第一次跑Servlet时也满屏报错,最后发现是javax和jakarta少打了一个字母。那种沮丧感很真实,但当你把整个链路走通,并亲手把同一套代码从IDEA部署到外置Tomcat,再从Tomcat迁移到Spring Boot之后,会发现自己对Web的理解已经完全不同。这套“从零到一”的路子,值得每个准备入行Java后端的人认真走一遍。别嫌它简单,地基打牢了,后面盖楼才不晃。