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
模块,需两步操作:
- 设置可见性(visibility):允许其他包引用该库
java_library (
name = "greeter",
srcs = glob(["src/main/java/com/baeldung/*.java"]),
visibility = ["//bazelapp:__pkg__"]
)
//bazelapp:__pkg__
表示bazelapp
包下的所有 target 都可访问- 更严格的可设为
//bazelapp:BazelApp
- 在依赖方声明 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_jar
的 name
一致。
5.2. 使用 Maven 仓库(推荐方式)
手动管理多个 http_jar
很麻烦。推荐使用 rules_jvm_external
统一管理 Maven 依赖。
- 在
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,
)
- 配置 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"
]
)
- 在
BUILD
文件中引用:
deps = ["//bazelgreeting:greeter", "@maven//:org_apache_commons_commons_lang3"]
✅ Bazel 会将 Maven 坐标自动转换为下划线命名格式:groupId_artifactId
。
这种方式更清晰、易维护,适合中大型项目。
6. 总结
本文通过一个简单的多模块 Java 项目,演示了如何使用 Bazel 替代 Maven/Gradle 进行构建。
核心要点回顾:
- ✅
WORKSPACE
和BUILD
是 Bazel 的配置核心 - ✅
java_binary
、java_library
定义构建目标 - ✅
deps
和visibility
控制模块依赖 - ✅
http_jar
和rules_jvm_external
管理外部依赖 - ✅
_deploy.jar
快速生成可部署的 fat jar
Bazel 的优势在于精准的依赖分析、高效的增量构建和跨语言支持,特别适合大型单体或微服务架构项目。
示例代码已托管至 GitHub:https://github.com/bazel-tutorials/java-multi-module,包含完整的 Maven 与 Bazel 双构建配置,欢迎 clone 学习。