
接手一台新电脑或者重装完系统坐回工位的那一刻我最怕的不是项目代码看不懂而是打开 IDEA 之后那一堆红字找不到 JDK、Maven 依赖全是红线、Tomcat 点启动就报 404、数据库连接直接超时。IDEA、JDK、Tomcat、Maven、MySQL 这五样东西单独拎出来装任何一个都不难难的是让它们互相认识——IDEA 要知道用哪个 JDK 编译、Maven 要知道去哪个本地仓库找包、Tomcat 要知道读哪个版本的 Servlet 规范、MySQL 要知道用什么字符集和时区跟 Java 驱动握手。任何一个环节的版本没对齐都会表现在代码没问题但就是跑不起来上。这篇内容面向的是刚上手 Java Web 的开发者以及隔三差五就要给别人重装一遍环境的运维和老手。我会把整套流程按先定版本、再装组件、最后串联验收的顺序拆开讲重点放在那些教程里不写、但实际一定踩的坑上环境变量配了却没生效、Maven 镜像写对了却依然下不动、Tomcat 10 之后包名换了导致老项目全崩、MySQL 8 的认证插件把老驱动挡在门外。看完你至少能做到一件事下次再配环境不用一边翻教程一边试错照着逻辑走一遍就能通。1. 装之前先想清楚的三件事比装本身更重要1.1 为什么安装顺序是 JDK 在前、IDEA 在最后很多人习惯先把 IDEA 装好再回头补 JDK 和 Maven结果 IDEA 第一次启动时扫描不到任何 SDK弹出一堆向导让人手忙脚乱还容易在向导里随手点个自动下载最后机器上出现两三个来路不明的 JDK。我更推荐固定一条顺序JDK → Maven → MySQL → Tomcat → IDEA。这条顺序背后的逻辑很简单后面每一个组件都依赖前面的组件但它不依赖 IDEA。JDK 是根Maven 是靠 JAVA_HOME 跑起来的所以 Maven 必须等 JDK 环境变量生效之后再装Tomcat 虽然自带启动脚本、不强制要 JDK但它要跑 Web 应用就必须有 JDK放在 Maven 后面属于顺手MySQL 跟前面几个完全不耦合位置随意我放在中间是为了让它先跑起来等最后联调时直接就能连。IDEA 放最后是因为它是个消费者——它需要读 JDK 列表、读 Maven 的 settings.xml、读 Tomcat 的安装目录。前面都摆好了IDEA 里基本只需要点几下确认路径不用来回切窗口。提示如果你是在公司电脑上装先确认有没有内网私服和统一的 JDK 版本要求。装完再改本地仓库路径比一开始就配错要麻烦得多。1.2 版本搭配表先定组合再动手下载版本不是越新越好。我见过太多人一路 Next 装完最新的 JDK 21 Tomcat 11 MySQL 8.4然后拿一份几年前的教程项目导入结果 servlet 类全部飘红。下面这张表是我自己反复用过的几套稳定组合直接抄不会翻车使用场景JDKTomcatMavenMySQL说明学习/老项目维护88.5 或 9.03.8.85.7 或 8.0大量存量项目仍是 javax.servlet主流新项目179.03.9.x8.0目前最省心的组合尝鲜新特性2110.1 或 113.9.x8.0注意 jakarta.servlet 包名变更关键分界线有两条一条在 JDK8 到 11 是模块化带来的变化11 到 17 是 LTS 的稳定跨越另一条在 Tomcat9.0 及以前用 javax.servlet10.0 开始改成 jakarta.servlet。这两条线决定了你的项目能不能直接编译过。如果团队没有明确要求我一般推荐 JDK 17 Tomcat 9.0 Maven 3.9 MySQL 8.0这套组合的兼容面最宽网上能搜到的解决方案也最多。1.3 目录规划别塞进 Program Files也别碰中文路径这一步看起来无关紧要实际上是后面一半报错的源头。默认安装向导喜欢把 JDK 丢到C:\Program Files\Java\下面把 MySQL 丢到C:\Program Files\MySQL\把 Maven 本地仓库丢到C:\Users\你的中文用户名\.m2\。三个问题紧接着就来了第一Program Files中间有空格某些老脚本、老插件在做字符串拼接时不加引号路径直接被截断第二这些目录受系统权限保护配置文件和临时文件写入容易被拒第三中文用户名会让部分命令行工具在读取本地仓库路径时出现编码异常报错信息还特别隐晦只说找不到文件。我自己的习惯是统一放在一个纯英文、无空格的根目录下比如D:\dev\ ├── jdk\jdk-17 ├── maven\apache-maven-3.9.6 ├── m2repo\ ├── tomcat\apache-tomcat-9.0.x └── mysql\mysql-8.0.x好处不只是看起来整齐。以后你想换 JDK 版本只要新增一个jdk\jdk-21目录改一下 JAVA_HOME 就切过去了想清空 Maven 本地仓库直接删m2repo目录不会牵连到系统盘备份环境时把D:\dev整个打包拷走新机器上大部分路径都不用改。注意本地仓库目录不要放在会被同步软件网盘、云盘客户端实时监控的目录里。Maven 下载依赖时会创建大量小文件同步进程会锁住文件导致解压失败报错通常是 Could not transfer artifact 或者压缩包损坏。2. JDK环境变量配不对后面全是白干2.1 选哪个发行版官网打不开时去哪找JDK 现在不是一个唯一答案。商业版、开源社区版、各家厂商的长期支持版都能用常见的几个方向需要一个通用、社区维护活跃的版本可以考虑 Eclipse Temurin、Amazon Corretto 这类开源构建需要跟国内镜像源配合加速下载的用国内厂商维护的构建版会快很多公司有指定采购的按公司要求走。版本上我的建议是学习阶段直接上 LTS 版本别去碰非 LTS 的奇数版本。LTS 的补丁周期长、资料多、第三方库支持充分遇到问题搜一下就有答案非 LTS 版本生命周期只有半年等你项目写完了可能都停止维护了。安装形态上有两种一种是带安装向导的Windows 上是.msi或.exe一种是压缩包.zip或.tar.gz。我强烈建议用压缩包。原因有三个安装向导会把文件散落到系统目录、注册表和环境变量里卸载时未必干净向导版有时会自动往系统 Path 里塞一个自己的软链接目录和你手动配的 JAVA_HOME 打架后面会细讲压缩包解压即用换版本就是换个目录出问题了直接删掉重来零残留。2.2 JAVA_HOME 和 Path 到底要配几个网上教程里那一长串变量名——JAVA_HOME、Path、CLASSPATH、JRE_HOME——建议只配两个JAVA_HOME 和 Path。具体操作以 Windows 为例图形界面路径此电脑右键 → 属性 → 高级系统设置 → 环境变量在系统变量里新建JAVA_HOME值填 JDK 的根目录注意是根目录不是bin目录例如D:\dev\jdk\jdk-17找到系统变量里的Path编辑新增一行%JAVA_HOME%\bin并且把它移动到列表最上方一路确定保存然后关掉所有已经打开的终端窗口重新开一个。第三点特别容易被忽略。环境变量是进程启动时从父进程继承的已经开着的 CMD 或 PowerShell 窗口不会自动刷新。我见过有人配完变量后一直在旧窗口里敲java -version看到还是老版本然后开始怀疑人生。至于 CLASSPATH很多老教程会让你配一堆路径比如.;%JAVA_HOME%\lib\dt.jar;%JAVA_HOME%\lib\tools.jar。这个在 JDK 9 以后就不需要了dt.jar和tools.jar在新版本里根本不存在配了反而会因为指向不存在的路径引发一些奇怪的类加载问题。除非你在维护一个明确要求 CLASSPATH 的极老项目否则直接跳过。还有一个容易踩的坑设置Path的时候%JAVA_HOME%\bin这一行不要加引号、不要加结尾的分号、不要在值里写完整路径又留了分号。带引号在某些解析场景下会被当成路径的一部分结果就是明明目录存在却说找不到命令。2.3 javac 不是内部或外部命令一条完整排查链路这个报错我帮人处理过不下二十次排查顺序基本可以固定下来第一步确认变量本身对不对。打开新的 CMD执行echo %JAVA_HOME%如果输出的不是你的 JDK 根目录说明变量名写错、写在了用户变量而当前账户读取顺序不同或者压根没保存成功。第二步确认 Path 里有没有生效。执行echo %Path%把输出内容从头看到尾找有没有%JAVA_HOME%\bin。注意这里是变量引用如果它被展开成实际路径了也能用但如果你看到的是字面量%JAVA_HOME%\bin一字不差地待在那里说明它前面那个JAVA_HOME变量没被识别多半是变量名拼错了。第三步看系统到底找到了哪个 java。这一步是分水岭where java where javac正常的输出应该指向D:\dev\jdk\jdk-17\bin\java.exe。如果你看到排在第一位的是这样一个路径C:\Program Files\Common Files\Oracle\Java\javapath\java.exe那问题就找到了。这个目录是某些 JDK 或 JRE 安装包自动塞进系统 Path 的软链接目录而且它的优先级往往排在你手动加的那行前面。系统先命中了它你配的 JAVA_HOME 就形同虚设。处理办法很简单在系统变量 Path 里把这个javapath的条目删掉或者往下移确保%JAVA_HOME%\bin在最上面。第四步如果 where 找到的是正确路径但javac还是报错。那说明你装的可能只是 JRE 而不是 JDK。JRE 只有java.exe没有javac.exe。回头看看你解压的目录里有没有bin\javac.exe没有就重新下载 JDK 包。第五步全都对但 IDEA 里还报找不到 JDK。这通常不是环境变量的问题而是 IDEA 的项目配置问题看下一节。2.4 IDEA 里的三层 SDK 结构别只配第一层很多人以为系统环境变量配好了IDEA 就自动用上了其实 IDEA 有自己独立的一套 SDK 配置跟系统环境变量是两条线。你需要认准三个层次层次位置作用Project SDKFile → Project Structure → Project整个项目默认用哪个 JDKLanguage Level同一个页面下方源码允许使用的语法特性级别Module SDK同一窗口 → Modules → Sources多模块项目里每个模块单独指定还有一个隐藏在 Maven 里的第四层Settings → Build Tools → Maven → Runner里的 JRE 选项以及Importing里的 JDK for importer。这两个如果没跟上你的 Project SDK会出现编译能过但运行报 UnsupportedClassVersionError这种诡异现象本质是用 JDK 8 编译出来的 class 想跑在 JDK 17 以外的地方或者反过来用了高版本 class 文件格式跑在低版本 JVM 上。经验报错信息里出现class file version 61.0这类字样时别去猜。对照一下也能记住52 是 JDK 855 是 JDK 1161 是 JDK 1765 是 JDK 21。看到版本号不匹配直接去查各层的 SDK 设置基本一次命中。3. Maven难点不在装而在三处路径对齐3.1 Maven 到底解决了什么问题刚接触时容易把 Maven 理解成一个下载 jar 包的工具这只是它最表层的功能。它实际解决的是三件事第一依赖管理。以前做 Web 项目你要手工去各个网站下 jar 包然后一股脑塞进WEB-INF/lib。A 依赖 B、B 又依赖 C 这种传递关系全靠人肉维护稍微升级一个包就要重新找一圈还经常出现同一个类存在两份不同版本的情况。第二统一的构建生命周期。clean、compile、test、package、install这一套阶段是标准化的不管换哪台机器、哪个 IDE执行的流程都一样。这也是为什么 CI 环境里几乎看不到手工打包。第三项目结构的约定。src/main/java、src/main/resources、src/test/java这套目录约定让所有人打开项目就知道代码该放哪。约定优于配置省下来的沟通成本非常可观。3.2 settings.xml 里我只改三处Maven 也推荐用压缩包解压到D:\dev\maven\apache-maven-3.9.6然后把MAVEN_HOME加到环境变量、把%MAVEN_HOME%\bin加进 Path。验证方式是mvn -v输出里会同时打印 Maven 版本和它认到的 Java 版本——这一步非常重要如果打印出来的 Java 版本不是你期望的那个说明 Maven 读到了错误的 JAVA_HOME后面所有编译都有隐患。接下来是核心配置文件settings.xml。它在两个位置存在——Maven 安装目录下的conf\settings.xml全局配置和用户目录下的.m2\settings.xml用户配置。用户配置优先级更高会覆盖全局配置。这就是为什么很多人改了安装目录下的文件却不生效他之前某次操作已经在用户目录下生成了一份。建议只维护一份我习惯改安装目录里的那份并明确指定 IDEA 使用它同时把用户目录下的那份删掉避免混淆。这份文件里我只改三处第一处本地仓库路径。在settings下加localRepositoryD:\dev\m2repo/localRepository第二处镜像仓库。找到mirrors节点加一个镜像mirror idaliyun-public/id mirrorOf*/mirrorOf namealiyun public/name urlhttps://maven.aliyun.com/repository/public/url /mirror这里有两个高频坑。一是URL 必须用 https——Maven 3.8.1 之后默认禁止通过 http 访问外部仓库你写 http 会直接报 blocked mirror 或者干脆被忽略二是mirrorOf里不要同时写多个互相冲突的条目比如又有一条mirrorOf*/mirrorOf又有一条mirrorOfcentral/mirrorOfMaven 只会按第一个匹配的处理你以为配了两份保险实际只用了其中一条。第三处编译级别。在profiles里配置一个默认激活的 profile声明编译插件使用的 JDK 版本。或者更简单直接在项目 pom 里用属性声明这两种我会搭配使用具体在 3.5 节讲。注意改了 settings.xml 之后之前下载失败的依赖垃圾需要清理。本地仓库里会留有大量.lastUpdated后缀的文件它们标记了这次下载失败过Maven 在一段时间内不会重试。要么手动搜索删除这些文件要么执行带上-U参数的构建强制更新。3.3 IDEA 里三处路径必须对齐否则等于没配打开 IDEA 的Settings → Build, Execution, Deployment → Build Tools → Maven这个页面有三个输入框Maven home path默认值可能是 IDEA 自带的 Bundled (Maven 3)这是个大坑。用 IDEA 自带的 Maven你的MAVEN_HOME和手工安装的那份就完全不起作用了。改成你自己的安装目录。User settings file指向你真正在维护的那份 settings.xml并勾上右边的 Override 复选框。不勾的话IDEA 会用默认位置。Local repository正常情况下它会自动读取 settings.xml 里的配置如果这里显示的还是默认的.m2\repository说明 settings.xml 没被正确加载回到上一步检查。这三个值不一致时最典型的症状是命令行mvn clean package一切正常IDEA 里却依赖全红、提示找不到包。因为 IDEA 用的是自己那套配置去另一个仓库目录里找当然找不到。另外那个JDK for importer也要选成项目使用的 JDK。它的作用是在导入依赖、解析 pom 时用哪个 JVM选错了会在导入阶段就报错压根走不到编译。3.4 依赖拉不下来的几种真实情况和处理顺序遇到依赖变红按这个顺序排查效率最高现象大概率原因处理方式控制台长时间无输出后超时镜像源不通或网络受限换镜像地址或用浏览器访问仓库地址验证连通性报 blocked mirror / 拒绝明文传输镜像 URL 用了 http改成 https报找不到某个具体版本坐标写错或该版本不存在去仓库网站搜一下确认版本号报包损坏 zipfile is empty下载中断留下半成品文件删除对应目录重新下载命令行正常、IDEA 报错三处路径没对齐回到 3.3 逐个核对还有一种情况很隐蔽依赖其实下下来了但 IDEA 没刷新索引。习惯做法是点 Maven 面板里的刷新按钮或者右键 pom 文件选 Maven → Reload project。如果还不行执行File → Invalidate Caches重建索引。这个操作治百病但每次都要重建耗时较长所以只在确认配置没问题时才用。3.5 编译级别到底听谁的pom.xml里的编译级别、IDEA 项目设置里的 Language Level、以及 Maven 编译插件实际用的 JDK这三者如果打架会出现IDEA 里不报错但打包失败或者反过来。我现在的做法是在 pom 里统一声明properties maven.compiler.release17/maven.compiler.release project.build.sourceEncodingUTF-8/project.build.sourceEncoding /properties用release而不是老的source/target组合是因为release参数会同时约束字节码版本和可用的 API 集合能防止你在 JDK 17 下用了 JDK 21 才有的 API、结果运行时才炸。再加上project.build.sourceEncoding声明 UTF-8能避免一类很烦人的问题本地用 UTF-8 写的代码在默认 GBK 的机器上编译出来中文全变乱码。4. MySQL装完能连上只是及格线4.1 安装版还是解压版MySQL 在 Windows 上有两种装法官方安装器Installer和免安装压缩包。安装器胜在省事能一路点下去还能顺手装 Workbench 可视化客户端压缩包胜在干净可控配置全在一个my.ini文件里出了问题好定位。我给新手的建议是第一次装用安装器因为你需要一个能跑起来的环境先找到感觉等你第二三次重装或者要在服务器上部署时再转压缩包。安装器里有个环节要注意在选择安装类型时不要选 Developer Default它会顺带装一堆你可能永远用不到的东西还占服务端口。选 Custom 只勾 MySQL Server 和 MySQL Workbench 就够了。如果用压缩包流程是解压到D:\dev\mysql\mysql-8.0.x手动创建my.ini用管理员身份执行mysqld --initialize-insecure不设密码初始化或--initialize生成随机密码在日志里找然后mysqld --install注册成服务再net start mysql启动。4.2 字符集、时区、认证插件三个必改项字符集默认字符集务必确认是utf8mb4。老版本里那个utf8其实是最多三个字节的 UTF-8存不了 emoji 和部分生僻字插入时报 Incorrect string value。改法是在my.ini的[mysqld]段加上[mysqld] character-set-serverutf8mb4 collation-serverutf8mb4_0900_ai_ci在建库建表时再显式声明一次双保险CREATE DATABASE demo DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci;时区这是 Java 连接 MySQL 时最常见的报错来源。服务端时区、操作系统时区、JDBC 连接串里声明的时区三者不一致就会报 The server time zone value is unrecognized 或者你存进去的时间跟取出来的差 8 小时。最稳的做法是在连接串里明确写死jdbc:mysql://127.0.0.1:3306/demo?useUnicodetruecharacterEncodingutf8useSSLfalseserverTimezoneAsia/ShanghaiallowPublicKeyRetrievaltrueuseSSLfalse是为了避免本地开发时反复弹 SSL 警告allowPublicKeyRetrievaltrue是配合下面要说的认证插件用的。认证插件MySQL 8.0 默认使用caching_sha2_password而一些老版本的 JDBC 驱动只认识mysql_native_password连接时会报 Unable to load authentication plugin。两条路要么升级驱动到 8.0 以后的版本推荐要么把账号的认证方式改回去ALTER USER app% IDENTIFIED WITH mysql_native_password BY 你的密码; FLUSH PRIVILEGES;我更推荐升级驱动因为改认证方式属于降级兼容新装的库没必要为老驱动让步。4.3 建库建账号别再到处用 root开发阶段用 root 图省事但把 root 密码写进项目配置文件、提交到版本库是实打实的安全隐患。我现在的习惯是每个项目单独建库、单独建账号只给这个库需要的权限CREATE USER demo_app% IDENTIFIED BY 一个足够长的密码; GRANT SELECT, INSERT, UPDATE, DELETE ON demo.* TO demo_app%; FLUSH PRIVILEGES;注意这里没有给DROP、ALTER、CREATE权限。开发阶段如果需要频繁改表结构可以临时加上上线前一定收回来。这个习惯的价值不在当下而在于它能防止一类事故代码里一个手滑的删表操作因为权限不足直接失败而不是真的把数据删了。4.4 连不上数据库时的排查顺序按这个顺序走基本五分钟内能定位服务在不在。Windows 下执行net start | findstr /i mysql或者去服务管理器看 MySQL 服务状态。服务没起后面都别谈。端口通不通。netstat -ano | findstr 3306。如果 3306 没被监听回去看服务日志数据目录下的.err文件大概率是初始化失败或my.ini里datadir路径写错。端口被谁占了。如果 3306 被别的进程占用、MySQL 起不来可以改端口也可以处理掉占用进程。改端口后记得同步改连接串。账号能不能登。用命令行客户端mysql -u demo_app -p -h 127.0.0.1试一下。命令行能进、Java 连不上问题就在驱动或连接串。驱动版本对不对。MySQL 8 的服务端配 5.x 的驱动十有八九出问题。pom 里统一用mysql-connector-j的新版本。连接串参数全不全。时区、编码、SSL、公钥检索这四项缺一项就可能有报错。4.5 忘了 root 密码时的自救流程这个场景很常见尤其是隔了几个月回来接着做项目。标准做法是临时跳过权限校验启动停掉 MySQL 服务用mysqld --skip-grant-tables --console方式启动此时不需要密码即可连接用命令行客户端连进去执行FLUSH PRIVILEGES;刷新权限表然后ALTER USER rootlocalhost IDENTIFIED BY 新密码;关掉这个进程正常启动服务用新密码登录。注意--skip-grant-tables期间任何本地进程都能无密码连入操作时确保这台机器没有对外暴露数据库端口改完立刻恢复正常启动。5. Tomcat那道 javax 与 jakarta 的墙5.1 版本选型比安装重要得多Tomcat 的安装本身几乎没有技术含量——下载 zip、解压、完事。真正的分水岭在版本选择上Tomcat 9.0 及以前Servlet API 的包名是javax.servlet从 Tomcat 10.0 开始换成了jakarta.servlet。这意味着什么你手上任何一份基于javax.servlet写的代码——不管是HttpServlet、Filter还是WebServlet注解——放到 Tomcat 10 里编译直接过不去或者编译能过但运行时抛ClassNotFoundException。而网上大量教程、示例项目、甚至一些仍在维护的老框架用的都是javax那一套。所以我的建议是除非你明确知道项目用的是新规范否则先上 Tomcat 9.0 系列。它能兼容的场景足够多遇到的坑也最少。等你要学 Jakarta EE 新规范了再单独装一份 10.1 并注意两个版本不要共用环境变量。5.2 解压即用与三个必看文件解压之后的目录结构值得花两分钟看一眼目录作用你会用到它做什么bin启动停止脚本startup.bat / shutdown.bat 手动测试conf配置文件改端口、改编码、加数据源webapps应用部署目录手工部署 war 时放这里logs日志启动失败时第一时间看这里lib公共依赖放全局 jar但一般别动三个必看文件conf/server.xml改端口和编码。找到 Connector 配置Connector port8080 protocolHTTP/1.1 connectionTimeout20000 URIEncodingUTF-8 redirectPort8443 /URIEncodingUTF-8是解决中文参数乱码的关键。老版本默认不是 UTF-8GET 请求里带中文参数就会乱码很多人以为是前端编码问题折腾半天。conf/logging.properties改控制台日志编码。Windows 控制台默认 GBKTomcat 日志输出 UTF-8叠加起来就是满屏乱码。可以在文件里把控制台处理器的编码改成 GBKjava.util.logging.ConsoleHandler.encoding GBK或者在 IDEA 的启动配置里加 VM 参数-Dfile.encodingUTF-8。两种方案选一种即可千万别两边都改否则可能从一种乱码变成另一种乱码。bin/catalina.bat需要调整 JVM 内存时改这里一般通过设置CATALINA_OPTS环境变量更规范。5.3 IDEA 里配 Tomcat 的几种方式以及社区版的现实先泼一盆冷水Tomcat 的集成运行是 IDEA 旗舰版的功能社区版没有。如果你用的是社区版在 Run/Debug Configurations 里翻遍也找不到 Tomcat Server 选项这是正常的不是你没找到。你有三个选择方案适用场景代价换用旗舰版公司项目、长期开发需要授权学生和开源项目可以申请免费授权装 Smart Tomcat 插件社区版临时跑 Web 项目配置项少功能比原生集成弱用 Maven 的 Tomcat 插件只想快速验证插件版本老对新规范支持有限改用 Spring Boot 内嵌容器新项目需要改造项目结构旗舰版的配置路径是Run → Edit Configurations → → Tomcat Server → Local然后指定 Tomcat 安装目录在 Deployment 标签页里添加 Artifact。社区版用户如果只是想验证一个 Servlet我建议直接用命令行把项目打包成 war 丢进webapps然后跑startup.bat虽然笨但一定通。顺带提一句内嵌容器的思路这是现在新建项目的主流做法Spring Boot 项目默认就把 Tomcat 作为内嵌容器打进来了spring-boot-starter-web里已经包含了它。如果你因为某些原因需要替换内嵌容器标准做法是在这个依赖里排除掉 Tomcat再引入其他容器的 starter。代码层面几乎不用改因为 Servlet API 是统一的——当然前提是你已经跨过了 5.1 节说的那道包名变更的墙。5.4 war 与 war exploded 的区别以及热更新怎么开在 Deployment 页面添加 Artifact 时你会看到两种形态war 包把整个项目打成一个压缩包再部署改了代码必须重新打包才生效war exploded把目录结构原样展开部署Tomcat 直接读目录里的 class 和资源文件改完可以增量更新。开发阶段一律选war exploded。然后在旁边的 On Update action 和 On frame deactivation 两个下拉框里选择更新策略前者是手动触发更新时的动作一般选 Update classes and resources后者是切换到浏览器等窗口时的自动动作选 Do nothing 避免频繁触发拖慢 IDEA。这里有个经典困惑改了 Java 代码浏览器刷新了但页面还是老逻辑。原因是 Tomcat 默认只重新加载被修改过的 class但如果你的修改涉及方法签名变更、新增类、改了注解热更新往往不到位。这时候不要犹豫直接重启 Tomcat。我在开发时对 JSP、HTML、CSS 这类资源改动依赖热更新对 Java 代码改动一律重启反而更省时间。5.5 三类高频故障的定位手法第一类8080 端口被占用。报错关键字是 Address already in use。执行netstat -ano | findstr :8080拿到最后一列的 PID去任务管理器对照结束进程。如果占用者是java.exe很可能是上一次 IDEA 里的 Tomcat 没完全停掉或者另一个 IDE 窗口还在跑。第二类启动日志乱码。回去看 5.2 节注意只改一边。第三类访问根路径 404。按这个顺序看Application context 是不是设成了/之外的值。设成/demo的话你访问根路径当然是 404要访问http://localhost:8080/demo/项目的 Artifact 有没有正确地包含进 Deployment 列表是不是把web.xml里的url-pattern和浏览器地址对不上或者在 Tomcat 10 里用了javax.servlet的注解这种情况下类根本不会被扫描到控制台可能只有一行不起眼的警告。第三条我再强调一次因为它太隐蔽了。注解不被识别时不会报错只会静静地不注册这个 Servlet然后在访问时给你一个 404。遇到代码怎么改都 404先确认包名。6. 把四个组件串起来跑通一遍前面都配好之后一定要做一次端到端验证。不是为了写业务代码而是为了确认这条链路上没有暗雷。6.1 用 Maven 骨架起一个最小 Web 工程在 IDEA 里新建项目选 Maven 骨架maven-archetype-webapp。它会生成标准的 webapp 目录结构和一份最简 pom。生成后先做两件事把Project SDK设为你的 JDK把 Language Level 对齐见 2.4 节。6.2 pom 里需要补的三样东西第一打包方式必须是 warpackagingwar/packaging第二Servlet API 的依赖。注意作用域必须是 provided否则它会被打进 war 包跟 Tomcat 自带的版本冲突dependency groupIdjakarta.servlet/groupId artifactIdjakarta.servlet-api/artifactId version5.0.0/version scopeprovided/scope /dependency用 Tomcat 9 的话这里要换成javax.servlet:javax.servlet-api版本 4.0.1。这就是 5.1 节那道墙在具体代码里的样子。第三MySQL 驱动和连接池。驱动用mysql-connector-j连接池用 HikariCP 或 Druid 都行这里用 Druid 举例。6.3 写一个同时碰到 Tomcat 和 MySQL 的验证页面写一个 Servlet在init或doGet里建一次数据库连接查一行数据然后打印出结果。不用写在正式代码里精心设计一个最朴素的实现就够WebServlet(/ping) public class PingServlet extends HttpServlet { Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException { String url jdbc:mysql://127.0.0.1:3306/demo ?useUnicodetruecharacterEncodingutf8 useSSLfalseserverTimezoneAsia/Shanghai allowPublicKeyRetrievaltrue; try (Connection conn DriverManager.getConnection(url, demo_app, 你的密码); Statement st conn.createStatement(); ResultSet rs st.executeQuery(select 1)) { rs.next(); resp.setContentType(text/plain;charsetUTF-8); resp.getWriter().println(db ok: rs.getInt(1)); } catch (Exception e) { resp.setContentType(text/plain;charsetUTF-8); resp.getWriter().println(db fail: e.getMessage()); } } }这个页面同时验证了四件事Tomcat 能加载并注册 Servlet、Servlet 注解被正确扫描、JDBC 驱动能被加载、连到 MySQL 的账号密码和参数都对。任何一项有问题它都会给你一个明确的失败信息比写一堆业务代码再慢慢排查高效得多。6.4 从 IDEA 一键跑起来配置 Tomcat 运行项Deployment 里加war explodedApplication context 设成/demo启动。浏览器访问http://localhost:8080/demo/ping看到db ok: 1整个环境就算真的搭完了。如果显示db fail: ...看冒号后面的具体异常Access denied是账号密码或权限问题Communications link failure是服务或端口问题Unknown database是库名写错了The server time zone value是连接串缺时区参数。异常信息本身就是最好的排查指南别跳过它去瞎猜。7. 多版本共存与环境迁移的实操经验7.1 一台机器上跑多个 JDK 和多份 Tomcat做老项目维护的人几乎都会遇到手上有个 JDK 8 的老系统和一个 JDK 17 的新系统要同时开发。系统级的 JAVA_HOME 只有一个来回改太累。我的做法是系统 JAVA_HOME 固定指向最常用的那个版本保证命令行下java -version是日常用的版本然后在 IDEA 里给每个项目单独设置 Project SDK指向不同的 JDK 目录。IDEA 是完全按项目隔离的多个窗口同时运行不同 JDK 的项目也没问题。命令行需要临时切换时写两个批处理脚本REM use-jdk8.bat set JAVA_HOMED:\dev\jdk\jdk-8 set PATH%JAVA_HOME%\bin;%PATH% java -version注意这种set只影响当前窗口关掉就恢复。不要用setx去改系统变量那会把全局状态改掉下次又得改回来。Tomcat 同理不同版本解压到不同目录各自的conf/server.xml里把端口错开比如 8080、8081、8082这样几份 Tomcat 可以同时启动互不干扰。端口冲突是很多人以为Tomcat 坏了的真实原因。7.2 换电脑时怎么把环境搬过去我整理过一份自己的迁移清单按这个顺序恢复几乎没有返工先确认新机器的用户名和安装盘符如果和旧机器不一致所有硬编码路径的配置文件都要改包括 settings.xml、my.ini、IDEA 的 SDK 配置IDEA 配置在用户目录下跟着账户走路径变了要重新指拷贝D:\dev整个目录保持相对结构不变重新配置系统环境变量JAVA_HOME、MAVEN_HOME、Path 三条用mysqld --install在新机器上重新注册 MySQL 服务服务注册信息存在系统里不是拷贝目录就能带过去的这一步最容易漏启动各组件做一次版本验证java -version、mvn -v、mysql --version、Tomcat 的 startup最后打开 IDEA核对 Maven 的三处路径和 Project SDK。经验第 4 步是迁移时的高频故障点。很多人拷完目录直接net start mysql报服务名无效因为服务压根没在新机器上注册过。而且注册时必须用管理员权限的终端否则会静默失败。8. 一些我自己踩出来的碎经验关于环境变量任何时候改完环境变量先关掉所有终端再重开这一条能省掉至少三成的配了没生效。关于版本能选 LTS 就选 LTS能选流行组合就选流行组合。环境搭建的目标是让后面的开发顺利不是给未来的自己增加调试负担。用冷门版本组合省下来的那点新鲜感远不够填排查问题的时间。关于配置文件的维护我会在D:\dev目录下放一个env-notes.md记录每个组件的版本号、安装路径、端口、账号密码不写明文只写存放位置。下次重装或者换机器翻这个文件比翻浏览器历史快得多。这个小习惯救过我很多次尤其是隔了半年再回来维护某个老项目的时候。关于排查心态环境类问题的特点是症状五花八门但根因集中。看到报错别急着搜关键词先问自己两个问题——这个组件的版本是哪个它和上下游的版本对不对得上把这两问答清楚绝大多数问题会自己浮出水面。