尧图精选

Tomcat部署前后端分离项目:从war包到资源托管全攻略

🕒 发布时间:2026/10/1 6:09:26 📁 来源:尧图网络
1. 部署前的基础认知Tomcat 与前后端分离项目前后端分离项目本地部署这件事说简单也简单说麻烦是真麻烦。很多人第一次接触时脑子里默认“前后端分离”就是把前端打包后丢进 Tomcat 的 webapps 里再把后端接口地址指过去——结果启动后访问前端页面发现一堆资源 404或者接口全部跨域报错半天排查不出头绪。先把这个项目到底要解决什么问题说清楚这是一个基于 Tomcat 的本地部署实践核心目标有两个一是让 Spring Boot 后端以 war 包形式跑在外部 Tomcat 上二是把前端构建产物比如 Vue 打包后的 dist 目录以一种合理的方式交给 Tomcat 托管最终实现前后端在本地环境里的联通访问。适合谁来参考如果你是刚接触前后端分离开发、想把本地联调环境弄得更贴近生产环境的人或者你所在团队的生产服务器还在用传统 Servlet 容器而非内嵌 Tomcat这篇内容就是写给你的。我之所以强调“用外部 Tomcat 而非 Spring Boot 内嵌 Tomcat”是因为很多人在本地开发时直接java -jar app.jar就能跑但一旦涉及部署到老一批服务器、或需要运维用统一脚本管理多个 Web 应用时外部 Tomcat 依然是绕不开的环节。而且外部 Tomcat 的坑——端口冲突、编码乱码、内存溢出、war 包热更新不生效——基本是固定那么几个踩过一次就记住了。在动手之前你得先想清楚一个问题你的前后端分离项目准备用哪种方案放进 Tomcat这事不提前想清楚后面全是返工。2. 三种部署思路先选好再动手2.1 方案一纯净前后端分离各自独立部署后端 Spring Boot 项目打成 war 包扔进 Tomcat 的webapps目录前端 Vue/React 项目构建出的 dist 目录也丢进webapps下的另一个目录比如webapps/ROOT或webapps/front。前端通过绝对路径请求后端接口比如http://localhost:8080/api/user/list。这个方案的优点是结构清晰前后端互不干扰后端升级时只需替换 war 包前端改版时只替换静态文件。缺点是必须解决跨域问题因为前端页面地址是http://localhost:8080/front/index.html后端接口地址是http://localhost:8080/api/...虽然同一个端口但如果前端静态资源和后端接口不在同一个 Servlet 上下文里浏览器依然视其为跨域请求。2.2 方案二前端打入后端 war 包单包部署把前端构建后的 dist 目录里的文件复制到后端项目的src/main/resources/static目录下然后整个后端打成 war 包。这样 Tomcat 只需部署一个 war前端页面和后端接口同源没有跨域问题。优点是部署最简单、本地联调最省心缺点是前后端耦合前端每次改动都要重新进后端项目里复制文件、重新打包开发效率低。适合小项目、演示项目或临时联调。2.3 方案三后端 war 包 前端通过 Tomcat 虚拟目录映射Tomcat 支持在conf/server.xml里通过Context标签配置虚拟目录把磁盘上的前端 dist 目录直接映射为一个访问路径。这样做的好处是前端构建产物不用复制进 webapps构建完刷新就能生效缺点是 server.xml 是全局配置不同团队协作时容易各改各的、冲突不断。我个人在本地部署时最常用的是方案一理由很简单它最接近真实生产环境的前后端分离架构。生产环境里前端往往用 Nginx 托管后端用 Tomcat本地用方案一模拟的是“前端静态服务 后端接口服务”的分离状态后续迁移生产时遇到的坑会更少。方案二适合赶时间的时候用方案三我基本只在调试静态资源路径问题时临时启用。想清楚用哪个方案之后下一步就是搭建一个可靠的本地 Tomcat 环境。3. 本地 Tomcat 环境准备下载、目录与配置3.1 下载与版本选择Tomcat 下载地址是官方站点注意别去第三方下载站。选择版本时有个基本原则Tomcat 的主版本要和 JDK 版本匹配。比如 JDK 8 配 Tomcat 8.5/9.0 都没问题JDK 11 建议 Tomcat 9.0JDK 17 则建议 Tomcat 10.1因为 Tomcat 10 开始用 Jakarta EE 命名空间javax.servlet变成了jakarta.servletSpring Boot 2.x 用的还是javax直接扔到 Tomcat 10 里会启动失败。这里有个很多人踩过的坑Spring Boot 2.x 项目打包 war 后部署到 Tomcat 10 会报ClassNotFoundException: javax.servlet.Filter。解决办法要么升级到 Spring Boot 3.x它已经切换为jakarta.servlet要么老老实实用 Tomcat 9。所以选版本前先确认你项目的 Spring Boot 大版本再反推 Tomcat 版本顺序千万别反了。Windows 用户下载 zip 包解压即用macOS/Linux 用户下载 tar.gz解压后把目录放到固定位置比如~/dev/tomcat/apache-tomcat-9.0.98。解压完成第一步就是把 bin 目录下的catalina.batWindows或catalina.shLinux打开检查JAVA_HOME是否指向 JDK 路径如果没设置启动时会直接报错。3.2 核心目录结构速览刚接触 Tomcat 的人容易把文件到处乱放其实它的目录结构非常固定bin启动、关闭脚本startup.sh/startup.bat、shutdown.sh/shutdown.batconf全局配置文件重点是server.xml、web.xml、tomcat-users.xmllibTomcat 自身的 jar 包不要把你的项目依赖 jar 扔进来logs运行日志排错的第一现场webapps部署目录war 包或解压后的应用放这里workJSP 编译后的临时文件目录遇到“改了代码不生效”可以先清这里3.3 server.xml 里我最常改的三处conf/server.xml是 Tomcat 的核心配置文件但绝大多数情况下你只需要关注三个点端口配置。默认 HTTP 端口是 8080如果被占用把Connector port8080 protocolHTTP/1.1 ... /里的端口号改掉。注意一次改三处HTTP 端口、redirectPort、以及Server port8005这是 Tomcat 的关闭指令端口。只改 HTTP 端口不改Server port容易出幺蛾子。编码配置。在 HTTP Connector 标签里加上URIEncodingUTF-8。本地部署前后端分离项目时URL 参数里如果带中文——比如搜索关键词——不加这个配置Tomcat 默认按 ISO-8859-1 解析请求参数直接乱码。部署目录配置。Host namelocalhost appBasewebapps ...里的appBase指向 webapps 目录。如果你希望前端静态资源从外部磁盘目录加载可以在这个 Host 节点下加Context path/front docBase/Users/me/project/front/dist reloadabletrue /这样访问http://localhost:8080/front/就是直接访问那个磁盘目录不用复制文件。3.4 启动脚本与 JVM 参数Windows 下用startup.bat启动macOS/Linux 下用startup.sh。但很多人的本地项目在 Tomcat 里跑起来后控制台中文乱码这大概率不是代码问题而是 JVM 默认字符集跟终端不一致。可以在catalina.sh的JAVA_OPTS里加上JAVA_OPTS-Dfile.encodingUTF-8 -Dsun.jnu.encodingUTF-8Windows 下还需要注意conf/logging.properties里java.util.logging.ConsoleHandler.encoding默认是 UTF-8但如果你的 Windows 控制台代码页是 GBK日志照样乱码把它改成GBK或者把控制台代码页切到 UTF-8 都行。这块我后面在问题排查章节会展开讲。4. IDEA 中配置 Tomcat本地部署的第一步4.1 关联本地 Tomcat用 IDEA 做本地部署我强烈建议先在 IDEA 里把 Tomcat 关联起来这样调试时能直接看到堆栈信息不用靠猜。打开 IDEA进入File - Settings - Build, Execution, Deployment - Application Servers点击选择Tomcat Server然后在Tomcat Home里选择你本地 Tomcat 解压目录Tomcat base directory通常会自动识别保持默认即可。这里有一点要注意IDEA 必须使用和项目 JDK 匹配的 Tomcat 版本否则部署时 IDEA 会提示版本不兼容。4.2 创建 Artifactwar 还是 war explodedTomcat 部署项目前IDEA 要求你指定一个 Artifact。对 war 包部署有两种形态war完整的 war 包部署时 Tomcat 会解压适合最终要扔到服务器上的场景war exploded展开的目录结构IDEA 直接把编译后的target/classes和静态资源映射给 Tomcat适合本地开发调试本地调试我用war exploded居多因为它配合reloadabletrue能实现改代码后自动热部署不用每次重启 Tomcat。IDEA 打开Project Structure - Artifacts - - Web Application: Exploded - From Modules选你对应的模块就行。4.3 配置 Deployment 上下文路径IDEA 的 Run/Debug Configurations 里Tomcat Server 的Deployment选项卡Application context默认是/也就是访问http://localhost:8080/直接进项目根。先后端联调时建议设置成有意义的上下文路径比如/api这样后端接口地址就是http://localhost:8080/api/xxx前端代理或请求路径也更清晰。设置上下文路径时一定要和后端项目的server.servlet.context-pathSpring Boot 配置保持一致否则会出现“接口访问 404 但配置文件里明明写了路径”的诡异情况。我在实际项目里就见过有人 IDEA 的 Deployment 上下文写了/api但 Spring Boot 的application.yml里也写了context-path: /api结果访问http://localhost:8080/api/api/login才通——这就是双重前缀叠加排查了半小时才反应过来。4.4 通过 IDEA 启动 Tomcat 后看不到日志IDEA 启动 Tomcat 后底部Console窗口应该能看到清晰的启动日志。如果你点了启动按钮后IDEA 面板里什么日志都没有多半是Tomcat Server - Logs里的日志级别或控制台配置问题。进入 Run 配置找到Logs选项卡勾选Show console when a message is printed to stdout然后把Tomcat Localhost Log、Tomcat Catalina Log、Tomcat Manager Log点成可用的状态日志就会正常输出了。5. 后端改造从 Spring Boot 内嵌容器到外部 Tomcat5.1 改造步骤jar 转 war 的三个关键点如果你原本是java -jar启动要改成部署到外部 TomcatSpring Boot 项目需要做三处改动缺一不可。第一处打包方式改为 war。在pom.xml的packaging标签里把jar改为war。第二处排除内嵌 Tomcat。Spring Boot 默认依赖内嵌 Tomcat如果不排除部署到外部 Tomcat 时同一个应用会有两个 Tomcat 在抢。在spring-boot-starter-web依赖里用exclusions排除spring-boot-starter-tomcatdependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-web/artifactId exclusions exclusion groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-tomcat/artifactId /exclusion /exclusions /dependency这里有个容易忽略的点排除内嵌 Tomcat 后本地用java -jar直接跑会报NoClassDefFoundError: ServletWebServerFactory因为内嵌容器没了。解决办法是给provided作用域加上 Tomcat 依赖让它只在编译时参与运行时不打包进去dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-tomcat/artifactId scopeprovided/scope /dependency第三处修改启动类。主类要继承SpringBootServletInitializer并重写configure方法。这一步的底层逻辑是外部 Tomcat 启动时不会像java -jar那样运行main方法它需要通过SpringBootServletInitializer这个类来引导 Spring 容器启动。onStartup钩子会由容器调用进而触发 Spring ApplicationContext 的初始化。注意这里继承的是哪个 Spring Boot 版本对应的类——Spring Boot 2.x 里这个类在org.springframework.boot.web.servlet.support包下别误导成 1.x 的org.springframework.boot.web.support包。SpringBootApplication public class Application extends SpringBootServletInitializer { Override protected SpringApplicationBuilder configure(SpringApplicationBuilder builder) { return builder.sources(Application.class); } }5.2 外部 Tomcat 下配置文件的加载顺序改成 war 包部署后配置文件的加载规则会变化。java -jar启动时application.yml在 classpath 根目录war 包部署时配置文件同样在WEB-INF/classes下如果application.yml在src/main/resources里打包后会在正确位置所以大部分情况下无需额外处理。但如果你部署到不同环境要覆盖配置优先级和 Java 进程启动不一样。外部 Tomcat 部署时常见做法是把外置配置文件放在 Tomcat 的conf目录下然后通过JAVA_OPTS指定JAVA_OPTS-Dspring.config.additional-location/opt/tomcat/conf/application-external.yml这里的spring.config.additional-location是 Spring Boot 2.4 的用法它会把外置配置追加到默认配置之后优先级更高。如果你用的是更早版本可以直接用--spring.config.location覆盖默认路径但这样默认的application.yml就不会被加载了容易把系统配置项弄丢。5.3 数据库配置加密本地部署的安全底线本地部署前后端分离项目时很多人习惯把数据库用户名密码明文写在application.yml里。本地自测无所谓但如果这个项目要分享给同事或打包给其他人部署明文密码就很不体面。推荐的做法是用jasypt-spring-boot-starter做配置加密。在application.yml里用ENC(...)标识加密后的密码然后在启动参数里传入解密密钥JAVA_OPTS-Djasypt.encryptor.passwordmySecretKey这个工具的坑主要体现在两点一是密钥必须和加密时使用的完全一致否则启动直接报Decryption failed二是某些 MySQL 驱动版本对连接串里的特殊字符敏感加密时如果明文密码里有、/、这类字符加密结果里通常是大写字母和数字不会出问题但要保证最终连接串本身没有非法转义。这个内容展开讲又是一篇文章本地部署阶段只需记住密钥永远不要提交进仓库用.env或本机环境变量管理。5.4 war 包名称与上下文路径的关系当你把打好的 war 包放进webapps目录后Tomcat 默认会把 war 包的文件名作为上下文路径。比如文件名是myapp.war访问地址就是http://localhost:8080/myapp/。如果希望根路径直接访问要么把 war 包命名为ROOT.war要么把原来的ROOT目录删掉再替换。这里有个很实际的建议不要指望通过修改 server.xml 的path属性来改变上下文路径虽然 Tomcat 在早期版本支持这么做但 Tomcat 9 之后对Context path属性的约束越来越严格部署在 Host 下的 Context 不允许指定path属性否则启动时直接抛异常。正规做法就是让 war 包的名字决定路径。IDEA 里部署war exploded时如果在 Deployment 配置里指定了 Application context那么 IDEA 启动时通常会自动调整部署名称这时候上下文路径由 IDEA 决定跟 war 包名字无关。6. 前端部署构建产物如何交给 Tomcat6.1 Vue/React 构建前的关键配置前端项目在构建之前必须处理接口请求的路径。以 Vue 项目为例开发环境下用的代理配置.env.development里常常是VUE_APP_BASE_API /api这个相对路径在部署后依然有效——只要前端静态资源和后端接口在同一个域下。但如果是独立部署前后端不同端口构建时就得用绝对路径或完整 URL。这里我踩过一次很深的坑前端项目构建时publicPath如果配置成/部署到 Tomcat 的/front/目录下访问前端页面时所有 JS/CSS 资源都会从根路径加载结果 404。解决办法是把publicPath设置为相对路径或者加上子路径。以 Vue CLI 为例module.exports { publicPath: process.env.NODE_ENV production ? /front/ : / }如果是 Vite 项目则是export default defineConfig({ base: process.env.NODE_ENV production ? /front/ : / })这个配置的本质是构建产物里的资源引用前缀必须和部署后的访问路径一致。你部署在/front/下资源就写成/front/static/js/xxx.js否则浏览器拿着/static/js/xxx.js去请求Tomcat 里根本没有这个路径。6.2 部署到 Tomcat 的三种形态对比部署形态操作方式优点缺点dist 内容复制到 webapps/ROOT每次构建后手动覆盖 ROOT 目录访问路径简洁根路径直接进需要人工操作易出错dist 整体复制为 webapps/front复制目录到 webapps 下路径清晰可多个前端并存上下文路径需要统一管理server.xml 虚拟目录映射修改 server.xmldocBase 指向磁盘目录前端构建完直接生效多人协作时配置易冲突我本地最常用的是第二种webapps/front。好处在于Tomcat 本来就是多应用部署的webapps/front和webapps/api可以同时存在路径一眼就看出哪里是静态资源、哪里是后端接口。生产环境迁到 Nginx 时也只需要把front目录原样搬到 Nginx 的 html 目录下。6.3 前端跨域的三大解法前后端分离部署到 Tomcat 后跨域是最容易冒出来的问题。所谓跨域本质是浏览器的同源策略在拦截不是 Tomcat 在拦截。请求发出去了服务器也收到了也会正常返回但浏览器发现响应头里缺少允许跨域的字段直接就吞掉了响应前端看到的就是blocked by CORS policy。第一层解法后端开启 CORS。Spring Boot 里写一个全局配置类Configuration public class CorsConfig implements WebMvcConfigurer { Override public void addCorsMappings(CorsRegistry registry) { registry.addMapping(/**) .allowedOriginPatterns(*) .allowedMethods(GET, POST, PUT, DELETE, OPTIONS) .allowedHeaders(*) .allowCredentials(true) .maxAge(3600); } }第二层解法前端通过代理转发。这个方案更适合在开发环境下使用生产环境意义不大因为代理转发的是开发服务器的地址而部署到 Tomcat 后前端静态资源已经不在 Node 服务下了。第三层解法把前后端放在同源下。也就是方案二里说的前端构建产物的 JS/CSS 走后端静态资源路径或者用 Tomcat 的虚拟目录让前端页面和后端接口同端口同域名。这就是为什么很多人明明没配跨域把前端打进 war 包后反而不报错了——不是跨域被解决了而是请求本来就是同源的。我的经验是本地部署阶段后端开 CORS 最省事生产环境优先想办法同源别人家 Nginx 在外层做反代或转发。7. 完整部署实操从零到能访问7.1 后端打包与上传在项目根目录执行mvn clean package -DskipTests打包完成后在target目录下会生成你的项目名.war。这个 war 包内部结构是标准的 Web 应用结构WEB-INF/classes放着编译后的 class 和资源配置文件WEB-INF/lib放着项目依赖的 jar 包。检查一下WEB-INF/classes/application.yml是否存在——如果这里没有配置文件war 包启动后会使用默认配置可能连数据库都连不上。把 war 包复制到 Tomcat 的webapps目录命名要谨慎。假设我想让它以/api路径访问就把 war 包改名为api.war。接着启动 Tomcatcd bin ./startup.sh启动后观察logs/catalina.out日志文件看到Deployed application at context path [/api]基本就说明后端部署成功。7.2 前端构建与部署以 Vue 项目为例npm install npm run build构建完成后dist目录里是index.html、static或assets等静态文件。在 Tomcat 的webapps下新建front目录把dist里的内容全部复制进去cp -R dist/* apache-tomcat-9.0.98/webapps/front/然后启动或重启 Tomcat访问http://localhost:8080/front/能看到前端页面。如果页面白屏但后端接口正常先按 F12 看 Network 面板确认 JS/CSS 资源的请求路径是不是 404路径 404 就回到上一节讲到的publicPath问题。7.3 联调验证三步走部署完之后不要急着到处点功能按这个顺序验证先直接访问一个后端接口确认后端接口本身可用。比如curl http://localhost:8080/api/user/list看返回的 JSON 数据是否正常。再访问前端页面确认页面能正常渲染资源不 404。最后在页面上触发一个调用后端接口的操作观察 Network 面板看请求的 URL、请求方法、请求头是否符合预期。第 3 步里最容易暴露问题的是OPTIONS预检请求。前端的 POST 请求如果带了Content-Type: application/json这种非简单请求头浏览器会先发一个OPTIONS预检如果你的后端接口没有正确处理OPTIONS预检失败后续真实请求根本不会发出前端看到的报错是Failed to fetch而不是 403/500。这个坑特别隐蔽因为有些后端的鉴权拦截器会对所有请求做认证直接 401 掉OPTIONS预检结果前端怎么调都失败后端日志却只有一行OPTIONS /xxx 401。7.4 用 Tomcat Manager 管理应用如果你需要频繁更新 war 包建议配置 Tomcat Manager 应用。编辑conf/tomcat-users.xml加入role rolenamemanager-gui/ role rolenamemanager-script/ user usernameadmin passwordadmin123 rolesmanager-gui,manager-script/然后访问http://localhost:8080/manager/html可以可视化地在页面上启动、停止、重新部署应用不用每次手动复制 war 包再重启 Tomcat。注意manager-gui权限只能从本机访问manager-script允许脚本远程调用如果只是本地用两个都配到本机就够了。多提一句Tomcat Manager 的manager-script接口允许通过 HTTP 方式部署 war 包curl -u admin:admin123 http://localhost:8080/manager/text/deploy?path/apiupdatetrue --upload-file api.war这个命令可以在不重启 Tomcat 的情况下热更新 war 包本地调试时比每次重启快得多。8. 常见问题与排查技巧实录把本地部署 Tomcat 时经常遇到、且网上答案往往只讲一半的问题整理在这。每个案例都是我实测过或亲眼见过的有通用性。8.1 启动后访问 404Tomcat 启动后访问http://localhost:8080/xxx报 404排查顺序要固定不要瞎猜。第一步确认应用是否真的部署了查看logs/catalina.out和logs/localhost.2025-xx-xx.log。后者专门记录应用部署情况如果有Exception sending context initialized event之类的信息说明应用启动中抛出异常通常页面会显示 Servlet 容器报错页而不会只是纯 404。第二步确认上下文路径。如果你的接口地址是http://localhost:8080/api/loginwar 包却是myapp.war那么实际路径是http://localhost:8080/myapp/login这就是 404 的最常见原因。第三步检查静态资源配置。有时应用能访问/api/xxx但页面或者静态资源请求 404看看 Spring Boot 的spring.mvc.static-path-pattern配置是否覆盖到了前端资源路径。8.2 Tomcat 乱码乱码问题分成两类启动日志乱码和页面响应乱码。启动日志乱码尤其是 Windows 下主要原因是日志输出编码和控制台编码不一致。Tomcat 9 的conf/logging.properties里默认ConsoleHandler.encoding UTF-8而 Windows 控制台大多数情况是 GBK双方不匹配就会出现中文注释或中文日志乱码。解决办法是改成java.util.logging.ConsoleHandler.encoding GBK页面响应乱码先检查是不是 HTTP 响应头没有指定 charset。Spring Boot 项目通常在application.yml里设置server.servlet.encoding.forcetrue这样所有响应用 UTF-8 编码。如果还不行用浏览器的开发者工具看 Network 面板里响应头的Content-Type: text/html;charsetUTF-8是否正确。还有一个隐蔽的乱码点在 Tomcat 解析 URL 参数上。前面说过URIEncodingUTF-8这个配置必须加在 Connector 上否则 GET 请求里的中文参数乱码是必现的。POST 请求体乱码则由CharacterEncodingFilter管理Spring Boot 默认装配了这个过滤器一般不会出问题。8.3 端口被占用启动时提示Address already in use: JVM_Bind或者null:8080说明 8080 被其他程序占了。Windows 下用netstat -ano | findstr 8080Linux/macOS 下用lsof -i :8080找到占用进程的 PID结束它或者改 Tomcat 的端口。这里有个容易忽视的场景上一次 Tomcat 没有正常关闭残留了 java 进程。比如用 CtrlC 强制终止 IDEA 里的 Tomcat另一个窗口里的 Tomcat 进程可能还活着端口占着不放。这种情况直接kill -9残留进程就行。8.4 内存溢出PermGen 与 Metaspace本地部署多项目后Tomcat 经常会出现内存溢出。老的 JDK 8 之前是java.lang.OutOfMemoryError: PermGen spaceJDK 8 用元空间报的是Metaspace。在catalina.sh的JAVA_OPTS里显式设置内存参数JAVA_OPTS-Xms512m -Xmx1024m -XX:MaxMetaspaceSize256m-Xms是初始堆大小-Xmx是最大堆大小这两个值不相等时JVM 会动态调整堆大小在高并发下可能频繁触发 Full GC。本地调试时直接让两者相等能减少 GC 停顿带来的干扰。MaxMetaspaceSize控制元空间上限类加载过多时不够用就报溢出设个 256m 基本够单项目跑。8.5 修改了代码但不生效这是最鬼的问题看起来像“改了 Java 代码重启 Tomcat 后还是老逻辑”。排查顺序第一确认 IDE 是否执行了Build。IDEA 里的 Tomcat 跑的是编译后的 class如果你改了代码没有触发编译Tomcat 加载的还是旧 class。执行Build - Rebuild Project再重启试试。第二清理 Tomcat 的work目录。JSP 编译缓存在这里但 Spring Boot 项目的接口代码不走 JSP所以问题一般不是缓存。真正可能缓存的是 IDE 部署时的临时副本。IDEA 部署war exploded时如果 output directory 和你的 target 目录不一致改代码后 IDEA 没有把新的编译结果同步过去也会出现“没生效”的假象。检查 IDEA Structure 里 Artifact 的 Output directory确认它指向的是你 Maven 编译的 target 目录。第三确认浏览器缓存。前端改了接口地址或代码HTML/JS 被浏览器缓存刷新页面加载的还是旧脚本在开发者工具里勾选 Disable cache或者用 CtrlF5 强制刷新。8.6 查看 Tomcat 进程状态的实用命令很多人在排错时喜欢一上来就改配置我建议先确认运行时状态。Linux/macOS 下ps -ef | grep tomcat这个命令能看到 Tomcat 进程的启动参数比如-Dcatalina.home指向哪个目录、-Dspring.config.location有没有生效。如果这些参数配置不对配置了但没加载的排查就是白忙活。如果在 macOS 上出现“Tomcat 启动后立即停止没有任何日志”大概率是$CATALINA_HOME环境变量没设。设置方式export CATALINA_HOME/路径/到/apache-tomcat-9.0.98Windows 上则是系统变量里加CATALINA_HOME并在Path中加入%CATALINA_HOME%\bin。9. 几个一直想强调的细节上面讲的都是操作层面的内容最后再补充几个我反复踩过、但经常被人忽略的细节。第一war 包的运行环境是独立于 IDE 的。你在 IDEA 里跑得很顺的项目打成 war 扔进 Tomcat 后如果发现某些配置没生效先在启动日志里确认 Tomcat 加载的到底是哪个目录下的配置文件。外部 Tomcat 启动时user.dir属性指向的是 Tomcat 的bin目录而不是你的项目目录所以一切依赖相对路径的配置文件都会以 Tomcat 目录为基准。第二本地部署和生产部署的差异要提前摸底。Tomcat 本地部署倒是顺利但生产环境往往有自己的运维规范——可能限定了 JVM 参数、可能要求用 systemd 服务管理可能不允许改动 server.xml。我在本地部署时经常会提前把生产环境常用的启动参数、日志轮转配置一并测一遍省得到生产环境再来回折腾。第三war 包部署后的热更新并不完全可靠。Tomcat Manager 的热部署在更新 war 包时会尝试重新加载应用的 ClassLoader但如果你的应用里有用到第三方库的静态状态、或者自己写了ThreadLocal缓存数据热部署后状态可能丢失或残留出现“更新后第一次访问报错再刷新就好了”这种诡异情况。本地调试没问题生产环境更新 war 包时最好还是选择一个低峰期重启。第四前后端分离部署时的日志要能串起来。本地部署前后端分离项目前端页面上报错后端日志里不一定有对应记录因为跨域请求的Origin头、前端报的 400/500 错误跟后端的异常栈不一定同时出现在同一个终端窗口。我习惯在本地部署时给后端的 Controller 层加一个简单的请求日志切面把每个请求的 URL、参数、耗时打出来前端出了问题直接翻后端日志看有没有请求进来——这一招排查跨域或参数问题特别有效。第五Tomcat 10 和 Spring Boot 的版本矩阵值得在项目开始前就定下来。如果你们项目是 Spring Boot 2.x局部用 Tomcat 9如果项目已经升级到 Spring Boot 3.x那 Tomcat 10.1 没问题。这事别拖到部署阶段才想起来因为发布包一旦打成 war内部依赖的 Servlet API 版本跟 Tomcat 不完全匹配时启动过程会报各种奇怪异常紧赶慢赶再改版本很可能来不及。本地部署前后端分离项目本身不難难点在于把“能跑”变成“稳定地跑、可排查地跑”。你把这些基础工作做扎实了无论后面是上生产还是交给同事维护都会省心很多。
上一篇/下一篇内容由系统自动关联 返回资讯列表 →