1. 概述

Bazel 是一个开源的构建与测试工具,功能上类似于 Maven 和 Gradle。✅ 它最大的优势在于支持多语言项目,并能为多个平台生成构建产物

本文将带你一步步使用 Bazel 构建一个简单的 Java 应用。为了更贴近实际场景,我们会从一个典型的多模块 Maven 项目出发,逐步迁移到 Bazel 构建体系。

动手之前,先确保你已经安装好 Bazel。安装指南可参考官方文档:Bazel 安装


2. 项目结构

我们先构建一个多模块 Maven 项目,结构如下:

bazel (根目录)
    pom.xml
    WORKSPACE (bazel workspace)
    |— bazelapp
        pom.xml
        BUILD (bazel build file)
        |— src
            |— main
                |— java
            |— test
                |— java
    |— bazelgreeting
        pom.xml
        BUILD (bazel build file)
        |— src
            |— main
                |— java
            |— test
                |— java

关键点说明:

  • WORKSPACE 文件:标志着 Bazel 工作区的根目录。整个项目只需一个顶层 WORKSPACE 文件即可。
  • BUILD 文件:定义构建规则(build rules),每个规则通过唯一的 target name 标识。
  • ✅ 构建包(build package):每个包含 BUILD 文件的目录,都可以看作一个独立的构建单元。

Bazel 的设计非常灵活,你可以根据需要在任意层级添加 BUILD 文件,实现细粒度的构建控制。本文为简化演示,只在模块级别设置最小化的 BUILD 文件。


3. 构建文件(BUILD)

3.1. 规则配置

我们先为 bazelapp 模块编写第一个构建规则:

java_binary (
    name = "BazelApp",
    srcs = glob(["src/main/java/com/baeldung/*.java"]),
    main_class = "com.baeldung.BazelApp"
)

各字段含义如下:

  • java_binary:构建可执行 Java 程序的规则,需配合其他属性使用
  • name:构建目标名称,用于命令行调用
  • srcs:源码路径匹配模式,glob 支持通配符
  • main_class:程序入口类(可选,但运行时必须指定)

⚠️ 注意:srcs 不会自动包含子目录,如需递归匹配,应使用 **,例如 glob(["src/main/java/com/baeldung/**/*.java"])

3.2. 执行构建

进入包含 WORKSPACE 的根目录,执行构建命令:

$ bazel build //bazelapp:BazelApp

目标格式说明:

  • //:从 workspace 根目录开始
  • bazelapp:相对路径,指向 BUILD 文件所在目录
  • BazelApp:目标名称(即 name 属性值)

这个写法类似于坐标定位,简单粗暴但非常清晰。

3.3. 构建产物

构建成功后,会在 bazel-bin/ 目录下生成两个文件:

bazel-bin/bazelapp/BazelApp.jar
bazel-bin/bazelapp/BazelApp
  • BazelApp.jar:包含编译后的所有 class 文件
  • BazelApp:可执行脚本,用于启动 jar(带 classpath 等参数)

你可以直接运行脚本:./bazel-bin/bazelapp/BazelApp

3.4. 可部署的 Fat Jar

如果需要将应用及其依赖打包成一个独立的 jar 文件用于部署,Bazel 提供了便捷方式:

$ bazel build //bazelapp:BazelApp_deploy.jar

✅ 只需在目标名后加上 _deploy.jar 后缀,Bazel 就会自动把所有依赖打成一个“胖jar”(fat jar),开箱即用。


4. 模块依赖管理

目前我们只构建了单个模块。真实项目中,模块间依赖才是常态。

4.1. 构建库模块

我们先让 bazelapp 依赖另一个模块 bazelgreeting

bazelgreeting 模块创建 BUILD 文件,使用 java_library 规则:

java_library (
    name = "greeter",
    srcs = glob(["src/main/java/com/baeldung/*.java"])
)

执行构建:

$ bazel build //bazelgreeting:greeter

输出:

INFO: Found 1 target...
Target //bazelgreeting:greeter up-to-date:
  bazel-bin/bazelgreeting/libgreeter.jar

生成的是 libgreeter.jar,命名规则为 lib<name>.jar

4.2. 配置依赖关系

要让 bazelapp 使用 greeter 模块,需两步操作:

  1. 设置可见性(visibility):允许其他包引用该库
java_library (
    name = "greeter",
    srcs = glob(["src/main/java/com/baeldung/*.java"]),
    visibility = ["//bazelapp:__pkg__"]
)
  • //bazelapp:__pkg__ 表示 bazelapp 包下的所有 target 都可访问
  • 更严格的可设为 //bazelapp:BazelApp
  1. 在依赖方声明 deps
java_binary (
    name = "BazelApp",
    srcs = glob(["src/main/java/com/baeldung/*.java"]),
    main_class = "com.baeldung.BazelApp",
    deps = ["//bazelgreeting:greeter"]
)

deps 字段用于声明依赖的 target,格式为 //<path>:<target>

构建 BazelApp 时,Bazel 会自动先构建 greeter 并将其 jar 加入 classpath。


5. 外部依赖管理

除了内部模块依赖,项目通常还需要引入第三方库。Bazel 支持多种方式管理外部依赖:

  • 本地依赖:同一 workspace 内或其他 workspace 的模块(已演示)
  • HTTP 存档:从远程 URL 下载 jar
  • Maven 仓库:通过 rules_jvm_external 接入中央仓库

5.1. 通过 HTTP 直接引入 JAR

以引入 Apache Commons Lang 为例,在 WORKSPACE 文件中配置:

load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_jar")

http_jar (
    name = "apache-commons-lang",
    url = "https://repo1.maven.org/maven2/org/apache/commons/commons-lang3/3.12.0/commons-lang3-3.12.0.jar"
)

然后在 bazelapp/BUILD 中添加依赖:

deps = ["//bazelgreeting:greeter", "@apache-commons-lang//jar"]

⚠️ 注意引用格式:@<name>//jar,其中 <name> 必须与 http_jarname 一致。

5.2. 使用 Maven 仓库(推荐方式)

手动管理多个 http_jar 很麻烦。推荐使用 rules_jvm_external 统一管理 Maven 依赖。

  1. WORKSPACE 中引入 rules_jvm_external
load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive")

RULES_JVM_EXTERNAL_TAG = "2.0.1"
RULES_JVM_EXTERNAL_SHA = "55e8d3951647ae3dffde22b4f7f8dee11b3f70f3f89424713debd7076197eaca"

http_archive(
    name = "rules_jvm_external",
    strip_prefix = "rules_jvm_external-%s" % RULES_JVM_EXTERNAL_TAG,
    sha256 = RULES_JVM_EXTERNAL_SHA,
    url = "https://github.com/bazelbuild/rules_jvm_external/archive/%s.zip" % RULES_JVM_EXTERNAL_TAG,
)
  1. 配置 Maven 仓库和依赖项:
load("@rules_jvm_external//:defs.bzl", "maven_install")

maven_install(
    artifacts = [
        "org.apache.commons:commons-lang3:3.12.0"
    ],
    repositories = [
        "https://repo1.maven.org/maven2"
    ]
)
  1. BUILD 文件中引用:
deps = ["//bazelgreeting:greeter", "@maven//:org_apache_commons_commons_lang3"]

✅ Bazel 会将 Maven 坐标自动转换为下划线命名格式:groupId_artifactId

这种方式更清晰、易维护,适合中大型项目。


6. 总结

本文通过一个简单的多模块 Java 项目,演示了如何使用 Bazel 替代 Maven/Gradle 进行构建。

核心要点回顾:

  • WORKSPACEBUILD 是 Bazel 的配置核心
  • java_binaryjava_library 定义构建目标
  • depsvisibility 控制模块依赖
  • http_jarrules_jvm_external 管理外部依赖
  • _deploy.jar 快速生成可部署的 fat jar

Bazel 的优势在于精准的依赖分析、高效的增量构建和跨语言支持,特别适合大型单体或微服务架构项目。

示例代码已托管至 GitHub:https://github.com/bazel-tutorials/java-multi-module,包含完整的 Maven 与 Bazel 双构建配置,欢迎 clone 学习。


原始标题:Building Java Applications with Bazel

« 上一篇: Java 中的 XOR 操作符
» 下一篇: Spring Cloud 教程