基礎知識

Spring Modulith 支援開發人員在 Spring Boot 應用程式中實作邏輯模組。它允許他們應用結構驗證、文件化模組排列、為個別模組執行整合測試、觀察模組在執行時期的互動,以及通常以鬆散耦合的方式實作模組互動。本節將討論開發人員在深入研究技術支援之前需要理解的基本概念。

應用程式模組

在 Spring Boot 應用程式中,應用程式模組是一個功能單元,包含以下部分

  • 由 Spring bean 實例實作並由模組發布的應用程式事件,向其他模組公開的 API,通常稱為提供的介面

  • 不應被其他模組存取的內部實作組件。

  • 以 Spring bean 相依性、監聽的應用程式事件和公開的組態屬性的形式,參考其他模組公開的 API,通常稱為要求的介面

Spring Moduliths 提供了在 Spring Boot 應用程式中表達模組的不同方式,主要區別在於整體排列中涉及的複雜程度。這允許開發人員從簡單開始,並在需要時自然地轉向更複雜的方式。

ApplicationModules 型別

Spring Moduliths 允許檢查程式碼庫,以根據給定的排列和可選組態推導出應用程式模組模型。 spring-modulith-core 構件包含 ApplicationModules,可以指向 Spring Boot 應用程式類別

建立應用程式模組模型
  • Java

  • Kotlin

var modules = ApplicationModules.of(Application.class);
val modules = ApplicationModules.of(Application::class.java)

modules 將包含從程式碼庫推導出的應用程式模組排列的記憶體內表示。哪些部分將被偵測為模組取決於指向的類別所在的 Java 套件結構。在簡單應用程式模組中找到更多關於預期預設排列的資訊。進階排列和自訂選項在進階應用程式模組

為了了解分析的排列外觀,我們可以將整體模型中包含的個別模組寫入到控制台

將應用程式模組排列寫入到控制台
  • Java

  • Kotlin

modules.forEach(System.out::println);
modules.forEach { println(it) }
我們的應用程式模組排列的控制台輸出
## example.inventory ##
> Logical name: inventory
> Base package: example.inventory
> Spring beans:
  + ….InventoryManagement
  o ….SomeInternalComponent

## example.order ##
> Logical name: order
> Base package: example.order
> Spring beans:
  + ….OrderManagement
  + ….internal.SomeInternalComponent

請注意每個模組是如何列出的,已識別包含的 Spring 組件,以及也呈現了各自的可見性。

排除套件

如果您想從應用程式模組檢查中排除某些 Java 類別或完整套件,您可以使用以下方式進行操作

  • Java

  • Kotlin

ApplicationModules.of(Application.class, JavaClass.Predicates.resideInAPackage("com.example.db")).verify();
ApplicationModules.of(Application::class.java, JavaClass.Predicates.resideInAPackage("com.example.db")).verify()

排除的其他範例

  • com.example.db — 比對給定套件 com.example.db 中的所有檔案。

  • com.example.db.. — 比對給定套件 (com.example.db) 和所有子套件 (com.example.db.acom.example.db.b.c) 中的所有檔案。

  • ..example.. — 比對 a.examplea.example.ba.b.example.c.d,但不比對 a.exam.b

關於可能比對器的完整詳細資訊,可以在 ArchUnit JavaDoc PackageMatcher 中找到。

簡單應用程式模組

應用程式的主要套件是主要應用程式類別所在的套件。 也就是使用 @SpringBootApplication 註解,通常包含用於執行它的 main(…) 方法的類別。 預設情況下,主要套件的每個直接子套件都被視為應用程式模組套件

如果此套件不包含任何子套件,則它被視為簡單套件。 它允許透過使用 Java 的套件範圍來隱藏其中的程式碼,以防止類型被駐留在其他套件中的程式碼引用,因此不受注入到這些套件中的相依性約束。 因此,自然而然地,模組的 API 由套件中的所有公開類型組成。

讓我們看一下範例排列 ( 表示公開類型, 表示套件私有類型)。

單一庫存應用程式模組
 Example
└─  src/main/java
   ├─  example                        (1)
   |  └─  Application.java
   └─  example.inventory              (2)
      ├─  InventoryManagement.java
      └─  SomethingInventoryInternal.java
1 應用程式的主要套件 example
2 應用程式模組套件 inventory

進階應用程式模組

如果應用程式模組套件包含子套件,則這些子套件中的類型可能需要設為公開,以便可以從同一模組的程式碼中引用它。

庫存和訂單應用程式模組
 Example
└─  src/main/java
   ├─  example
   |  └─  Application.java
   ├─  example.inventory
   |  ├─  InventoryManagement.java
   |  └─  SomethingInventoryInternal.java
   ├─  example.order
   |  └─  OrderManagement.java
   └─  example.order.internal
      └─  SomethingOrderInternal.java

在這種排列中,order 套件被視為 API 套件。 允許來自其他應用程式模組的程式碼引用其中的類型。 order.internal,就像應用程式模組基礎套件的任何其他子套件一樣,被視為內部套件。 這些套件中的程式碼不得被其他模組引用。 請注意 SomethingOrderInternal 如何成為公開類型,可能是因為 OrderManagement 依賴於它。 不幸的是,這也意味著它可以從其他套件(例如 inventory 套件)引用。 在這種情況下,Java 編譯器對於防止這些非法引用沒有太大用處。

開放應用程式模組

上述描述的排列被認為是封閉的,因為它們僅向其他模組公開積極選擇公開的類型。 當將 Spring Modulith 應用於舊版應用程式時,從其他模組隱藏巢狀套件中的所有類型可能不足或需要標記所有這些套件以進行公開。

若要將應用程式模組轉換為開放式模組,請在 package-info.java 類型上使用 @ApplicationModule 註解。

將應用程式模組宣告為開放式
  • Java

  • Kotlin

@org.springframework.modulith.ApplicationModule(
  type = Type.OPEN
)
package example.inventory;
@org.springframework.modulith.ApplicationModule(
  type = Type.OPEN
)
package example.inventory

將應用程式模組宣告為開放式將導致以下驗證變更

  • 通常允許從其他模組存取應用程式模組內部類型。

  • 所有類型,也包括駐留在應用程式模組基礎套件的子套件中的類型,都會新增到未命名的具名介面,除非明確指派給具名介面。

此功能旨在主要用於現有專案的程式碼庫,逐步轉向 Spring Modulith 建議的封裝結構。 在完全模組化的應用程式中,使用開放式應用程式模組通常暗示次優的模組化和封裝結構。

明確的應用程式模組相依性

模組可以選擇透過在套件上使用 @ApplicationModule 註解(透過 package-info.java 檔案表示)來宣告其允許的相依性。 例如,由於 Kotlin 缺少對該檔案的支援,您也可以在位於應用程式模組根套件中的單一類型上使用註解。

庫存明確組態模組相依性
  • Java

  • Kotlin

@org.springframework.modulith.ApplicationModule(
  allowedDependencies = "order"
)
package example.inventory;
package example.inventory

import org.springframework.modulith.ApplicationModule

@ApplicationModule(allowedDependencies = "order")
class ModuleMetadata {}

在這種情況下,inventory 模組中的程式碼僅允許引用 order 模組中的程式碼(以及首先未指派給任何模組的程式碼)。 在驗證應用程式模組結構中了解如何監控它。

具名介面

預設情況下,如進階應用程式模組中所述,應用程式模組的基礎套件被視為 API 套件,因此是唯一允許來自其他模組的傳入相依性的套件。 如果您想向其他模組公開其他套件,則需要使用具名介面。 您可以透過使用 @NamedInterface 或明確使用 @org.springframework.modulith.PackageInfo 註解的類型來註解這些套件的 package-info.java 檔案來實現此目的。

封裝名為 SPI 具名介面的套件排列
 Example
└─  src/main/java
   ├─  example
   |  └─  Application.java
   ├─ …
   ├─  example.order
   |  └─  OrderManagement.java
   ├─  example.order.spi
   |  ├—  package-info.java
   |  └─  SomeSpiInterface.java
   └─  example.order.internal
      └─  SomethingOrderInternal.java
example.order.spi 中的 package-info.java
  • Java

  • Kotlin

@org.springframework.modulith.NamedInterface("spi")
package example.order.spi;
package example.order.spi

import org.springframework.modulith.PackageInfo
import org.springframework.modulith.NamedInterface

@PackageInfo
@NamedInterface("spi")
class ModuleMetadata {}

該宣告的效果是雙重的:首先,允許其他應用程式模組中的程式碼引用 SomeSpiInterface。 應用程式模組能夠在明確的相依性宣告中引用具名介面。 假設 inventory 模組正在使用它,它可以像這樣引用上述宣告的具名介面

定義對專用具名介面的允許相依性
  • Java

  • Kotlin

@org.springframework.modulith.ApplicationModule(
  allowedDependencies = "order :: spi"
)
package example.inventory;
@org.springframework.modulith.ApplicationModule(
  allowedDependencies = "order :: spi"
)
package example.inventory

請注意,我們如何透過雙冒號 :: 串連具名介面的名稱 spi。 在此設定中,inventory 中的程式碼將被允許依賴於 SomeSpiInterface 和駐留在 order.spi 介面中的其他程式碼,但不允許依賴於 OrderManagement 等。 對於沒有明確描述相依性的模組,應用程式模組根套件 SPI 套件都是可存取的。

如果您想表示應用程式模組被允許引用所有明確宣告的具名介面,則可以使用星號 (*),如下所示

使用星號宣告對所有宣告的具名介面的允許相依性
  • Java

  • Kotlin

@org.springframework.modulith.ApplicationModule(
  allowedDependencies = "order :: *"
)
package example.inventory;
@org.springframework.modulith.ApplicationModule(
  allowedDependencies = "order :: *"
)
package example.inventory

自訂模組偵測

預設情況下,應用程式模組將預期位於 Spring Boot 應用程式類別所在的套件的直接子套件中。 可以啟動替代偵測策略,僅考慮明確註解的套件,透過 Spring Modulith 的 @ApplicationModule 或 jMolecules @Module 註解。 可以透過將 spring.modulith.detection-strategy 組態為 explicitly-annotated 來啟動該策略。

切換應用程式模組偵測策略以僅考慮註解的套件
spring.modulith.detection-strategy=explicitly-annotated

如果預設應用程式模組偵測策略或手動註解的策略都不適用於您的應用程式,則可以透過提供 ApplicationModuleDetectionStrategy 的實作來自訂模組的偵測。 該介面公開單一方法 Stream<JavaPackage> getModuleBasePackages(JavaPackage),並將使用 Spring Boot 應用程式類別所在的套件呼叫。 然後,您可以檢查位於其中的套件,並根據命名慣例或類似方式選擇要視為應用程式模組基礎套件的套件。

假設您宣告了自訂 ApplicationModuleDetectionStrategy 實作,如下所示

實作自訂 ApplicationModuleDetectionStrategy
  • Java

  • Kotlin

package example;

class CustomApplicationModuleDetectionStrategy implements ApplicationModuleDetectionStrategy {

  @Override
  public Stream<JavaPackage> getModuleBasePackages(JavaPackage basePackage) {
    // Your module detection goes here
  }
}
package example

class CustomApplicationModuleDetectionStrategy : ApplicationModuleDetectionStrategy {

  override fun getModuleBasePackages(basePackage: JavaPackage): Stream<JavaPackage> {
    // Your module detection goes here
  }
}

此類別現在可以註冊為 spring.modulith.detection-strategy,如下所示

spring.modulith.detection-strategy=example.CustomApplicationModuleDetectionStrategy

自訂應用程式模組排列

Spring Moduliths 允許組態您透過 @Modulithic 註解在主要 Spring Boot 應用程式類別上建立的應用程式模組排列周圍的一些核心方面。

  • Java

  • Kotlin

package example;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.modulith.Modulithic;

@Modulithic
@SpringBootApplication
class MyApplication {

  public static void main(String... args) {
    SpringApplication.run(DemoApplication.class, args);
  }
}
package example

import org.springframework.boot.autoconfigure.SpringBootApplication
import org.springframework.boot.runApplication
import org.springframework.modulith.Modulithic

@Modulithic
@SpringBootApplication
class DemoApplication

fun main(args: Array<String>) {
  runApplication<DemoApplication>(*args)
}

註解公開以下屬性以進行自訂

註解屬性 描述

systemName

應用程式的人類可讀名稱,用於產生的文件中。

sharedModules

將具有給定名稱的應用程式模組宣告為共用模組,這表示它們將始終包含在應用程式模組整合測試中。

additionalPackages

指示 Spring Modulith 將組態的套件視為其他根應用程式套件。 換句話說,也將為這些套件觸發應用程式模組偵測。