paint-brush
如何使用 Spring Boot 和 Gradle 快速引入新 API经过@johnjvester
新歷史

如何使用 Spring Boot 和 Gradle 快速引入新 API

经过 John Vester14m2025/03/24
Read on Terminal Reader

太長; 讀書

上市时间可以成就或毁掉任何想法或解决方案。了解利用 ChatGPT、Spring Boot、Gradle 和 Heroku 可以多快创建 RESTful API。
featured image - 如何使用 Spring Boot 和 Gradle 快速引入新 API
John Vester HackerNoon profile picture
0-item
1-item
2-item


过去五年来,我一直把“一切始于一个想法”这句话贴在办公室的墙上。


在我开始为健身应用程序开发 API 集合后不久,我的妻子在 Etsy 上找到了这款产品。我喜欢这句话,因为它抓住了我在新项目创建阶段所投入的热情。即使我已经工作了三十年,这仍然是我最喜欢工程师的方面。


这段时间我学到的是,只有当人们有机会体验时,一个想法才会有意义。如果一个想法需要很长时间才能变成现实,你最终会错失良机,因为别人会抢先一步。这就是为什么初创公司总是竞相尽快将他们的想法推向市场。


让我们来看看如何快速地将想法变成现实。

假设

在本文中,我们将尽量简单。我们将使用 Java 17 和 Spring Boot 3 来创建 RESTful API。在此示例中,我们将使用 Gradle 进行构建自动化。


虽然我们计划推向市场的服务理念通常会使用持久层,但在这个例子中我们将把它放在一边,并在存储库类中静态定义我们的数据。


我们不必担心为该示例添加任何安全性,只需允许匿名访问该概念证明。

励志名言 API

假设我们的想法是一个励志名言 API。为了确保我们能尽快完成,我要求 ChatGPT 为我创建一个 OpenAPI 规范。


几秒钟内,ChatGPT 给出了回应:


以下是 ChatGPT 生成的 YAML 格式的 OpenAPI 规范:


 openapi: 3.0.0 info: title: Motivational Quotes API description: An API that provides motivational quotes. version: 1.0.0 servers: - url: https://api.example.com description: Production server paths: /quotes: get: summary: Get all motivational quotes operationId: getAllQuotes responses: '200': description: A list of motivational quotes content: application/json: schema: type: array items: $ref: '#/components/schemas/Quote' /quotes/random: get: summary: Get a random motivational quote operationId: getRandomQuote responses: '200': description: A random motivational quote content: application/json: schema: $ref: '#/components/schemas/Quote' /quotes/{id}: get: summary: Get a motivational quote by ID operationId: getQuoteById parameters: - name: id in: path required: true schema: type: integer responses: '200': description: A motivational quote content: application/json: schema: $ref: '#/components/schemas/Quote' '404': description: Quote not found components: schemas: Quote: type: object required: - id - quote properties: id: type: integer quote: type: string


我只需要进行一次手动更新 - 确保idquote属性对于Quote架构是必需的。这只是因为我忘记在原始提示中向 ChatGPT 提及此约束。

有了它,我们准备使用API-First方法开发新服务。

使用 API-First 构建 Spring Boot 服务

在此示例中,我将使用Spring Boot CLI创建一个新项目。以下是使用 Homebrew 安装 CLI 的方法:


 $ brew tap spring-io/tap $ brew install spring-boot

创建新的 Spring Boot 服务

我们将该项目命名quotes ,并使用以下命令创建它:


 $ spring init --dependencies=web quotes


让我们检查一下quotes文件夹的内容:


 $ cd quotes && ls -la total 72 drwxr-xr-x@ 11 jvester 352 Mar 1 10:57 . drwxrwxrwx@ 90 jvester 2880 Mar 1 10:57 .. -rw-r--r--@ 1 jvester 54 Mar 1 10:57 .gitattributes -rw-r--r--@ 1 jvester 444 Mar 1 10:57 .gitignore -rw-r--r--@ 1 jvester 960 Mar 1 10:57 HELP.md -rw-r--r--@ 1 jvester 545 Mar 1 10:57 build.gradle drwxr-xr-x@ 3 jvester 96 Mar 1 10:57 gradle -rwxr-xr-x@ 1 jvester 8762 Mar 1 10:57 gradlew -rw-r--r--@ 1 jvester 2966 Mar 1 10:57 gradlew.bat -rw-r--r--@ 1 jvester 28 Mar 1 10:57 settings.gradle drwxr-xr-x@ 4 jvester 128 Mar 1 10:57 src


接下来,我们编辑build.gradle文件,如下所示,以采用 API-First 方法。


 plugins { id 'java' id 'org.springframework.boot' version '3.4.3' id 'io.spring.dependency-management' version '1.1.7' id 'org.openapi.generator' version '7.12.0' } openApiGenerate { generatorName = "spring" inputSpec = "$rootDir/src/main/resources/static/openapi.yaml" outputDir = "$buildDir/generated" apiPackage = "com.example.api" modelPackage = "com.example.model" configOptions = [ dateLibrary: "java8", interfaceOnly: "true", useSpringBoot3: "true", useBeanValidation: "true", skipDefaultInterface: "true" ] } group = 'com.example' version = '0.0.1-SNAPSHOT' java { toolchain { languageVersion = JavaLanguageVersion.of(17) } } repositories { mavenCentral() } dependencies { implementation 'org.springframework.boot:spring-boot-starter-web' implementation 'org.springframework.boot:spring-boot-starter-validation' implementation 'org.openapitools:jackson-databind-nullable:0.2.6' implementation 'io.swagger.core.v3:swagger-annotations:2.2.20' annotationProcessor 'org.projectlombok:lombok' testImplementation 'org.springframework.boot:spring-boot-starter-test' testRuntimeOnly 'org.junit.platform:junit-platform-launcher' } sourceSets { main { java { srcDirs += "$buildDir/generated/src/main/java" } } } compileJava.dependsOn tasks.openApiGenerate tasks.named('test') { useJUnitPlatform() }


最后,我们将生成的 OpenAPI 规范作为openapi.yaml放入resources/static文件夹中。

生成 API 和模型对象

在 IntelliJ 中打开项目后,我执行以下命令来构建 API 存根和模型对象。


 ./gradlew clean build


现在,我们可以看到根据我们的 OpenAPI 规范创建的apimodel对象。这是QuotesAPI.java文件:


添加业务逻辑

在基础服务准备就绪并遵守我们的 OpenAPI 合同后,我们开始向服务添加一些业务逻辑。


首先,我们创建一个QuotesRepository类,它返回我们服务的数据。如上所述,这些数据通常会存储在某个专用的持久层中。对于此示例,硬编码五个引号的数据就可以了,而且它让我们保持专注。


 @Repository public class QuotesRepository { public static final List<Quote> QUOTES = List.of( new Quote() .id(1) .quote("The greatest glory in living lies not in never falling, but in rising every time we fall."), new Quote() .id(2) .quote("The way to get started is to quit talking and begin doing."), new Quote() .id(3) .quote("Your time is limited, so don't waste it living someone else's life."), new Quote() .id(4) .quote("If life were predictable it would cease to be life, and be without flavor."), new Quote() .id(5) .quote("If you set your goals ridiculously high and it's a failure, you will fail above everyone else's success.") ); public List<Quote> getAllQuotes() { return QUOTES; } public Optional<Quote> getQuoteById(Integer id) { return Optional.ofNullable(QUOTES.stream().filter(quote -> quote.getId().equals(id)).findFirst().orElse(null)); } }


接下来,我们创建一个QuotesService ,它将与QuotesRepository进行交互。采用这种方法将使数据与业务逻辑分离。


 @RequiredArgsConstructor @Service public class QuotesService { private final QuotesRepository quotesRepository; public List<Quote> getAllQuotes() { return quotesRepository.getAllQuotes(); } public Optional<Quote> getQuoteById(Integer id) { return quotesRepository.getQuoteById(id); } public Quote getRandomQuote() { List<Quote> quotes = quotesRepository.getAllQuotes(); return quotes.get(ThreadLocalRandom.current().nextInt(quotes.size())); } }


最后,我们只需要实现从 API-First 方法生成的QuotesApi


 @Controller @RequiredArgsConstructor public class QuotesController implements QuotesApi { private final QuotesService quotesService; @Override public ResponseEntity<List<Quote>> getAllQuotes() { return new ResponseEntity<>(quotesService.getAllQuotes(), HttpStatus.OK); } @Override public ResponseEntity<Quote> getQuoteById(Integer id) { return quotesService.getQuoteById(id) .map(quote -> new ResponseEntity<>(quote, HttpStatus.OK)) .orElseGet(() -> new ResponseEntity<>(HttpStatus.NOT_FOUND)); } @Override public ResponseEntity<Quote> getRandomQuote() { return new ResponseEntity<>(quotesService.getRandomQuote(), HttpStatus.OK); } }


此时,我们已拥有功能齐全的励志名言 API,并附带一小部分响应。

最后几点

Spring Boot 通过springdoc-openapi-starter-webmvc-ui依赖项为我们提供了基于 Web 的 Swagger Docs 用户界面选项。


 dependencies { ... implementation 'org.springdoc:springdoc-openapi-starter-webmvc-ui:2.8.5' ... }


虽然框架允许工程师使用简单的注释来描述他们的 API,但我们可以使用resources/static文件夹中现有的openapi.yaml文件。


我们可以在application-properties.yaml文件中实现这种方法,以及一些其他小的配置更新:


 server: port: ${PORT:8080} spring: application: name: quotes springdoc: swagger-ui: path: /swagger-docs url: openapi.yaml


为了好玩,我们添加一个banner.txt文件,供服务启动时使用。我们将此文件放入resources文件夹中。


 ${AnsiColor.BLUE} _ __ _ _ _ ___ | |_ ___ ___ / _` | | | |/ _ \| __/ _ \/ __| | (_| | |_| | (_) | || __/\__ \ \__, |\__,_|\___/ \__\___||___/ |_| ${AnsiColor.DEFAULT} :: Running Spring Boot ${AnsiColor.BLUE}${spring-boot.version}${AnsiColor.DEFAULT} :: Port #${AnsiColor.BLUE}${server.port}${AnsiColor.DEFAULT} ::


现在,当我们在本地启动服务时,我们可以看到横幅:


一旦启动,我们可以通过访问/swagger-docs端点来验证 Swagger Docs 是否正常工作。



最后,我们将创建一个新的基于 Git 的存储库,以便我们可以跟踪任何未来的变化:


 $ git init $ git add . $ git commit -m "Initial commit for the Motivational Quotes API"


现在,让我们看看我们可以多快部署我们的服务

使用 Heroku 完成旅程

到目前为止,介绍我的新想法的主要重点是创建 OpenAPI 规范并为我的服务编写一些业务逻辑。Spring Boot 为我处理了其他所有事情。


在运行我的服务时,我更喜欢使用 Heroku,因为它非常适合 Spring Boot 服务。我可以快速部署我的服务,而不会陷入云基础设施问题的泥潭。Heroku 还可以轻松为我的基于 Java 的应用程序传递配置值


为了匹配我们使用的 Java 版本,我们在项目的根文件夹中创建一个system.properties文件。该文件有一行:


 java.runtime.version = 17


然后,我在同一位置创建一个Procfile ,用于自定义部署行为。此文件也有一行:


 web: java -jar build/libs/quotes-0.0.1-SNAPSHOT.jar


是时候部署了。使用Heroku CLI ,我可以使用几个简单的命令部署服务。首先,我对 CLI 进行身份验证,然后创建一个新的 Heroku 应用程序。


 $ heroku login $ heroku create Creating app... done, vast-crag-43256 https://vast-crag-43256-bb5e35ea87de.herokuapp.com/ | https://git.heroku.com/vast-crag-43256.git


我的 Heroku 应用程序实例名为vast-crag-43256 (我可以传入指定的名称),服务将在 https://vast-crag-43256-bb5e35ea87de.herokuapp.com/ 运行。


最后一件事是使用 Git 命令将代码推送到 Heroku 来部署服务:


 $ git push heroku master


此命令完成后,我们可以通过 Heroku 仪表板验证部署是否成功:


现在,我们已准备好试用我们的新服务!

激励名言在行动中

通过在 Heroku 上运行 Motivational Quotes 服务,我们可以使用一系列curl命令验证一切是否按预期运行。


首先,让我们来看一下这五句励志名言的完整列表:


 $ curl \ --location 'https://vast-crag-43256-bb5e35ea87de.herokuapp.com/quotes'


 [ { "id":1, "quote":"The greatest glory in living lies not in never falling, but in rising every time we fall." }, { "id":2, "quote":"The way to get started is to quit talking and begin doing." }, { "id":3, "quote":"Your time is limited, so don't waste it living someone else's life." }, { "id":4, "quote":"If life were predictable it would cease to be life, and be without flavor." }, { "id":5, "quote":"If you set your goals ridiculously high and it's a failure, you will fail above everyone else's success." } ]


让我们通过 ID 检索一条励志名言:


 $ curl \ --location 'https://vast-crag-43256-bb5e35ea87de.herokuapp.com/quotes/3'


 { "id":3, "quote":"Your time is limited, so don't waste it living someone else's life." }


让我们随机摘取一句励志名言:


 $ curl --location \ 'https://vast-crag-43256-bb5e35ea87de.herokuapp.com/quotes/random'


 { "id":5, "quote":"If you set your goals ridiculously high and it's a failure, you will fail above everyone else's success." }


我们甚至还可以浏览 Swagger 文档。


结论

上市时间可以成就或毁掉任何创意。这就是为什么初创公司专注于尽快推出创新产品。到达终点线的时间越长,竞争对手抢先一步的风险就越大。


我的读者可能还记得我的个人使命宣言,我觉得它适用于任何 IT 专业人士:


“将时间集中在提供能够扩展知识产权价值的功能上。其他一切都要利用框架、产品和服务。”

— J. Vester


在本文中,我们了解了 Spring Boot 如何处理实现 RESTful API 所需的一切。利用 ChatGPT,我们甚至可以用人类语言表达我们想要的服务,并且它在几秒钟内为我们创建了一个 OpenAPI 规范。这使我们能够利用 API-First 方法。准备就绪后,我们可以通过发出一些 CLI 命令使用 Heroku 来实现我们的想法。

Spring Boot、ChatGPT 和 Heroku 提供了框架和服务,让我能够全神贯注地实现我的想法。因此,我能够坚持我的个人使命宣言,更重要的是,能够快速实现我的想法。我所要做的就是专注于我的想法背后的业务逻辑——这才是应该做的!


如果你有兴趣,可以在GitLab上找到本文的源代码。


祝您度过愉快的一天!