Java Web项目部署完整笔记:Windows单体/聚合项目 + 云服务器部署指南
整理了一下近期做Java Web项目部署的实操笔记,从最基础的Windows本地单体SSM项目,到多模块聚合项目,再到云服务器上线,把核心步骤和容易踩坑的地方都补全了,留作复盘也分享给大家。
一、Windows 环境:单体 SSM 项目部署
以经典的SSM项目为例,单体项目是最基础的部署形态,核心逻辑可以概括为:Maven打war包 → 放入Tomcat容器 → 启动验证访问。
1. 先搞懂:Maven 打包的核心逻辑
Maven是我们打包的核心工具,先理清两个核心点:标准生命周期和打包产物结构。
(1)Maven 标准生命周期
执行打包命令时,Maven会按顺序执行以下阶段,前一个执行完成才会进入下一个:
clean:清理上一次打包生成的target目录validate:校验项目结构是否正确、所有必要信息是否完整compile:编译项目的源代码test:运行项目中的单元测试package:将编译后的代码打包成可分发的格式(Web项目对应war包)verify:运行合规检查,确保包符合质量标准install:将打好的包安装到本地仓库,供本地其他项目依赖使用site:生成项目的站点文档deploy:将最终的包部署到远程私服仓库
日常部署最常用的命令是 mvn clean install,可以一键完成清理、编译、测试、打包并安装到本地仓库。支撑这套流程的是Maven的三大核心:插件 (执行每个阶段的具体逻辑)、依赖项 (项目引入的第三方jar包)、仓库 (本地/中央/远程仓库,存放依赖和自建包)。

(2)打包后的 target 目录结构
执行完install后,项目根目录会生成target文件夹,里面是打包的全部产物:
classes:编译后的Java字节码文件和项目资源文件generated-sources/generated-test-sources:自动生成的源码目录test-classes:编译后的测试类文件surefire-reports:单元测试执行结果报告项目名.war:最终用于部署的应用包,比如ssm.war

2. 部署到 Tomcat 完整步骤
- 找到本地Tomcat安装目录,进入
webapps文件夹(这是Tomcat默认的应用部署目录) - 把target目录下生成的
ssm.war复制粘贴到webapps目录中 - 进入Tomcat的
bin目录,双击startup.bat启动Tomcat服务 - 启动过程中Tomcat会自动解压war包,生成同名的项目文件夹
- 打开浏览器访问:
http://localhost:8080/ssm(8080是Tomcat默认HTTP端口,ssm对应你的项目名)
💡 小提示:Tomcat默认端口是8080,如果启动失败大概率是端口被占用,可以去
conf/server.xml里修改Connector的port值。


二、Windows 环境:聚合项目部署
聚合项目就是一个父工程下管理多个子模块,每个子模块可以独立打包成一个war包,对应不同的业务系统(比如后台管理端、文件服务、门户前端)。
1. 打包:和单体项目逻辑一致
在父工程根目录执行mvn clean install,Maven会按依赖顺序自动编译所有子模块,最终每个子模块的target目录下都会生成自己独立的war包,例如:
hami-console-1.0-SNAPSHOT.war(管理控制台端)hami-file-1.0-SNAPSHOT.war(文件服务模块)hami-portal-1.0-SNAPSHOT.war(用户门户端)

2. 重点:多项目共存的 Tomcat 配置
如果把多个war包都丢进同一个Tomcat的webapps、共用同一个端口,会出现访问路径冲突、资源竞争的问题。更稳妥的方案是在一个Tomcat里配置多个Service,每个Service对应一个项目、独占一个端口。
配置文件位置:Tomcat安装目录/conf/server.xml

配置核心规则(踩坑总结)
- 每个
<Service>的name属性必须全局唯一 - 每个Service内的HTTP端口、AJP端口都不能和其他Service重复
- 每个Service内的
<Engine>的name要和Service名称对应 <Context>的docBase要和webapps下的war包名(或解压后的文件夹名)完全一致
完整配置示例
<!-- 第一个项目:管理控制台端 -->
<Service name="Catalina1">
<!-- HTTP端口:8085 -->
<Connector port="8085" protocol="HTTP/1.1"
connectionTimeout="20000"
redirectPort="8443" URLEncoding="UTF-8"/>
<!-- AJP端口:8010 -->
<Connector port="8010" protocol="AJP/1.3" redirectPort="8443" secretRequired=""/>
<Engine name="Catalina1" defaultHost="localhost">
<Realm className="org.apache.catalina.realm.LockOutRealm">
<Realm className="org.apache.catalina.realm.UserDatabaseRealm"
resourceName="UserDatabase"/>
</Realm>
<Host name="localhost" appBase="webapps"
unpackWARs="true" autoDeploy="true">
<Valve className="org.apache.catalina.valves.AccessLogValve" directory="logs"
prefix="localhost_access_log" suffix=".txt"
pattern="%h %l %u %t "%r" %s %b" />
<!-- docBase对应war包名,path="/"表示直接用根路径访问 -->
<Context docBase="hami-console-1.0-SNAPSHOT" path="/" reloadable="true"/>
</Host>
</Engine>
</Service>
<!-- 第二个项目:用户门户端 -->
<Service name="Catalina2">
<Connector port="8082" protocol="HTTP/1.1"
connectionTimeout="20000"
redirectPort="8443" URLEncoding="UTF-8"/>
<Connector port="8011" protocol="AJP/1.3" redirectPort="8443" secretRequired=""/>
<Engine name="Catalina2" defaultHost="localhost">
<Realm className="org.apache.catalina.realm.LockOutRealm">
<Realm className="org.apache.catalina.realm.UserDatabaseRealm"
resourceName="UserDatabase"/>
</Realm>
<Host name="localhost" appBase="webapps"
unpackWARs="true" autoDeploy="true">
<Valve className="org.apache.catalina.valves.AccessLogValve" directory="logs"
prefix="localhost_access_log" suffix=".txt"
pattern="%h %l %u %t "%r" %s %b" />
<Context docBase="hami-portal-1.0-SNAPSHOT.war" path="/" reloadable="true"/>
</Host>
</Engine>
</Service>
<!-- 第三个项目:文件服务模块 -->
<Service name="Catalina3">
<Connector port="8080" protocol="HTTP/1.1"
connectionTimeout="20000"
redirectPort="8443" URLEncoding="UTF-8"/>
<Connector port="8012" protocol="AJP/1.3" redirectPort="8443" secretRequired=""/>
<Engine name="Catalina3" defaultHost="localhost">
<Realm className="org.apache.catalina.realm.LockOutRealm">
<Realm className="org.apache.catalina.realm.UserDatabaseRealm"
resourceName="UserDatabase"/>
</Realm>
<Host name="localhost" appBase="webapps"
unpackWARs="true" autoDeploy="true">
<Valve className="org.apache.catalina.valves.AccessLogValve" directory="logs"
prefix="localhost_access_log" suffix=".txt"
pattern="%h %l %u %t "%r" %s %b" />
<Context docBase="hami-file-1.0-SNAPSHOT.war" path="/" reloadable="true"/>
</Host>
</Engine>
</Service>
配置完成后启动Tomcat,三个项目会同时启动,分别通过不同端口访问:
- 管理控制台:
http://localhost:8085 - 用户门户端:
http://localhost:8082 - 文件服务:
http://localhost:8080
💡 补充说明:
URLEncoding="UTF-8"建议一定要加上,否则GET请求传中文会出现乱码;unpackWARs="true"表示启动时自动解压war包,autoDeploy="true"开启应用热部署能力。

三、云服务器部署:单体/聚合项目通用
把项目放到云服务器上运行,核心流程和Windows本地几乎一致,最大的区别是前置环境安装 和访问方式,还有几个非常容易忽略的网络配置坑。
1. 前置:服务器基础环境安装
先在云服务器上把基础环境装好,版本尽量和本地开发环境保持一致,避免兼容性问题:
- JDK:推荐和本地同版本(比如JDK8),配置好JAVA_HOME环境变量
- MySQL:安装对应版本的数据库,导入项目需要的库表和数据
- Tomcat:和本地版本保持一致,上传解压即可使用
如果是Linux服务器,一般用tar.gz解压安装的方式,环境变量配置在
/etc/profile中;Windows服务器操作就和本地完全一致。
2. 部署流程
- 把本地打好的war包,通过Xshell、FileZilla等工具上传到服务器Tomcat的
webapps目录 - 如果是聚合项目,按照上面的方法修改服务器上的
conf/server.xml,配置多Service多端口 - 修改项目内的配置:比如数据库连接地址,从
localhost:3306改成服务器地址(推荐用内网IP) - 启动Tomcat(Linux下执行
bin/startup.sh,Windows下双击startup.bat)
3. 访问方式
本地调试用的是localhost,服务器上就替换成服务器公网IP + 端口号 ,例如:
http://120.xx.xx.xx:8080/项目名
4. 最容易踩的坑:网络与权限
- 安全组端口放行:云服务器控制台的安全组规则里,一定要放开所有用到的端口(比如8080、8082、8085、3306等),否则浏览器完全访问不到
- 服务器防火墙:Linux的firewalld、iptables,Windows的系统防火墙,都要放行对应端口
- MySQL远程访问:如果需要远程连接服务器上的MySQL,需要开启对应用户的远程访问权限
- 路径兼容问题 :Linux的路径分隔符是
/,Windows是\,项目里如果写死了文件路径,一定要改成适配Linux的格式
四、常见问题快速排查
- Tomcat启动闪退:大概率是JDK环境变量配置错误,或者端口被占用,去logs目录查看catalina日志可以精准定位
- 访问返回404:检查Context的docBase和path是否写错,war包是否正常解压完成
- 页面中文乱码 :server.xml的Connector节点加上
URLEncoding="UTF-8",同时检查项目内的编码过滤器是否正常配置 - 数据库连接失败:核对连接地址、端口、账号密码,确认服务器MySQL是否允许远程访问,以及安全组3306端口是否放行
整体下来,部署的核心逻辑其实并不复杂,就是「打包 → 放入容器 → 调整配置 → 启动验证」,麻烦的大多是各种环境差异和配置细节,多踩两次坑自然就熟练了。