使用 Spring Boot CLI 快速開發 Spring Boot 應用程式

昨天介紹了 SDKMAN 這套工具,文章中有安裝 Spring Boot CLI 這套命令列工具,雖然在有 IDE 的情形下已經用不太到這個 CLI 工具,不過我還是大致看了一下,結果發現用 Groovy 開發 Spring Boot 還蠻棒的!👍

Spring Boot

簡介 Spring Boot CLI

Spring Boot CLI 是一個命令列工具 (CLI Tool),如果你想快速開發 Spring 應用程時的時候,是可以用這套工具輔助你快速完成一個應用程式開發。你只要先撰寫一份 Groovy 腳本(scripts),就可以立即透過 Spring Boot CLI 執行出一個 Spring Boot 應用程式,完全不用任何前置作業。

當然,你也可以使用 Spring Boot CLI 來快速建立一個基於 Maven 或 Gradle 的 Spring Boot 專案 (透過 Spring Initializr 服務)。

Spring Boot CLI 基本使用方式

在你透過 SDKMAN 執行 sdk install springboot 命令安裝成功後,直接執行 spring 或 spring help 命令,就可以得到基本使用方式說明。

usage: spring [--help] [--version]
      <command> [<args>]

Available commands are:

  run [options] <files> [--] [args]
    Run a spring groovy script

  grab
    Download a spring groovy script's dependencies to ./repository

  jar [options] <jar-name> <files>
    Create a self-contained executable jar file from a Spring Groovy script

  war [options] <war-name> <files>
    Create a self-contained executable war file from a Spring Groovy script

  install [options] <coordinates>
    Install dependencies to the lib/ext directory

  uninstall [options] <coordinates>
    Uninstall dependencies from the lib/ext directory

  init [options] [location]
    Initialize a new project using Spring Initializr (start.spring.io)

  encodepassword [options] <password to encode>
    Encode a password for use with Spring Security

  shell
    Start a nested shell

Common options:

  --debug Verbose mode
    Print additional status information for the command you are running


See 'spring help <command>' for more information on a specific command.

從說明中可以發現,裡面有許多子命令都是透過 groovy script 來執行的,因此你可以將其視為 Groovy Script 的啟動器,只是他主要用來跑 Spring Boot 應用程式而已。

  • 查看初始化 Spring Boot 命令的可用選項

    spring init --list
    

    這裡將會顯示所有 spring init 可用餐數的清單與預設值:

    Parameters
    +-------------+------------------------------------------+------------------------------+
    | Id          | Description                              | Default value                |
    +-------------+------------------------------------------+------------------------------+
    | artifactId  | project coordinates (infer archive name) | demo                         |
    | bootVersion | spring boot version                      | 2.7.3                        |
    | description | project description                      | Demo project for Spring Boot |
    | groupId     | project coordinates                      | com.example                  |
    | javaVersion | language level                           | 17                           |
    | language    | programming language                     | java                         |
    | name        | project name (infer application name)    | demo                         |
    | packageName | root package                             | com.example.demo             |
    | packaging   | project packaging                        | jar                          |
    | type        | project type                             | maven-project                |
    | version     | project version                          | 0.0.1-SNAPSHOT               |
    +-------------+------------------------------------------+------------------------------+
    
  • 建立 Spring Boot 應用程式專案目錄

    spring init --dependencies=web,lombok my-project
    

    此命令執行時訊息如下,他其實還是透過 https://start.spring.io 網站來產生一個專案範本,並且上述命令會自動產生一個 my-project 資料夾:

    Using service at https://start.spring.io
    Project extracted to '/home/will/projects/my-project'
    
  • 建立 Spring Boot 應用程式專案壓縮檔

    如果想產生一個專案範本的壓縮檔 (my-project.zip),可以這樣執行:

    spring init --dependencies=web,lombok my-project.zip
    

    預設執行 spring init 命令 (不加上任何參數),會預設產生一個 demo.zip 檔案。

  • 建立 Spring Boot 應用程式專案目錄,並使用 Gradle Build Tool

    如果想產生一個使用 Gradle、指定 Java 1.8 版本、並以 war 封裝的 Spring Boot 專案,可以這樣執行:

    spring init --build=gradle --java-version=1.8 --dependencies=web,lombok --packaging=war my-project2
    

體驗 Spring Boot CLI 建立專案

Spring Boot CLI 建立出來的 Spring Boot 專案,都已經套用了

  1. 建立 Spring Boot 應用程式專案目錄 (demo1)

    spring init --dependencies=web,lombok --groupId=com.duotify demo1
    

    這個 demo1 資料夾將會呈現以下目錄結構:

    $ tree
    .
    ├── HELP.md
    ├── mvnw
    ├── mvnw.cmd
    ├── pom.xml
    └── src
        ├── main
        │   ├── java
        │   │   └── com
        │   │       └── duotify
        │   │           └── demo2
        │   │               └── DemoApplication.java
        │   └── resources
        │       ├── application.properties
        │       ├── static
        │       └── templates
        └── test
            └── java
                └── com
                    └── duotify
                        └── demo2
                            └── DemoApplicationTests.java
    
    14 directories, 7 files
    
  2. 使用 Visual Studio Code 開啟 demo1 資料夾

    code demo1
    

    若是第一次開啟 VS Code 請安裝 Essential Java Spring Boot Snippets 擴充套件。

  3. 加入預設首頁

    先找到應用程式入口點,也就是有標示 @SpringBootApplication 的類別

    src/main/java/com/duotify/demo1/DemoApplication.java
    

    原始碼長這樣:

    package com.duotify.demo1;
    
    import org.springframework.boot.SpringApplication;
    import org.springframework.boot.autoconfigure.SpringBootApplication;
    
    @SpringBootApplication
    public class DemoApplication {
    
      public static void main(String[] args) {
        SpringApplication.run(DemoApplication.class, args);
      }
    
    }
    

    這裡的 @SpringBootApplication 是一個集合標注,他也同時意味著標注了 @Configuration、@EnableAutoConfiguration 與 @ComponentScan 這三個 Annotation,因此 DemoApplication 本身就是個自動設定檔。

    只要在 DemoApplication 類別再標示個 @RestController 並加入一個 home() 方法,標示 @GetMapping("/") 就有了預設首頁! 👍

    package com.duotify.demo1;
    
    import org.springframework.boot.SpringApplication;
    import org.springframework.boot.autoconfigure.SpringBootApplication;
    import org.springframework.web.bind.annotation.GetMapping;
    import org.springframework.web.bind.annotation.RestController;
    
    @SpringBootApplication
    @RestController
    public class DemoApplication {
    
      @GetMapping("/")
      public String home() {
        return "Hello World";
      }
    
      public static void main(String[] args) {
        SpringApplication.run(DemoApplication.class, args);
      }
    
    }
    
  • 啟動專案

    在 VS Code 裡面可以直接按下 F5 功能鍵,快速啟動專案。

    如果要在 Terminal 執行可輸入以下命令:

    mvn spring-boot:run
    

    注意: 你不用事先安裝 spring-boot 這個 Maven Plugin 就可以執行該命令,他會自動下載 spring-boot plugin 並執行裡面的 run 目標(Goal)。

    透過瀏覽器開啟 http://localhost:8080/ 即可看到網站!

    http://localhost:8080/

體驗 Spring Boot CLI 執行 Groovy script

  1. 建立一個 hello.groovy 腳本

    以下命令跑在 Bash 環境下:

    cat <<EOF > hello.groovy
    @RestController
    class WebApplication {
    
        @RequestMapping("/")
        String home() {
            "Hello World!"
        }
    
    }
    EOF
    
  2. 直接執行 hello.groovy 腳本

    spring run hello.groovy
    

    spring run hello.groovy

    透過瀏覽器開啟 http://localhost:8080/ 即可看到網站!

    http://localhost:8080/

    事實上,你還可以對這個應用程式做出微調,例如你不想將應用程式跑在 Port 8080,改用 Port 9000 的命令如下:

    spring run hello.groovy -- --server.port=9000
    

    記得中間要用 -- 隔開參數。

    如果你想傳入額外的 JVM 參數,可以透過 JAVA_OPTS 環境變數來傳入,例如:

    JAVA_OPTS=-Xmx1024m spring run hello.groovy
    

    注意: 這裡的 JAVA_OPTS=-Xmx1024m 環境變數設定只會套用在這一次的命令列執行而已。

仔細想想,這也太酷了吧,整個 Spring Boot 網站,就只要一個檔案就可以跑起來了耶!👍

如果你有超過不止一個 API 要執行,你可以撰寫多個 *.groovy 檔案,例如:

  1. 建立一個 test.groovy 腳本

    以下命令跑在 Bash 環境下:

    cat <<EOF > test.groovy
    @RestController
    class TestController {
    
        @RequestMapping("/test")
        String home() {
            "Test!"
        }
    
    }
    EOF
    
  2. 直接透過 shell globbing 語法 (*.groovy) 執行所有腳本

    spring run *.groovy
    

    透過瀏覽器開啟 http://localhost:8080/ 與 http://localhost:8080/test 都可看到網站!

帥呀老皮!有夠乾淨的專案架構!😍

那我們如果要封裝應用程式為 JAR 檔可以嗎?可以的!

  1. 將 *.groovy 封裝成 demo2.jar 檔

    spring jar demo2.jar *.groovy
    

    image

    這裡的 demo2.jar.original 是你的原始應用程式內容,而 demo2.jar 則是包含 Tomcat 的內容,所以檔案大很多。

    請注意: 使用 spring jar 封裝的時候,預設會包含 public/**, resources/**, static/**, templates/**, META-INF/**, * 這些檔案與路徑,且預設會排除 .*, repository/**, build/**, target/**, **/*.jar, **/*.groovy 這些檔案與路徑。你也可以透過 --include 或 --exclude 自行加入或排除更多檔案到 JAR 檔中。

  2. 執行 JAR 檔

    java -jar demo2.jar
    

Groovy 是如何使用第三方套件的?

雖然 Groovy 是一個基於 Java 的動態腳本語言,背後其實還是跑在 JVM 底下,那 Java 原本需要的那些 Code 怎麼可能憑空消失呢?說好的 import 呢?說好的 classPath 呢?還有 Spring Boot 需要的那些相依套件 (例如 spring-boot-starter-web, spring-boot-starter-data-jpa, ... 之類的) 又到哪裡去了?

在標準的 Groovy 語言中有個 @Grab 標注(annotation),可以讓你宣告第三方函式庫的相依性,因此可以讓 Groovy 像 Maven 或 Gradle 那樣自動下載 JARs 檔,但卻不需要任何建置工具。

而 Spring Boot 則是利用了這個技術,直接依據你所寫的程式碼自動抓取(Grab)你需要的函式庫或套件。例如我們剛剛的 hello.groovy 腳本,在 WebApplication 類別上用到了 @RestController 標注,因此會自動抓取 Tomcat 與 Spring MVC 套件回來,是不是真的很酷!😍

cat <<EOF > hello.groovy
@RestController
class WebApplication {

    @RequestMapping("/")
    String home() {
        "Hello World!"
    }

}
EOF

官網的 Spring Boot CLI 文件有列出一些對應表,讓你知道寫了哪些程式,就會自動下載哪些套件:

Items Grabs
JdbcTemplate, NamedParameterJdbcTemplate, DataSource JDBC Application.
@EnableJms JMS Application.
@EnableCaching Caching abstraction.
@Test JUnit.
@EnableRabbit RabbitMQ.
extends Specification Spock test.
@EnableBatchProcessing Spring Batch.
@MessageEndpoint @EnableIntegration Spring Integration.
@Controller @RestController @EnableWebMvc Spring MVC + Embedded Tomcat.
@EnableWebSecurity Spring Security.
@EnableTransactionManagement Spring Transaction Management.

你只要去 Spring Boot CLI 的原始碼專案找出所有 CompilerAutoConfiguration 的子類別,就可以知道怎麼運作的了。

除了這些內建會自動抓取的相依套件外,你在 Spring Boot 可以透過 @Grab('freemarker') 語法,宣告需要使用到哪些套件,無須指定 group 或 version 等資訊,完整的短名清單可以參閱 Dependency Coordinates 文件。

你可以透過 spring grab *.groovy 命令,自動將所有程式碼中用到的相依套件全部下載到 ./repository 目錄下。

在用 Groovy 撰寫 Spring Boot 的時候,大多數情況都是不用特別撰寫 import 語法的,例如 @Component, @RestController 與 @RequestMapping 之類的標注通通不需要特別明確匯入,因此程式碼十分乾淨清爽!👍

最後,使用 Groovy 撰寫 Spring Boot 時,可以不用特別寫啟動類別與 public static void main(String[] args) 方法,連 SpringApplication 實體都不用建立,Spring Boot CLI 會自動幫你與原始碼一起建立,是不是很棒!👍

總結

我之前沒想過要用 Apache Groovy 來寫 Spring Boot 應用程式,而且我也很少聽到有人這麼做,但研究過 Spring Boot CLI 之後,我覺得用 Groovy 也蠻好的耶,整個目錄架構與程式碼都非常簡潔,而且底層一樣是走 JVM 架構,與現有的任何 Java 相依套件都相容,也沒有什麼效能問題,自動化部署與封裝也都很正常。

但 Apache Groovy 畢竟是一個新的程式語言,有他自己的語言特性,語法也跟 Java 有些許差異,雖然學習門檻沒有很高,但不一樣就是不一樣,要推廣應該不太容易吧。

相關連結

留言評論