Bazel使用案例:构建Springboot工程

06d3dff1452e87de72a3d08ea3e24733.jpeg

本文是关于如何使用Bazel搭建Springboot 3.1.0工程(基于JDK17)。为什么使用Bazel,而不是使用Maven或者Gradle?可以看我之前关于Bazel的介绍文章。

前期准备

在根目录加入.bazelversion文件,并加入6.2.0,指定当前工程使用的Bazel的版本。这样,Bazel命令自动使用该版本的Bazel进行构建。

在根目录加入.bazelrc文件,并指定构建和测试时使用JDK17,内容如下:

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:在Bazel中,所有的外部依赖统一定义WORKSPACE文件中;

  • • BUILD.bazel:内容留空即可,用于告诉Bazel当前目录也是一个Package。

Bazel本身是支持多语言的。所以,我们需要特定语言的rule来帮助我们在WORKSPACE中定义外部依赖。

对于Java工程,我们使用rules_jvm_external[1]进行外部依赖的管理。它的使用步骤如下:

步骤1:在WORKSPACE中增加rules_jvm_external配置

以下配置指定了rules_jvm_external的下载位置,并进行rule的初始化:

load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive")  RULES_JVM_EXTERNAL_TAG = "4.5"  
RULES_JVM_EXTERNAL_SHA = "<sha hash value>"  
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,  
)  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//:defs.bzl", "maven_install")  maven_install(  artifacts = [# The project's dependencies"junit:junit:4.12",  "org.hamcrest:hamcrest-library:1.3",  ],  repositories = [  # Private repositories are supported through HTTP Basic auth  # "http://username:password@localhost:8081/artifactory/my-repository",    "https://maven.aliyun.com/repository/public",  ],  
)

以上采用了非Bzlmod的管理rule。

步骤2:初始化maven_install.json

rules_jvm_external通过maven_install.json对Java依赖的版本进行固定。类似前端工程通过package-lock.json文件,用于固定依赖的版本。

因为是新工程,需要在根目录执行以下命令生成maven_install.json:

bazel run @maven//:pin

然后在WORKSPACE中的maven_install语句加入:

load("@maven//:defs.bzl", "pinned_maven_install")  
pinned_maven_install()

并在maven_install的参数列表中增加maven_install_json参数,效果如下:

maven_install(# artifacts, repositories, ...maven_install_json = "//:maven_install.json",
)

步骤3:加入Springboot的外部依赖

修改WORKSPACE中maven_install的artifacts的参数,加入Springboot 3.1.0所需的依赖:

SPRING_BOOT_VERSION = "3.1.0"  
SPRING_VERSION = "6.0.9"  
maven_install(  artifacts = [  # log  "org.slf4j:slf4j-api:2.0.7",  "ch.qos.logback:logback-classic:1.4.6",  # template engine  "org.springframework.boot:spring-boot-starter-thymeleaf:%s" % SPRING_BOOT_VERSION,  # spring  "org.springframework.boot:spring-boot-autoconfigure:%s" % SPRING_BOOT_VERSION,  "org.springframework.boot:spring-boot-configuration-processor:%s" % SPRING_BOOT_VERSION,  "org.springframework.data:spring-data-jpa:%s" % "3.1.0",  "org.springframework.boot:spring-boot-test-autoconfigure:%s" % SPRING_BOOT_VERSION,  "org.springframework.boot:spring-boot-starter-test:%s" % SPRING_BOOT_VERSION,  "org.springframework.boot:spring-boot-starter-validation:%s" % SPRING_BOOT_VERSION,  "org.springframework.boot:spring-boot-test:%s" % SPRING_BOOT_VERSION,  "org.springframework.boot:spring-boot:%s" % SPRING_BOOT_VERSION,  "org.springframework.boot:spring-boot-starter:%s" % SPRING_BOOT_VERSION,  "org.springframework.boot:spring-boot-starter-web:%s" % SPRING_BOOT_VERSION,  "org.springframework:spring-webmvc:%s" % SPRING_VERSION,  "org.springframework:spring-beans:%s" % SPRING_VERSION,  "org.springframework:spring-context:%s" % SPRING_VERSION ,  "org.springframework:spring-test:%s" % SPRING_VERSION,  "org.springframework:spring-web:%s" % SPRING_VERSION,  "org.springframework:spring-core:%s" % SPRING_VERSION,  "org.springframework:spring-orm:%s" % SPRING_VERSION,  "org.springframework:spring-tx:%s" % SPRING_VERSION,  "jakarta.servlet:jakarta.servlet-api:6.0.0",  'javax.annotation:javax.annotation-api:1.3.2',...

执行以下命令更新maven_install.json文件:

bazel run @unpinned_maven//:pin

至此目录结构如下:

> $ tree 
.
├── .bazelrc
├── .bazelversion
├── .gitignore
├── BUILD.bazel
├── WORKSPACE
├── maven_install.json

创建Maven工程结构

本例中,我们在根目录创建一个server模块来对外提供服务,最终效果图如下:

9d18e9d69a318be6f45e5ea4b40f9608.png

可以看出,server模块的目录结构与常规的Maven工程的结构相同。在Bazel并不一定需要采用Maven工程的结构,只是为了保持Java工程的习惯。

为达以上效果,我们需要做以下事情:

配置rules_spring

由于Springboot的代码需要使用Springboot loader进行启动,Springboot程序的打包逻辑与普通的Java程序不同。这意味着,Bazel原生的 java_binary 无法正常启动Springboot程序。

其它构建工具Maven/Gradle是通过plugin完成对Springboot工程的打包。

而在Bazel通过rules_spring[2]实现相同的功能。具体方法是在WORKSPACE中加入rules_spring:

http_archive(  name = "rules_spring",  sha256 = "<hash value>",  urls =["https://github.com/salesforce/rules_spring/releases/download/2.3.0/rules-spring-2.3.0.zip",  ],  
)

配置Java构建

在Bazel,构建逻辑写在BUILD.bazel文件中。本案例的server/src/main/java/BUILD.bazel的内容如下:

# load rule that you can use it
load("@rules_spring//springboot:springboot.bzl", "springboot")  package(default_visibility = ["//visibility:public"])  
app_deps = [  "@maven//:org_thymeleaf_thymeleaf",  "@maven//:com_fasterxml_jackson_core_jackson_annotations",  "@maven//:org_springframework_spring_beans",  "@maven//:org_springframework_spring_core",  "@maven//:org_springframework_boot_spring_boot_starter_thymeleaf",  "@maven//:org_springframework_boot_spring_boot_loader_tools",  "@maven//:org_springframework_boot_spring_boot_loader",  "@maven//:org_springframework_boot_spring_boot",  "@maven//:org_springframework_boot_spring_boot_autoconfigure",  "@maven//:org_springframework_boot_spring_boot_starter_web",  "@maven//:org_springframework_spring_context",  "@maven//:org_springframework_spring_webmvc",  "@maven//:org_springframework_boot_spring_boot_starter_validation",  "@maven//:jakarta_servlet_jakarta_servlet_api",  "@maven//:org_springframework_spring_web",  "@maven//:ch_qos_logback_logback_classic",  "@maven//:org_slf4j_slf4j_api"  
]  # define a lib contains all java files, in this case
java_library(  name = "lib",  srcs = glob(["**/*.java"]),  deps = app_deps,  # include the all resourcesresources = ["//server/src/main/resources:server-resources"],  
)  springboot(  name = "springboot",  # specify the main classboot_app_class = "codes.showme.server.Main",  # refrence the libraryjava_library = ":lib",  # build failed is there's any duplicated classesdupeclassescheck_enable = True,  
dupeclassescheck_ignorelist = "//server:springboot_dupeclass_allowlist.txt",
)

配置Resources

本例中,我们采用了Thymeleaf模板引擎进行前端渲染。所以,我们在server/src/main/resources/templates/pages中增加Thymeleaf模板。

为了告诉Springboot Thymeleaf的模板的位置,在application.yml配置以下内容:

spring:  thymeleaf:  mode: HTML  # prefix: file:<path of the templates>  prefix: classpath:/templates/pages/  cache: false  content-type: text/html  encoding: UTF-8  suffix: .html  check-template-location: true  application:  name: bazel-springboot  main:  banner-mode: "off"

最后,再在server/src/main/resources/BUILD.bazel中配置server-resources target:

filegroup(  name = "server-resources",  srcs = glob([  "application.yml",  "templates/pages/**/*",  ]),  visibility = ["//visibility:public"],  
)

至此,基础工程已经配置完成。看起来需要配置很多内容。但是这些配置就是在工程开始时配置一次。今后修改就不需要配置这么多内容了。

文章最后提供了本文的代码模板。使用该模板即可节约大量配置时间。

构建并启动工程

基础工程已经配置完成后,剩下的就是在此基础上构建新功能,并执行调试。

我们通过以下命令进行构建:

bazel build //...

如果希望在本地启动并调试,运行以下命令:

bazel run //server/src/main/java:springboot

如果运行在生产环境,建议使用Bazel打包好的bazel-bin/server/src/main/java/springboot.jar,或者将其打包到Docker镜像中。

总结

本教程遗留了以下几个问题需要处理:

  1. 1. 未集成ORM的能力;

  2. 2. 未集成前端JS/CSS相关的能力。

以上能力会在接下来的教程中实现。

完整的工程地址:https://github.com/zacker330/bazel-springboot-project-template

补充

rules_spring提供了类冲突检测能力,构建时出现如下异常,时代表工程中存在重复的依赖。这一检测能力对软件工程的稳定性非常有益。遇到这种情况,你有两种选择:

  1. 1. 移除其中一个依赖;

  2. 2. 在dupeclassescheck_ignorelist的文件中配置允许重复。

Exception: Found duplicate classes in the packaged springboot jar
Spring Boot packaging has failed for bazel-out/darwin-fastbuild/bin/server/src/main/java/springboot.jar because multiple copies of the same class, but with different hashes, were found:class jakarta/servlet/annotation/ServletSecurity$EmptyRoleSemantic.classjar processed_jakarta.servlet-api-6.0.0.jar hash b31cc341ef8131abf1c791b152880c53jar processed_tomcat-embed-core-10.1.8.jar hash 54513d067d21bf5a31fe942473085becclass jakarta/servlet/annotation/ServletSecurity$TransportGuarantee.classjar processed_jakarta.servlet-api-6.0.0.jar hash eb31c9cca28bba1ba7f3e6fc5839e828jar processed_tomcat-embed-core-10.1.8.jar hash 0acd10bfd01aa6cec1b89cdd0fcbd0f9class jakarta/servlet/annotation/ServletSecurity.classjar processed_jakarta.servlet-api-6.0.0.jar hash 9b7a0b
引用链接

[1] rules_jvm_external: https://github.com/bazelbuild/rules_jvm_external
[2] rules_spring: https://github.com/salesforce/rules_spring

‍‍‍‍

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/416589.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

异步编程(JS)

前言 想要学习Promise&#xff0c;我们首先要了解异步编程、回调函数、回调地狱三方面知识&#xff1a; 异步编程 异步编程技术使你的程序可以在执行一个可能长期运行的任务的同时继续对其他事件做出反应而不必等待任务完成。 与此同时&#xff0c;你的程序也将在任务完成后显示…

关于C#中的LINQ的延迟执行

简介 Linq中的绝大多数查询运算符都有延迟执行的特性,查询并不是在查询创建的时候执行,而是在遍历的时候执行 实例&#xff1a; public void Test2(){List<int> items new List<int>() { -1, 1, 3, 5 };IEnumerable<int> items2 items.Where(x > x &g…

模糊数学在处理激光雷达的不确定性和模糊性问题中的应用

模糊数学是一种用于处理不确定性和模糊性问题的数学工具&#xff0c;它可以帮助我们更好地处理激光雷达数据中的不确定性和模糊性。激光雷达是一种常用的传感器&#xff0c;用于测量目标物体的距离、速度和方向等信息。然而&#xff0c;在实际应用中&#xff0c;激光雷达所获取…

【AI的未来 - AI Agent系列】【MetaGPT】5. 更复杂的Agent实战 - 实现技术文档助手

在 【AI的未来 - AI Agent系列】【MetaGPT】2. 实现自己的第一个Agent 中&#xff0c;我们已经实现了一个简单的Agent&#xff0c;实现的功能就是顺序打印数字。 文章目录 0. 本文实现内容1. 实现思路2. 完整代码及细节注释 0. 本文实现内容 今天我们来实现一个有实际意义的Ag…

系统架构设计师

软考系统架构设计师笔记 专用的成电路&#xff08;Application Specific Integrated Circuit&#xff0c;ASIC) PTR记录&#xff1a;Pointer Record&#xff0c;常被用于反向地址解析&#xff0c;即通过IP地址查询服务器域名。 软件工程 软件开发模型 【增量模型的优点】 …

Verilog基础:强度建模(二)

相关阅读 Verilog基础https://blog.csdn.net/weixin_45791458/category_12263729.html?spm1001.2014.3001.5482 三、拥有单个强度和确定值的net型信号的线与组合&#xff08;线网多驱动&#xff09; 首先来说明一下什么叫信号拥有单个强度和确定值&#xff0c;其实如果一个ne…

Midjourney网页版

引言 基于国外的api开发开发了一款网页版的midjourney&#xff0c;文末有链接 相关资源 Midjourney官方教学资料Midjourney官网discord官网B站学习资源推荐 账号注册 获取网络访问权限 使用Midjourney的前提是计算机有外网访问权限 此处推荐两款软件,lantern的优势是免费&…

C#操作pdf之使用itext实现01-生成一个简单的table

创建.net 8控制台项目 安装itext <PackageReference Include"itext" Version"8.0.2" /><PackageReference Include"itext.bouncy-castle-adapter" Version"8.0.2" /><PackageReference Include"itext.bouncy-cast…

2023 IoTDB Summit:湖南大唐先一科技有限公司主任架构师舒畅《IoTDB 在发电领域的应用实践》...

12 月 3 日&#xff0c;2023 IoTDB 用户大会在北京成功举行&#xff0c;收获强烈反响。本次峰会汇集了超 20 位大咖嘉宾带来工业互联网行业、技术、应用方向的精彩议题&#xff0c;多位学术泰斗、企业代表、开发者&#xff0c;深度分享了工业物联网时序数据库 IoTDB 的技术创新…

基于Django的Python应用—学习笔记—功能完善

一、让用户可以输入信息 创建forms.py 创建基于表单的页面的方法几乎与前面创建网页一样&#xff1a;定义一个 URL &#xff0c;编写一个视图函数并编写一个模板。一个主要差别是&#xff0c;需要导入包含表单 的模块forms.py 。 from django import forms from .models impor…

C++继承(万字详!!)

文章目录 继承的概念及定义继承的概念继承定义 基类和派生类对象赋值转换继承中的作用域派生类的默认成员函数继承与友元继承与静态成员复杂的菱形继承及菱形虚拟继承菱形继承菱形虚拟继承 继承的总结和反思笔试面试题 继承的概念及定义 继承的概念 继承(inheritance) 机制是面…

unity 编辑器开发一些记录(遇到了更新)

1、封装Toggle组件 在用toggle等会状态改变的组件时&#xff0c;通过select GUILayout.Toggle(select, text, options)通常是这样做&#xff0c;但是往往有些复杂编辑器需求&#xff0c;当select变化时需要进行复杂的计算&#xff0c;所以不希望每帧去计算select应该的信息。…