news 2026/10/27 7:11:02

IDEA新建JavaWeb项目避坑:从项目模型到Tomcat部署排查

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
IDEA新建JavaWeb项目避坑:从项目模型到Tomcat部署排查

每次带新人,只要涉及 IDEA 新建项目这件事,问题就特别集中:代码一个字没改,浏览器一打开就是 404;依赖明明写在 pom 里,编译却报“找不到符号”;JSP 改完刷新还是老样子。这些毛病九成不是业务代码写错,而是“新建项目”那一步埋下的伏笔。IDEA 新建项目(Java 项目、JavaWeb 项目)看着只是点几下 Next,实际上它在后台同时干了四件事:确定项目 SDK(就是项目使用的 JDK)、生成目录结构、建立模块与源码根目录的映射、声明构建方式与部署产物。这四件事里任何一件选偏,后面都要花几个小时甚至几天去补。这篇东西就是把这四件事拆开讲透,从环境检查、普通 Java 项目搭建,到 Maven 版 JavaWeb 项目的目录、依赖、Tomcat 部署配置,再到我这些年被坑出来的排查套路,全部铺开写,新手照着做能跑通,有基础的人也能在里面对照出自己一直没搞明白的那个环节。

1. 先把地基摸清:IDEA 里的项目模型到底是什么

1.1 Project、Module、Facet、Artifact 四层结构

很多人用 IDEA 很久,还是把 Project 当成“一个文件夹”,这个理解偏差是后面所有困惑的源头。IDEA 的模型是分层的,从上到下依次是 Project、Module、Facet、Artifact,四层各有各的职责,一层断链,运行阶段就会以 404 或者 ClassNotFound 的形式把问题甩回给你。

Project 是最外层的工作空间,对应你打开的那个窗口,它在磁盘上体现为一个根目录加上一个隐藏的.idea配置目录。.idea里存的不是代码,而是这个工作空间的元信息:用了哪个 JDK、包含哪些模块、编码设置、运行配置等等。你把这个目录删掉,项目结构信息基本就丢了,只剩下源码文件;反过来,如果你换台机器只拷了.idea没拷源码,打开就是一堆红。

Module 是真正的代码单元和构建单元。一个 Project 下面可以有多个 Module,每个 Module 有独立的源码根目录、独立的依赖列表、独立的编译输出路径。日常说的“这个项目拆成几个模块”,说的就是这一层。Module 的配置文件在旧版本里是一个.iml文件,新版本收进了.idea/modules目录里,但本质没变,它记录的是“哪些目录算源码、哪些算资源、依赖从哪来、编译产物扔哪去”。

Facet 是 IDEA 给模块贴的“身份标签”。一个模块如果被标记了 Web Facet,IDEA 才知道这个模块里有 Web 资源目录、有部署描述符web.xml,才知道打包的时候该去哪找WEB-INF。这个标签是纯 IDE 层面的概念,跟代码本身没关系,所以它最容易被忽略,也最容易出问题——你明明建了webapp目录,也放了index.jsp,但模块上没有 Web Facet,IDEA 就当作没看见,部署下去自然什么都没有。

Artifact 是打包产物的描述,回答的是“部署的时候到底把哪个东西扔进容器”。它有两种常见形态:war exploded指的是一个展开的目录,容器直接读这个目录;war指的是压缩好的归档包。Artifact 的配置里会明确写出输出目录、包含哪些模块的编译结果、Web 资源根目录在哪。Tomcat 的运行配置里挂的就是 Artifact,这一层要是挂错了或者没挂,服务器能起来,但页面就是打不开。

我把这四层的关系画成一句话:Project 是壳,Module 是肉,Facet 是标签,Artifact 是快递包裹。壳对了肉不对,编译不过;肉对了标签没贴,Web 资源不被识别;标签贴了包裹没装,服务器跑起来也是空的。

1.2 为什么“新建项目”这一步最容易翻车

翻车集中在三个地方。

第一个是版本链条没对齐。JDK 版本、语言级别(Language Level)、编译目标版本(target)、Maven 编译插件的 source/target、Servlet 规范版本,这几项必须是同一条链上的。我见过太多次这样的组合:项目 SDK 选的 17,语言级别是 8,pom 里写的是 1.8,然后用了某个只在高版本才有的语法,IDEA 编辑器不报错,一编译就炸。或者反过来,环境全按 8 配,依赖里拉进来的某个包要求 11 以上,运行期直接UnsupportedClassVersionError。

第二个是“模板默认值”陷阱。IDEA 的向导每个版本都在变,默认勾选项也在变。老版本新建 Java 项目时有个 “Java Web” 的附加选项可以勾,新版本改成了独立的 Jakarta EE 模板;老版本是先建 Project 再建 Module,新版本向导里直接就把 Module 建好了。你照着两年前的教程点,会发现界面对不上,然后凭感觉乱点,最后建出来的结构跟教程里完全不一样。这不是你的问题,是教程过期了,但代价要你自己承担。

第三个是目录标记。源码目录没标成 Sources Root(蓝色),IDEA 就不会编译里面的类;资源目录没标成 Resources Root,配置文件就不会进 classpath;webapp目录没在 Facet 里指认,静态资源就不会被部署。这三个标记全是“右键菜单里的一个选项”,新手根本想不到要去点,结果就是代码看着没问题,运行死活不对。

提示:新建项目之前,先花三十秒确认三件事——本机装了几个 JDK、IDEA 里配的是哪个、命令行java -version输出的是哪个。很多奇怪的编译错误,起因就是这三者不一致。

2. 环境前置准备:新手最容易忽略的检查清单

2.1 JDK 的选择与配置

JDK 这件事,我给的建议很直接:本机保留两到三个版本就够了,别再多了。常见组合是一个 8(维护老项目)、一个 11 或 17(新项目)。装太多版本的直接后果是,你在 IDEA 里选 SDK 的时候根本分不清哪个是哪个,路径还都长得差不多。

装完之后要在 IDEA 里登记一次。入口在 Project Structure 里的 SDKs 面板,点加号,选 JDK 的安装根目录,IDEA 会自动识别出版本号并给它起个名字。这里有个小技巧:给 SDK 命名的时候带上完整版本号,比如“JDK 1.8.0_301”,别用默认的“1.8”,因为同一个大版本可能有多个小版本,出问题时你连自己用的是哪个都不知道。

项目级的 SDK 在 Project 面板里设置,同时下面还有一个 Language Level 下拉框。这两项的关系是:SDK 决定“用哪个编译器”,Language Level 决定“允许用什么语法”。两者不一致时,编辑器会容忍你的写法,但编译阶段会报错。我的做法是让 Language Level 永远跟着 SDK 走,除非是老项目必须降级,那就明确写出来,别让它悬着。

再往下是模块级的 SDK。Project Structure 里 Modules 面板,每个模块可以单独指定 SDK。多模块项目里经常出现某个模块被误设成了别的版本,编译时提示“无效的目标发行版”,找半天找不到原因。这种情况我一般直接全局搜*.iml文件和.idea/modules目录,把版本号统一掉。

2.2 Maven 的本地仓库与配置

JavaWeb 项目基本都要用 Maven,所以本地仓库这件事绕不开。默认的本地仓库在用户目录下的.m2/repository,这个位置本身没问题,但如果你的系统盘空间紧张,或者习惯把开发环境放在非系统盘,就得改。

改的方式是编辑 Maven 安装目录下conf/settings.xml,找到<localRepository>节点,把注释打开写上自己的路径。这里有个坑:settings.xml有两份,一份在 Maven 安装目录的conf下(全局配置),一份在用户目录的.m2下(用户配置),IDEA 默认读的是用户配置。你在全局配置里改了仓库路径,IDEA 里没生效,就是因为这个。要么两份都改,要么在 IDEA 的 Maven 设置里手动把 User settings file 指过去。

仓库源也建议换一下。默认的中央仓库在国内下载速度很不稳定,经常下到一半卡住,然后留下一个.lastUpdated后缀的残留文件,之后无论怎么重试都失败,因为 Maven 认为这个依赖已经试过了。遇到这种情况,最干脆的做法是去本地仓库对应目录下,把所有.lastUpdated文件删掉,再重新拉一次。

# 在本地仓库根目录执行,清掉所有失败标记 find ~/.m2/repository -name "*.lastUpdated" -delete

至于具体换哪个镜像地址,各家云厂商都提供,选一个响应快的填进去就行。填的位置在settings.xml的<mirrors>节点里,注意<mirrorOf>一般写central,别写成*,否则会把你私有仓库的请求也一起劫持掉,到时候自己发布的包拉不到,又是一轮排查。

2.3 容器版本的对应关系

服务端容器的版本与 Servlet 规范版本是绑死的,这一条必须记牢,因为它直接决定了你 import 的包名是javax.servlet还是jakarta.servlet。这两个包名不通用,写错了编译就过不去,而且改了包名之后还要对齐依赖版本,链条很长。

大致的对应关系是这样:Servlet 3.1 对应容器 8.0/8.5,Servlet 4.0 对应容器 9.0,Servlet 5.0 起包名从javax换成jakarta,对应容器 10.0 及以上。这个分界线是从 9 到 10 的那一步,也是目前最容易踩的一步——很多教程还在用 9,你本地装的是 10,照着抄就报“找不到符号”。

我个人的建议是:如果是跟着教程学,容器版本和教程保持一致,别自己升级;如果是新起项目,直接用新版本,从一开始就别碰旧包名。最怕的是学着学着中途换版本,前面写的代码全部要改 import。

另外还要确认容器的 JRE 指向。容器运行需要 JDK,在 IDEA 的运行配置里会有一个 JRE 下拉框,如果这一项是空的或者指向了一个不存在的路径,服务器启动时会直接报错退出,错误信息通常很含糊,只说“无法启动”,不说原因。

3. 新建普通 Java 项目:从空白到跑通第一个类

3.1 向导面板逐项拆解

从欢迎界面点新建项目,进入向导。左侧是语言列表,选 Java;右侧从上往下依次是名称、存储位置、构建系统、JDK、以及是否添加示例代码。这几项看着简单,其实每一项都影响后面的走向。

名称和存储位置:名称会同时作为目录名和模块名。我的习惯是名称用小写加短横线,跟包名解耦,因为目录名一旦用了中文或者空格,某些构建工具会直接给你上一课。存储位置尽量选一个短路径,别放在桌面或者深层目录里,Windows 上路径过长导致编译失败的情况真的存在。

构建系统这一项是新手最容易忽略的。可选项通常是 IntelliJ(内置构建)、Maven、Gradle,还有一栏是添加示例代码的勾选框。选“IntelliJ”意味着不生成任何构建描述文件,全靠 IDE 自己的编译机制,适合练语法、写单文件工具类;选 Maven 会生成 pom,适合后面要引第三方库、要打成可分发产物的场景。

JDK 这一项,如果你前面已经登记过 SDK,下拉里会直接列出来。没登记过就点右边的添加按钮现场指路径。

注意:勾选“添加示例代码”会帮你生成一个带main方法的类,省事但容易让人误以为项目已经配好了。实际上它只证明“能编译能跑”,不证明目录标记、依赖管理这些关键项是对的。我建议新手第一次故意不勾,自己从空目录开始建包、建类,把这个过程走一遍,理解会深很多。

3.2 项目建好之后的目录长什么样

一个干净的 Java 项目落地后,磁盘上大概是这个结构:

demo-java/ ├── .idea/ # IDEA 的工作空间配置,不要提交到版本库 ├── src/ # 源码根目录,蓝色标记 │ └── com/example/demo/ │ └── Main.java └── out/ # 编译输出,橙色标记,自动生成 └── production/demo-java/

src目录是蓝色的,这个颜色不是装饰,是 IDEA 告诉编译器“这个目录下的.java文件需要参与编译”。如果哪天你发现新建的类前面没有那个小图标,或者代码里全是红线提示“找不到符号”,先去 Project Structure 的 Modules 面板看看 Sources 标签页里,src是不是被标成了 Sources。有时候导入别人的项目,目录结构是对的,但标记丢了,表现就跟代码写错一模一样。

out目录是编译输出,里面放的是.class文件。这个目录不需要你手动维护,也不需要提交。它被标成橙色(Excluded),意思是“里面的内容不参与索引和搜索”,把它排除掉可以显著减少 IDE 的索引负担,尤其是编译产物多的时候。

.idea目录要单独说一下。这个目录里存的是你个人的工作空间状态,包括窗口布局、打开的标签页、运行配置。多人协作时它通常被加入忽略列表,因为每个人的配置不一样,提交上去只会制造冲突。但有一种情况例外:项目的 JDK 版本、编码设置这些“团队应该统一”的东西,有的团队会把它提交上去。这件事没有绝对对错,关键是团队内部要统一口径,别一半人提交一半人不提交。

3.3 建包、建类与运行配置

在src上右键新建包,包名按反域名习惯写,比如com.example.demo。一定不要图省事直接把类建在src根下,那样类就落进了默认包,默认包里的类不能被别的包 import,后面想重构都难。

包建好之后在里面新建 Java 类,填类名,回车。IDEA 会生成一个空类骨架。输入psvm回车生成main方法,输入sout回车生成输出语句,这两个是最高频的代码模板,值得一开始就记住。

package com.example.demo; public class Main { public static void main(String[] args) { System.out.println("hello"); } }

写完之后点类名左边那个绿色三角就能跑。第一次运行时 IDEA 会自动创建一个运行配置,类型是 Application,里面记录了主类全名、使用哪个模块的 classpath、工作目录。这个配置可以在运行下拉框里点“编辑配置”查看和修改。

关于运行配置,有三项值得留意。Main class必须是带main方法的类全名,写错了会提示“找不到或无法加载主类”。Use classpath of module决定了运行时能加载哪些类,多模块项目里如果选错了模块,会出现“代码明明在,运行就是找不到”的情况。Working directory是相对路径的基准,读文件写文件时用的相对路径都以它为起点,默认是项目根目录,但你可以改,改完之后代码里那些相对路径的含义就跟着变了,这一点在读写配置文件时特别容易出问题。

还有一种情况:右键菜单里压根没有“运行”选项。这通常意味着 IDEA 没把这个文件识别成可编译的源码,回到上一节检查 Sources 标记,八成就解决了。

4. 新建 JavaWeb 项目:三条路线的取舍

4.1 路线一:用 Maven 骨架一键生成

这是最省事的一条路。新建项目时左侧选 Maven,勾上“从骨架创建”,在骨架列表里搜webapp,选中那个maven-archetype-webapp,填好 GroupId 和 ArtifactId,一路下一步。

生成出来的结构是这样:

web-demo/ ├── pom.xml └── src/ └── main/ └── webapp/ ├── index.jsp └── WEB-INF/ └── web.xml

这个骨架的好处是pom.xml里已经把packaging写成了war,webapp目录和web.xml也给你备好了,符合 Web 项目的标准目录约定。缺点是它不生成src/main/java目录,你得自己建,建完之后还要去 Project Structure 里把它标记成 Sources Root。这一步漏掉,你写的 Servlet 类根本不会参与编译,部署上去当然找不到。

骨架生成时还有一个坑:它默认的web.xml版本可能很老,声明的 schema 是 2.3 或者 2.4 那一档。如果你的容器是新版本,用老 schema 启动会有一堆警告,某些特性也用不了。判断方法很简单,打开web.xml看version属性的值,如果是 2.x,手动改成 4.0 并把xmlns那一串换成对应版本的地址就行。另一种更省事的做法是干脆不要web.xml,改用注解方式注册 Servlet,这个在下一节展开。

4.2 路线二:手工建结构,掌控每一处细节

我个人的主力做法是这条。先建一个空的 Maven 项目,不勾骨架,然后把目录结构一层层手工建出来:

web-demo/ ├── pom.xml └── src/ ├── main/ │ ├── java/ # 标记为 Sources Root │ ├── resources/ # 标记为 Resources Root │ └── webapp/ # 标记为 Web 资源目录 │ └── WEB-INF/ └── test/ └── java/ # 标记为 Test Sources Root

建完之后必须做三件事,缺一不可。第一,src/main/java右键标记为 Sources Root;第二,src/main/resources标记为 Resources Root;第三,去 Project Structure 的 Facets 面板添加一个 Web Facet,把它挂到当前模块上,然后在 Web Resource Directory 里把src/main/webapp指过去。

第三条是新手最容易漏的。前面说过,Facet 是 IDEA 层面的身份标签,没有它,webapp目录在 IDEA 眼里就是个普通的文件夹,打包的时候不会被当成 Web 根目录,部署下去自然空空如也。

这条路线的价值在于:你对每一处配置都心里有数。哪一步对应什么效果,出问题时该往哪查,全在掌控里。而且这套结构跟大多数构建工具的默认约定一致,换个 IDE 或者上流水线,都不需要额外适配。

4.3 路线三:Jakarta EE 模板,慎用但可以了解

新版本 IDEA 里有一个专门的企业级模板,选中之后会一次性把 Web 模块、依赖、目录、运行配置全给你配好,甚至连容器的运行配置都能生成。听起来很美,实际用起来有几个顾虑。

一是它默认的版本组合通常比较新,包名可能是jakarta.*,你本地的容器如果不是对应版本,跑不起来。二是它生成的配置项多,对新手来说是个黑盒,出了问题不知道从哪一层开始查。三是各种教程和现成代码大多还是旧包名体系,直接往上贴会冲突。

我的建议是:如果你本地环境本来就是新版本,而且打算从头学新的规范,用这个模板没问题;如果你是跟着某份材料学,或者要维护既有项目,老老实实走路线一或者路线二,把控制权握在自己手里。

路线上手成本目录完整度掌控度适用场景
Maven 骨架低缺java目录中快速验证、跟着教程走
手工搭建中完整高正式项目、需要长期维护
企业级模板低完整低新版本环境从头起步

5. 让页面真正跑起来:JavaWeb 的关键配置

5.1 pom.xml 里的三处关键写法

pom.xml是 Maven 项目的总纲,Web 项目里必须把三件事写对:打包方式、依赖范围、编码与编译版本。

<packaging>war</packaging>

这一行决定了产物形态。写成jar,打包出来就是一个普通归档,容器不认;写成war,才会按 Web 应用的目录约定组织,容器才能部署。骨架生成的项目一般已经是war,手工建的要自己加。

依赖这块,Servlet 相关的包必须设成provided:

<dependency> <groupId>javax.servlet</groupId> <artifactId>javax.servlet-api</artifactId> <version>4.0.1</version> <scope>provided</scope> </dependency>

provided的含义是“编译时需要,运行时不打包”。为什么这么做?因为容器自己就带着这套 API。你如果按默认的compile范围把它打进WEB-INF/lib里,容器启动时 classpath 上就有两份同名的类,轻则警告,重则类加载冲突,报出一堆莫名其妙的LinkageError。这个坑极其常见,因为 IDEA 的自动导入补全默认就是compile,不手动改就等着踩。

编译版本和编码也要显式写死,别指望默认值:

<properties> <maven.compiler.source>8</maven.compiler.source> <maven.compiler.target>8</maven.compiler.target> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> </properties>

source和target必须跟你项目 SDK 的语言级别一致。三处不一致的后果前面说过,症状是“编辑器不报错、编译报错”,最难查。编码这项不写,某些平台上默认编码不是 UTF-8,源码里的中文注释和字符串会变成乱码,而且是编译之后就坏掉,回过头看源码又是好的,非常迷惑。

还有一个可选但很有用的配置:

<build> <finalName>demo</finalName> </build>

finalName决定了打出来的包名,而这个包名在很多容器配置里会直接影响访问路径。默认的包名是artifactId-version,带版本号和横线,访问路径就变成/web-demo-1.0-SNAPSHOT/hello这种又长又难看的地址。设成demo之后路径就清爽了。这件事看似小事,但在配访问路径和排查 404 的时候,能省掉不少来回。

5.2 部署描述符与注解方式的选择

老的写法是在web.xml里注册 Servlet:

<servlet> <servlet-name>hello</servlet-name> <servlet-class>com.example.demo.HelloServlet</servlet-class> </servlet> <servlet-mapping> <servlet-name>hello</servlet-name> <url-pattern>/hello</url-pattern> </servlet-mapping>

新的写法是直接在类上打注解:

package com.example.demo; 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;charset=UTF-8"); resp.getWriter().write("<h1>hello servlet</h1>"); } }

注解方式要求 Servlet 规范在 3.0 以上,现在基本都是了。它的好处是不用维护两份配置,改了路径直接改注解,不会出现“类改了名字但web.xml没同步”的低级错误。

但注解方式有一个前提容易被忽略:web.xml里的metadata-complete属性不能设成true。这个属性为true时,容器会跳过注解扫描,只认web.xml。有些老项目的模板里默认写的是true,你加了注解怎么都不生效,查半天查不出原因,就是卡在这。

我的习惯是:新项目直接不要web.xml,全用注解;老项目保持原样,别混着改。混用是最麻烦的,一部分 Servlet 在配置里,一部分在注解里,出问题时要在两个地方来回找。

5.3 容器运行配置的完整过程

这是整个流程里最需要细节的一段。打开运行配置面板,点加号,找到容器类型下的本地实例,新建一个配置。

第一块,服务器本体。在 Server 面板里指定容器安装目录、访问端口、以及运行时使用的 JRE。端口默认是 8080,如果被别的程序占了,启动时会报“地址已被占用”,改成 8081 之类的即可。JRE 那一项千万别留空,留空的表现是点启动之后一闪而过,日志里只有一行含糊的报错。

第二块,部署产物。切到 Deployment 面板,点加号添加工件(Artifact)。这里通常会看到两个候选:一个是war exploded,一个是war。前者是展开目录,后者是压缩包。开发阶段一定选前者,因为它支持热更新,改了类或者页面之后不用重启;后者每次改动都得重新打包部署,循环太慢。

添加工件之后,下面有一个 Application context 输入框,这个值决定了访问路径的前缀。默认可能是/或者/项目名_war_exploded,前者意味着根路径直接访问,后者意味着要带上那一长串。我一般手动改成/demo这种短名字,然后在浏览器里访问http://localhost:8080/demo/hello。这里填的值和finalName没关系,两者是独立的,这也是为什么很多人误以为改了finalName就能改访问路径,结果没生效。

第三块,更新策略。在 Server 面板里有个“On frame deactivation”或者“On update action”的下拉框,可选“更新类和资源”“重启服务器”“不操作”。开发阶段选“更新类和资源”最舒服,切回浏览器时自动把改动同步过去。但要注意,这个策略只对war exploded有效,对压缩包无效。而且它同步的是资源和编译后的类,不包含依赖变动和配置变动,改了pom.xml还是得重启。

配好之后点运行,控制台会打印一串启动日志。看到“服务器在 X 毫秒内启动”之类的字样就说明起来了。这时候别急着写业务,先访问一下根路径,如果能看到index.jsp的默认内容,说明整条链路是通的:编译、打包、部署、访问,四个环节都没问题。这一步验证过了,再往上加代码,出问题就只可能是代码本身的问题,排查范围缩小一大截。

5.4 编译输出路径与工件的对应关系

这一段讲的是“为什么我改了代码但部署上去的还是旧版本”这个问题。

IDEA 的编译输出路径可以在 Project Structure 的 Modules 面板里看到,每个模块有一个输出目录。较新的版本把编译输出统一放到了项目的target或out目录下,具体位置取决于构建方式。用 Maven 构建的,输出在target/classes;用内置构建的,输出在out/production/模块名。

工件(Artifact)的定义里,会明确写出“从哪个模块的输出目录取类文件”。这两者的关系是一条链:源码 → 编译输出目录 → 工件定义 → 容器部署目录。链上任何一环指错,你改的代码就进不了部署目录。

排查方法很直接:改一行代码,看编译输出目录里对应的.class文件修改时间有没有变。没变说明没编译,检查自动编译是不是关了;变了但部署目录里没变,说明工件定义指错了地方,去 Artifact 配置里核对输出路径。这个思路一旦建立起来,这类问题基本几分钟就能定位。

提示:自动编译这个开关在设置里的编译相关面板中,默认状态在不同版本里不一样。如果发现改代码之后必须手动点一下“构建”才生效,先去把这个开关打开。

6. 常见问题与排查实录

6.1 三个高频错误的定位顺序

404,页面打不开。这个错误的排查有固定顺序,别乱试。第一步,看容器日志里应用有没有成功部署,如果有“部署成功”之类的记录,说明打包没问题,是路径问题。第二步,核对访问地址的 context path,就是部署配置里那个输入框的值,多一个斜杠少一个斜杠都算错。第三步,确认webapp目录下有没有默认首页,或者你访问的路径在代码里有没有对应的映射。第四步,回到 Facet 配置看 Web 资源目录指对了没有。这四步走完,百分之九十五的 404 都能定位。

ClassNotFound,类找不到。先分清是编译期还是运行期。编译期报错,检查依赖有没有正确引入、版本有没有冲突、源码目录标记对不对。运行期报错,重点看部署产物的WEB-INF/lib目录里到底有没有那个 jar。缺的话,可能是依赖范围写成了provided而它并不是容器提供的包,也可能是打包插件配置里排除了某些依赖。

端口占用,启动失败。报错信息里会明确写出被占用的端口号。改端口是最省事的做法。如果一定要用原端口,就去查是哪个进程占着。Windows 上用netstat -ano | findstr 8080拿到进程号再查,类 Unix 系统上用lsof -i :8080。很多时候占用端口的不是什么可疑程序,就是你上一次没关干净的容器实例。

6.2 中文乱码的整条链路

乱码这件事,出问题的环节比想象的多,得按链路一段段查:源码文件编码、编译器读取编码、输出资源编码、容器解析请求编码、浏览器渲染编码。五个环节里有任意一个不是 UTF-8,就可能出现乱码。

具体做法:设置里把全局编码、项目编码、属性文件编码全部设成 UTF-8;pom.xml里写死project.build.sourceEncoding;JSP 页面顶部声明pageEncoding="UTF-8";Servlet 里输出前设resp.setContentType("text/html;charset=UTF-8");容器配置文件里的连接器节点加上URIEncoding="UTF-8"。

还有一个很容易漏的点:控制台输出乱码。这个跟应用本身没关系,是运行配置里的虚拟机参数没指定文件编码导致的。在运行配置的虚拟机参数里加上编码设置,控制台的中文就正常了。这个不影响功能,但影响你调试时看日志的心情,值得顺手解决。

6.3 常见问题速查表

现象最可能的原因快速验证方式
页面 404访问路径没带 context path看部署配置里的上下文值,手工补全
页面 404工件没添加到部署列表打开部署面板看列表是否为空
编译报“找不到符号”源码目录未标记看目录颜色是否为蓝色
运行报类找不到依赖范围写错检查部署目录下WEB-INF/lib
改动不生效更新策略为“不操作”改策略为更新类和资源
中文乱码编码链路某处不是 UTF-8逐环节核对编码设置
启动一闪而过运行配置的 JRE 为空打开配置补上 JRE
依赖下载失败本地仓库残留失败标记删掉.lastUpdated文件重试
注解不生效配置里metadata-complete为 true改成 false 或删掉该属性
包名报错识别不了规范版本与容器不匹配核对包名是javax还是jakarta

7. 我踩过之后固化下来的几条习惯

第一条,一个业务系统一个 Project,但内部按职责拆模块。早期我图省事所有东西塞一个模块里,结果 Web 层依赖了数据层的实现细节,测试代码跟业务代码混在一起,改一处牵连一片。拆开之后,模块之间的依赖方向变得可见,谁依赖谁一眼就能看出来,编译期的约束帮你挡住了一部分低级错误。

第二条,版本号集中声明,别散落在各处。pom.xml里的依赖版本统一放进properties里,改的时候只改一处。散着写的直接后果是某天升级一个包,忘了另一个,两边版本冲突,运行时报出谁也看不懂的错误。

第三条,每次新建项目之后,先走一遍最小验证。不要一上来就写业务,先跑通一个最简单的输出,确认编译、部署、访问三个环节都通,再往上叠。这个习惯帮我省掉了无数次“到底是代码错了还是配置错了”的纠结。

第四条,把常用配置固化成模板。IDEA 支持把当前的工程配置存成模板复用,把 JDK 版本、编码、常用依赖、运行配置这些每次都一样的部分固化下来,下次新建直接套。这件事的价值随项目数量线性增长,做过三次以上同类项目就该动手了。

最后再分享一个我自己用得最多的小技巧:任何“明明代码没问题但跑不起来”的情况,先去看编译输出目录里的.class文件时间戳。这个动作只需要两秒钟,却能立刻把问题二分——时间戳没更新,问题在构建链路;时间戳更新了,问题在部署或者运行配置。我靠这一招把排查时间从半小时压缩到几分钟,屡试不爽。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/27 7:11:02

Linux标准IO编程:缓冲机制与性能优化实战

1. Linux标准IO编程核心解析在Linux环境下进行软件开发时&#xff0c;IO操作是最基础也是最重要的环节之一。标准IO&#xff08;stdio&#xff09;作为C语言标准库提供的缓冲式文件操作接口&#xff0c;相比直接使用系统调用&#xff08;如read/write&#xff09;具有更高效的性…

作者头像 李华
网站建设 2026/10/27 7:10:16

程序设计的核心:算法与数据结构解析

1. 程序本质的经典定义解析"程序 算法 数据结构"这个公式最早由计算机科学先驱Niklaus Wirth在其1976年出版的著作《Algorithms Data Structures Programs》中提出。这个看似简单的等式揭示了软件开发的底层逻辑&#xff1a;算法决定了如何处理信息&#xff0c;数…

作者头像 李华
网站建设 2026/10/27 7:09:59

计算机生物科技:基因编辑中的计算范式迁移

1. 这不是科幻片——当计算机真正坐进基因编辑实验室的工位“计算机生物科技”这个词&#xff0c;最近在学术会议和产业论坛里出现频率高得有点反常。很多人第一反应是&#xff1a;不就是用电脑画个蛋白质结构图&#xff1f;或者跑个BLAST比对序列&#xff1f;——这种理解&…

作者头像 李华
网站建设 2026/10/27 7:09:58

HTML/CSS前端开发核心技术与最佳实践

1. 为什么HTML/CSS是前端开发的基石2008年我刚入行时&#xff0c;导师递给我一本《HTML与CSS设计模式》&#xff0c;扉页上写着"先学会走路&#xff0c;再想着飞翔"。当时不以为然&#xff0c;直到在第一个项目里用布局被客户投诉"这网页在手机上像被卡车碾过&q…

作者头像 李华
网站建设 2026/10/27 7:09:21

Agent-Reach:大规模Agent可靠触达机制的设计与实践

1. 从凌晨两点的告警说起&#xff1a;Agent触达为什么是个真问题接手运维平台的那段时间&#xff0c;我整个人被一类故障折腾得够呛。平台上挂着三百多个Agent节点&#xff0c;分布在几十个机房的业务网段里&#xff0c;每天凌晨批量任务一触发&#xff0c;告警群里就会准时出现…

作者头像 李华
网站建设 2026/10/27 7:08:04

2026年教务系统选型指南与核心能力解析

1. 教务系统选型现状与挑战教务管理信息化建设已经走过了二十多个年头&#xff0c;从最初的单机版成绩录入系统&#xff0c;到如今覆盖教学全流程的智能化管理平台&#xff0c;技术迭代带来了前所未有的管理效率提升。但面对市场上近百种教务系统产品&#xff0c;高校信息中心主…

作者头像 李华