PAYATHON 2026

如何为 Java web 应用搭建适合多人协作的开发服务器?

技术老齐

建议每人维护一套独立环境,把配置纳入 Git 管理。对于这个学校项目,用 Docker Compose 管理 Tomcat 和 MySQL,在容器中完成 Maven 构建,就能满足大部分需求。Vagrant 也可以用,等到确实需要统一整套虚拟机操作系统时再引入。

日常开发不建议所有人共用同一个 Tomcat 和数据库。有人重启服务、部署尚未完成的代码或修改表结构,都可能影响其他成员。共享服务器更适合用来验收和演示。

先确定需要统一的内容

IDE 可以各自选择,团队需要统一的是以下内容:

内容应如何管理
JDK、Tomcat、MySQL在环境配置中指定版本
Maven、构建插件和依赖固定版本,提交构建配置
数据库结构提交建表或迁移脚本
开发数据提供可重复导入的少量测试数据
构建、启动和停止命令写进 README,尽量保持一致
密码和个人配置提供配置模板,实际值不提交

统一环境能减少差异,但不能解决所有问题。文件名大小写、字符编码、时区和数据库排序规则也可能影响结果。

注意 Servlet 与 Tomcat 的兼容性

如果课程示例使用 javax.servlet.*,需要选择兼容这一 API 的容器,例如 Tomcat 9。从 Tomcat 10 开始,相关规范 API 改为 jakarta.servlet.*,旧应用仅替换服务器还不能完成迁移。具体兼容关系可查阅 Tomcat 版本表和迁移说明。

下面以 JDK 17、Tomcat 10.1 和使用 jakarta.servlet.* 的 Maven WAR 项目为例。如果已有代码使用 javax.servlet.*,应先调整版本组合。

用 Docker Compose 建立可重复的环境

宿主机需要安装可用的容器运行环境、Docker Compose 和代码编辑工具。项目使用的 JDK、Maven、Tomcat 和 MySQL 可以全部放在容器里。

假设项目已经能通过 Maven 打包为 WAR,目录可以这样安排:

school-web/
├── pom.xml
├── src/
├── Dockerfile
├── compose.yaml
├── .dockerignore
├── .env.example
└── README.md

1. 让构建脱离 IDE

在 pom.xml 中声明 war 打包方式,配置 Java 编译目标,并指定依赖和构建插件的版本。Servlet API 依赖通常使用 provided,由 Tomcat 在运行时提供;应用还需要声明兼容的 MySQL Connector/J 依赖。

为方便后面复制 WAR,在现有 <build> 中设置:

<finalName>app</finalName>

这样预期的构建产物就是 target/app.war。这些配置统一交给 Maven 管理,避免每个人都在 IDE 中手动添加 JAR。

2. 在容器中构建 WAR,再部署到 Tomcat

创建 Dockerfile:

FROM maven:3-eclipse-temurin-17 AS build

WORKDIR /workspace
COPY pom.xml .
COPY src ./src
RUN mvn -B clean package

FROM tomcat:10.1-jdk17-temurin

COPY --from=build /workspace/target/app.war \
    /usr/local/tomcat/webapps/ROOT.war

EXPOSE 8080
CMD ["catalina.sh", "run"]

这个示例适用于单模块项目。如果构建还依赖其他模块或项目配置文件,需要一并复制。

示例中的镜像标签会随维护更新。团队正式使用时,应将所有镜像固定为核实过的完整版本;要求更严格时,可以使用镜像 digest,并通过提交记录统一升级。对应标签可查阅 Maven 镜像说明和 Tomcat 镜像说明。

创建 .dockerignore,避免把无关文件发送给构建器:

.git
target
.env
.idea
*.iml

3. 配置 Tomcat 和 MySQL

创建 compose.yaml:

services:
  db:
    image: mysql:8.4
    environment:
      MYSQL_DATABASE: school
      MYSQL_USER: school
      MYSQL_PASSWORD: ${DB_PASSWORD:?Set DB_PASSWORD in .env}
      MYSQL_ROOT_PASSWORD: ${DB_ROOT_PASSWORD:?Set DB_ROOT_PASSWORD in .env}
    volumes:
      - mysql-data:/var/lib/mysql
    healthcheck:
      test:
        - CMD-SHELL
        - >-
          MYSQL_PWD="$${MYSQL_PASSWORD}"
          mysql --protocol=TCP -h 127.0.0.1
          -u "$${MYSQL_USER}" "$${MYSQL_DATABASE}"
          -e "SELECT 1" >/dev/null
      interval: 5s
      timeout: 5s
      retries: 30
      start_period: 30s

  web:
    build: .
    ports:
      - "127.0.0.1:8080:8080"
    environment:
      DB_URL: jdbc:mysql://db:3306/school
      DB_USER: school
      DB_PASSWORD: ${DB_PASSWORD:?Set DB_PASSWORD in .env}
    depends_on:
      db:
        condition: service_healthy

volumes:
  mysql-data:
  • 应用通过 db:3306 连接数据库。Compose 网络内可以使用服务名访问数据库,localhost 则指向应用容器自身。具体寻址方式见 Compose 网络文档。
  • DB_URL、DB_USER 和 DB_PASSWORD 是自行约定的应用配置名。Servlet 应用需要读取这些变量,例如通过 System.getenv() 配置连接池,Tomcat 不会自动完成这一步。
  • 普通 depends_on 只保证启动顺序。示例将数据库健康检查与 service_healthy 配合使用,等待数据库能够接受查询后再启动应用。应用仍需处理运行期间的连接中断。两者的区别见 Compose 启动顺序文档。
  • MySQL 没有发布宿主机端口,需要使用命令行客户端时,可以直接进入数据库容器。

创建 .env.example:

DB_PASSWORD=replace-with-a-local-development-password
DB_ROOT_PASSWORD=replace-with-another-local-password

成员首次使用时复制一份:

cp .env.example .env

修改本地密码,并把 .env 加入 .gitignore。示例通过环境变量传递开发凭据,生产环境的凭据管理需要另行设计。

4. 启动和更新应用

在项目目录执行:

docker compose up --build -d
docker compose logs -f web

Tomcat 启动并成功部署应用后,就可以通过 http://localhost:8080/ 访问对应页面或 Servlet 路径。ROOT.war 表示部署到根上下文,是否有首页取决于应用代码。

修改代码后,重新构建:

docker compose up --build -d web

这套基础流程容易理解和复现,但不会自动同步源码或热部署。等团队熟悉构建和部署过程后,再优化更新速度。

使用容器里的 MySQL 客户端:

docker compose exec db mysql -u school -p school

停止环境:

docker compose down

命名卷会保留数据库数据。docker compose down -v 会删除这里定义的数据库卷,只应在确认需要清空本地数据时使用。

数据库也需要随代码演进

除了统一 MySQL 版本,团队还需要保持表结构一致。项目初期可以提交建表 SQL 和测试数据;表结构开始持续变化后,可以使用 Flyway、Liquibase 等迁移工具记录每次修改,避免在群里通知大家手工执行 SQL。

MySQL 官方镜像会在首次初始化空数据目录时,执行 /docker-entrypoint-initdb.d 下的初始化脚本。已有数据库卷时,重启容器不会重新执行这些脚本,因此不能用它们处理后续迁移。初始化条件见 MySQL 镜像文档。

IDE 能否自动使用虚拟机或容器里的 Tomcat?

可以预先保存配置,具体能做到什么取决于 IDE、版本和插件。启动、部署和调试是三个不同的操作:

  1. 启动 Tomcat。
  2. 把 WAR 部署到 Tomcat。
  3. 连接 JVM 进行断点调试。

上面的 Compose 和 Dockerfile 已经负责前两项。IDE 可以只用来编辑代码,启动环境时无需重新添加服务器。

需要远程调试时,可以将以下配置合并到 web 服务中,保留原有数据库环境变量和 HTTP 端口映射:

command: ["catalina.sh", "jpda", "run"]
environment:
  JPDA_ADDRESS: "*:5005"
  JPDA_TRANSPORT: "dt_socket"
ports:
  - "127.0.0.1:5005:5005"

然后在 IDE 中保存一个连接 localhost:5005 的远程 JVM 调试配置。Tomcat 的启动脚本支持这些 JPDA 设置,详见 catalina.sh。

本地调试源码应与已部署代码一致,否则断点位置可能不匹配。调试端口只在需要时开启,并限制在本机访问。

如果团队统一使用某个 IDE,可以提交它支持共享的运行配置,注意不要包含个人绝对路径。如果各自选择 IDE,就统一命令、端口和配置约定,每个人保存一次连接设置即可。启动虚拟机本身无法替任意 IDE 自动注册 Tomcat。

容器能解决构建和运行环境问题,但宿主机上的 Java IDE 要提供完整索引和编译检查,可能仍需项目 JDK。如果希望开发工具也使用容器内的 JDK,需要选择支持容器或远程开发的 IDE 方案。

什么时候值得使用 Vagrant?

如果课程要求学习 Linux 管理,或者团队需要统一完整的操作系统环境,Vagrant 就比较合适。可以按以下步骤配置:

  1. 选择支持成员操作系统、CPU 架构及虚拟化 provider 的基础 box。
  2. 在 Vagrantfile 中固定 box 版本,配置项目共享目录和端口转发。
  3. 使用 provisioning 脚本安装指定工具,并完成首次配置。
  4. 让成员通过 vagrant up 创建环境,通过 vagrant ssh 进入环境。

借助 Vagrant 的共享目录机制,成员可以在宿主机编辑代码,在虚拟机内编译和运行。

也可以在 Vagrant 虚拟机里运行 Docker Compose,由 Vagrant 管理 Linux 虚拟机,Compose 管理应用服务。这需要额外处理虚拟机资源和共享目录,还要配置容器到虚拟机、虚拟机到宿主机的端口映射。对于刚开始的 Servlet/JSP 项目,这些工作未必值得。

如果采用这一组合,建议虚拟机内只安装容器运行环境,Tomcat 和 MySQL 统一交给 Compose 管理,减少重复维护。

题目中的第三方 Vagrant 项目可以用来参考配置思路。采用前应检查基础系统、软件版本和 provider 支持情况,并实际核实是否适合当前成员的机器。

多人协作还需要哪些约定?

配好环境后,还需要做好以下工作:

  • 使用 Git 协作,每个成员保留自己的工作副本,通过分支和代码审查合并修改。
  • 在 README 中记录从新机器到首次成功访问应用的完整步骤,包括数据库初始化方法。
  • 在 CI 中使用固定的构建环境执行编译和测试。需要真实数据库的集成测试,应单独准备数据库服务。
  • 准备一套共享验收环境,部署明确的提交或构建产物,供团队检查集成结果。

如果成员电脑无法运行这些环境,也可以使用共享 Linux 服务器。每位成员都应有独立的工作目录和 Compose 项目,并使用不同端口;仅设置不同项目名,无法自动解决宿主机端口冲突。

开始时,先跑通克隆仓库、填写本地配置和执行一条启动命令的流程,再请另一位成员从空环境完整复现一次。过程中发现的手工步骤,都应补进配置或 README。

备注:内容仅供参考。