前语

依据官网的界说,Bazel是类似于Make,Maven和Gradle的开源构建和测试东西。它运用人类可读的高级构建语言Starlark(一种根据python的方言)。 Bazel支撑多种语言的项目,并为多种渠道构建输出。

从我个人视点来看,bazel是一个强壮且复杂的构建系统,经过build rule的概念,支撑多种语言、不同渠道,支撑构建C/C++,Java,Android,IOS,Golang,Nodejs,Docker项目

本文的目的是运用bazel去构建并运转一个spring boot项目。

装备bazel编译java项目

在项目根目录中创立.bazelrc文件,设置bazel运用java17构建:

build --java_language_version=17 --java_runtime_version=17 --tool_java_language_version=17 --tool_java_runtime_version=17
test  --java_language_version=17 --java_runtime_version=17 --tool_java_language_version=17 --tool_java_runtime_version=17

在根目录中创立workspace文件,并引进相关的java依靠:

load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive")
RULES_JVM_EXTERNAL_TAG = "6.0"
RULES_JVM_EXTERNAL_SHA = "c44568854d8bb92fe0f7dd6b1e8957ae65e45e32a058727fcf62aaafbd36f17b"
http_archive(
    name = "rules_jvm_external",
    strip_prefix = "rules_jvm_external-%s" % RULES_JVM_EXTERNAL_TAG,
    sha256 = RULES_JVM_EXTERNAL_SHA,
    urls = [
        "https://github.com/bazelbuild/rules_jvm_external/archive/%s.zip" % RULES_JVM_EXTERNAL_TAG,
        "https://mirror.ghproxy.com/https://github.com/bazelbuild/rules_jvm_external/archive/%s.zip" % RULES_JVM_EXTERNAL_TAG,
    ]
)
load("@rules_jvm_external//:repositories.bzl", "rules_jvm_external_deps")
rules_jvm_external_deps()
load("@rules_jvm_external//:setup.bzl", "rules_jvm_external_setup")
# rules_jvm_external_setup()
load("@rules_jvm_external//:specs.bzl", "maven")
load("@rules_jvm_external//:defs.bzl", "maven_install")

然后运用运用maven_install导入spring boot项目的相关依靠。
这儿的shiro依靠被我独自拎出来做特别处理去兼容java17

shiros = [
    maven.artifact(
        group = "org.apache.shiro",
        artifact = "shiro-spring",
        version = "1.13.0",
        classifier = "jakarta",
        exclusions = [
            maven.exclusion(
                  group = "org.apache.shiro",
                  artifact = "shiro-core",
            ),
            maven.exclusion(
                  group = "org.apache.shiro",
                  artifact = "shiro-web",
            ),
        ]
    ),
    maven.artifact(
        group = "org.apache.shiro",
        artifact = "shiro-core",
        version = "1.13.0",
        classifier = "jakarta",
    ),
    maven.artifact(
        group = "org.apache.shiro",
        artifact = "shiro-web",
        version = "1.13.0",
        classifier = "jakarta",
    ),
]
maven_install(
    artifacts = [
        "org.springframework.boot:spring-boot:3.2.2",
        "org.springframework.boot:spring-boot-starter:3.2.2",
        "org.springframework.boot:spring-boot-loader-tools:3.2.2",
        "org.springframework.boot:spring-boot-loader:3.2.2",
        "org.springframework.boot:spring-boot-starter:3.2.2",
        "org.springframework.boot:spring-boot-starter-web:3.2.2",
        "org.springframework.boot:spring-boot-starter-jdbc:3.2.2",
        "org.springframework.boot:spring-boot-starter-quartz:3.2.2",
        "org.springframework.boot:spring-boot-starter-aop:3.2.2",
        #"org.springframework.boot:spring-boot-configuration-processor:3.2.2",
        "io.springfox:springfox-boot-starter:3.0.0",
        "com.auth0:java-jwt:3.19.4",
        "org.postgresql:postgresql:42.4.0",
        "jakarta.servlet:jakarta.servlet-api:6.0.0",  
        'javax.annotation:javax.annotation-api:1.3.2',
        "org.springframework.boot:spring-boot-devtools:3.2.2",
        "org.springframework.boot:spring-boot-starter-test:3.2.2"
    ] + shiros,
    fetch_sources = True,
    repositories = [
        "https://maven.aliyun.com/repository/public/",
        "https://maven.aliyun.com/nexus/content/groups/public/",
        "http://uk.maven.org/maven2",
        "https://maven.google.com",
        "https://repo1.maven.org/maven2",
    ],
    # maven_install_json = "//:maven_install.json",
)

maven_install_json = "//:maven_install.json"现在是被注视掉的,这是因为咱们需求主动生成改文件:

bazel run @maven//:pin

履行成功之后需求在workspace中给maven_install增加maven_install_json特点, 并将加载@maven//:defs.bzl中的pinned_maven_install装备

maven_install(
    artifacts = # ...,
    repositories = # ...,
    maven_install_json = "@//:maven_install.json",
)
load("@maven//:defs.bzl", "pinned_maven_install")
pinned_maven_install()

在更新maven依靠的话,那么能够运用下面的依靠更新maven_install.json

bazel run @unpinned_maven//:pin

maven_install_json是能够不必装备的,可是我引荐尽量装备。它有两个优点,可重复性和速度(reproducibility and speed)。

在根目录创立BUILD.bazel进行编译java项目的预备:

oad("@rules_java//java:defs.bzl", "java_binary", "java_library", "java_test")
package(default_visibility = ["//visibility:public"])
java_library(
    name = "jobs-lib",
    srcs = glob([
        "src/main/java/org/daming/jobs/*.java",
        "src/main/java/org/daming/jobs/api/advice/*.java",
        "src/main/java/org/daming/jobs/api/controller/*.java",
        "src/main/java/org/daming/jobs/api/interceptor/*.java",
        "src/main/java/org/daming/jobs/base/*.java",
        "src/main/java/org/daming/jobs/base/**/*.java",
        "src/main/java/org/daming/jobs/config/**/*.java",
        "src/main/java/org/daming/jobs/pojo/**/*.java",
        "src/main/java/org/daming/jobs/security/**/*.java",
        "src/main/java/org/daming/jobs/service/**/*.java",
        "src/main/java/org/daming/jobs/task/**/*.java",
    ]),
    resources = glob(["src/main/resources/**"]),
    deps = [
        "@maven//:org_springframework_boot_spring_boot_starter_web",
        "@maven//:org_springframework_boot_spring_boot_starter_jdbc",
        "@maven//:org_springframework_boot_spring_boot_starter_quartz",
        "@maven//:org_springframework_boot_spring_boot_starter_aop",
        "@maven//:io_springfox_springfox_boot_starter",
        "@maven//:io_springfox_springfox_core",
        "@maven//:io_springfox_springfox_spi",
        "@maven//:io_springfox_springfox_oas",
        "@maven//:io_springfox_springfox_spring_web",
        "@maven//:io_swagger_swagger_annotations",
        "@maven//:com_auth0_java_jwt",
        "@maven//:org_postgresql_postgresql",
        "@maven//:org_springframework_boot_spring_boot_devtools",
        "@maven//:org_springframework_boot_spring_boot",
        "@maven//:org_springframework_boot_spring_boot_loader",
        "@maven//:org_springframework_boot_spring_boot_loader_tools",
        "@maven//:org_springframework_boot_spring_boot_autoconfigure",
        "@maven//:org_springframework_spring_aop",
        "@maven//:org_springframework_spring_beans",
        "@maven//:org_springframework_spring_core",
        "@maven//:org_springframework_spring_context",
        "@maven//:org_springframework_spring_expression",
        "@maven//:org_springframework_spring_web",
        # "@maven//:org_apache_shiro_shiro_spring_boot_starter",
        "@maven//:org_apache_shiro_shiro_core_jakarta",
        "@maven//:org_apache_shiro_shiro_spring_jakarta",
        "@maven//:org_apache_shiro_shiro_web_jakarta",
        "@maven//:org_slf4j_slf4j_api",
        "@maven//:org_quartz_scheduler_quartz",
        "@maven//:org_aspectj_aspectjweaver",
        "@maven//:org_apache_tomcat_embed_tomcat_embed_core",
        "@maven//:jakarta_servlet_jakarta_servlet_api",  
        "@maven//:jakarta_annotation_jakarta_annotation_api",
        "@maven//:jakarta_xml_bind_jakarta_xml_bind_api",
        "@maven//:com_fasterxml_jackson_core_jackson_core",
        "@maven//:com_fasterxml_jackson_core_jackson_databind"
    ],
)
java_binary(
    name = "jobs",
    main_class = "org.daming.jobs.JobsApplication",
    runtime_deps = [":jobs-lib"],
    deploy_manifest_lines = {
        "Main-Class": "org.daming.jobs.JobsApplication",
    },
)

然后咱们能够履行bazel命令去构建一个java项目了:

# 构建
bazel build //:jobs
# 构建并运转
bazel run //:jobs

然后你会发现构建没有问题,可是运转会报错:

jobs git:(master) ✗ java -jar bazel-bin/jobs.jar
no main manifest attribute, in bazel-bin/jobs.jar

因为Springboot的代码需求运用Springboot loader进行发动,Springboot程序的打包逻辑与普通的Java程序不同。这意味着,Bazel原生的 java_binary 无法正常发动Springboot程序。

所以咱们需求给bazel装备spring相关支撑。

装备rule_spring去构建运转spring boot

咱们运用salesforce的rules_spring界说好了的rule去协助咱们构建spring boot项目。

咱们在workspace增加rules_spring的规矩文件

http_archive(
    name = "rules_spring",
    sha256 = "7bb891ccb2f53ca188a769b3a3777be1c38348e18091afea05320f3003b3e886",
    urls = [
        "https://github.com/salesforce/rules_spring/releases/download/2.3.1/rules-spring-2.3.1.zip",
        "https://mirror.ghproxy.com/https://github.com/salesforce/rules_spring/releases/download/2.3.1/rules-spring-2.3.1.zip",
    ],
)

然后在BUILD.bazel中间中导入rules_spring的相关规矩:

load("@rules_spring//springboot:springboot.bzl", "springboot")
springboot(  
    name = "springboot",  
    # specify the main class
    boot_app_class = "org.daming.jobs.JobsApplication",
    # refrence the library
    java_library = ":jobs-lib",
    # https://github.com/salesforce/rules_spring/issues/177
    boot_launcher_class = 'org.springframework.boot.loader.launch.JarLauncher',
)

因为spring boot 3.2.0之后运用新的发动器,所以咱们这儿指定了boot_launcher_class。 假如你运用的版本低于3.2.0,能够直接删去。

现在咱们能够编译运转spring boot项目了

# 构建
bazel build //:springboot
# 运转
bazel run //:springboot

运转部分输出如下:


Application    Name: 
Application Version: 
Spring Boot Version: 3.2.2 (v3.2.2)
2024-02-11 21:49:17.449 INFO [main] org.springframework.boot.StartupInfoLogger:50 Starting JobsApplication using Java 17 with PID 67028 (/private/var/tmp/_bazel_gming001/7a71463ee80a3358d2f71ab2db616aea/execroot/__main__/bazel-out/darwin_arm64-fastbuild/bin/springboot.jar started by gming001 in /private/var/tmp/_bazel_gming001/7a71463ee80a3358d2f71ab2db616aea/execroot/__main__/bazel-out/darwin_arm64-fastbuild/bin/springboot.runfiles/__main__)
2024-02-11 21:49:17.451 INFO [main] org.springframework.boot.SpringApplication:654 No active profile set, falling back to 1 default profile: "default"
2024-02-11 21:49:17.501 INFO [main] org.springframework.boot.logging.DeferredLog:252 For additional web related logging consider setting the 'logging.level.web' property to 'DEBUG'
2024-02-11 21:49:17.846 WARN [main] org.springframework.context.support.AbstractApplicationContext:632 Exception encountered during context initialization - cancelling refresh attempt: java.lang.TypeNotPresentException: Type javax.servlet.http.HttpServletRequest not present
2024-02-11 21:49:17.856 INFO [main] org.springframework.boot.autoconfigure.logging.ConditionEvaluationReportLogger:82 

遗留的问题

有两个问题没有解决,第一个是怎么运转的单元测试,这个还没研究,不过这个不急。还有一个是这个项目实际上跑不起来,会报错:

java.lang.TypeNotPresentException: Type javax.servlet.http.HttpServletRequest not present

我本来以为是bazel的装备问题,成果我发现直接maven也跑不起来。。。

我想了想了应该是springfox没有去适配spring boot3的问题,不想降级spring boot的版本话,只能从springfox迁移到springdoc

源码

本文运用的源代码都能够在jobs看到。

参考资料