當我們談論Spring的時候到底在談什麼

落叶微风發表於2024-03-22

你好,這裡是codetrend專欄“Spring6全攻略”。歡迎點選關注檢視往期文章。

Spring對於不做程式開發的人來說字面意思就是春天,四季的開始。

對於程式設計師來說這個單詞完全擁有另外一個含義,Spring指的是一個開源專案,而這個專案非常厲害。

Spring這個術語在不同的語境中有不同的含義。它可以用來指代Spring Framework專案本身。隨著時間的推移,其他Spring專案也建立在Spring Framework之上。當人們說“Spring”時,通常指的是整個專案家族。

Spring Framework被分為多個模組。應用程式可以選擇它們需要的模組。在核心部分是核心容器的模組,包括配置模型和依賴注入機制。

除此之外,Spring Framework為不同的應用架構提供了基礎支援,包括訊息傳遞、事務性資料和永續性,以及Web。它還包括基於Servlet的Spring MVC Web框架,同時也有Spring WebFlux響應式Web框架。

Spring的來龍去脈可以透過這個Mermaid流程圖表示。

graph LR Jakarta/Java_EE --實現--> Spring_Framework Spring專案 --使用--> Spring_Framework

Spring與企業應用開發

Java EE(Java Platform, Enterprise Edition)是一種基於Java程式語言的企業級應用程式開發平臺。它提供了一套全面的API和執行時環境,用於簡化企業級應用程式的開發和部署。Java EE包括各種技術規範和API,如Servlets、JSP、EJB、JPA、JMS等,旨在幫助開發者構建可靠、安全、可擴充套件的企業級應用程式。

Java EE 也是是一個不斷髮展的解決方案,它設計的介面和框架以及整個解決方案在Spring框架中都有體現。也就是說Spring是Java EE的繼承者和發揚者。

Spring使得建立Java企業應用程式變得簡單。它提供了一切您在企業環境中需要的內容,支援Groovy和Kotlin作為JVM上的替代語言,並靈活地根據應用程式的需求建立多種架構。從Spring Framework 6.0開始,Spring要求使用Java 17或更高版本。

Spring支援廣泛的應用場景,而這些解決方案就是為企業開發而生。在大型企業中,應用程式通常存在很長時間,必須在開發人員無法控制的JDK和應用伺服器上執行。其他應用可能作為一個包含嵌入式伺服器的單個jar檔案執行,可能在雲環境中。還有一些可能是獨立的應用程式(如batch 或者 integration workloads),不需要伺服器。

Spring是開源的。這也是Spring成功的秘訣之一。它擁有龐大而活躍的社群,基於各種真實用例提供持續反饋。Spring透過不斷迭代、不斷不斷髮展變得強大。

Spring的歷史

Spring 框架誕生於 2003 年,是為了應對早期 J2EE 規範的複雜性而開發的。

儘管有人認為 Java EE 及其現代繼承者 Jakarta EE 與 Spring 處於競爭關係,但實際上它們是互補的。

Java Specification Requests (JSRs) 是 Java Community Process (JCP) 下制定的技術規範提案,它代表了一種標準化的過程。每個 JSR 都是為了定義或更新 Java 平臺的一部分,涵蓋了從核心語言、API 到企業級平臺的各種技術規格。比如 Servlet API(JSR 340)、WebSocket API(JSR 356)、JPA(JSR 338) 等都是 JSR 規範的具體例項,它們定義了 Java 開發人員應該遵循的標準介面和實現。

Spring框架實現了 JSR 規範。透過JSR規範既可以保持框架的一定相容性,又能保證框架的推廣和保持流行。但實現規範是一個選擇性的過程,Spring6框架的輕量級設計思想決定了實現規範的選擇性。

Jakarta EE(前身為 Java EE)是一個企業級 Java 平臺標準,它整合了一系列經過 JCP 認證的 JSR 規範,為開發企業級應用提供一整套解決方案,包括但不限於 web 層、業務層、持久化層和訊息傳遞等方面。Spring Framework 支援並整合了許多 Jakarta EE 中的關鍵技術規範,同時又提供了自己特有的程式設計模型和擴充套件功能。

Spring 程式設計模型並不包含 Jakarta EE 平臺,而是從傳統 Java EE 範圍內精心選擇的個別規範整合,包括:

  • Servlet API(JSR 340)
  • WebSocket API(JSR 356)
  • 併發工具(JSR 236)
  • JSON 繫結 API(JSR 367)
  • Bean 驗證(JSR 303)
  • JPA(JSR 338)
  • JMS(JSR 914)
  • 用於事務協調的 JTA/JCA 設定

Spring 框架還支援依賴注入(JSR 330)和通用註解(JSR 250)規範,應用開發人員可以選擇使用而不是 Spring 框架提供的特定機制。這也是程式設計師通常提到的基於註解開發的來源。因為實現了這些統一規範,Spring的相容和功能都很強大。

最初這些JavaEE規範的實現都是放在 javax 包,也就是Spring Framework 5 以前的框架都是放在這個包路徑。比如javax.servlet.http.HttpServletRequest就是實現的這個規範Servlet API(JSR 340)的一個類。

而在Spring Framework 6中,這個類的路徑變成了Jakarta.servlet.http.HttpServletRequest。這更能說明Spring與Java EE的關係。

在現在的Springy應用中,Java/Jakarta EE 在應用程式開發中的角色已經發生了變化。在 J2EE 和 Spring 早期,應用程式被建立為部署到應用伺服器上。

現在藉助 Spring Boot,應用程式以 devops 和雲友好的方式建立,內嵌了 Servlet 容器,易於更改。

截至 Spring Framework 5,WebFlux 應用程式可以不直接使用 Servlet API,可以在不是 Servlet 容器的伺服器上執行(例如 Netty)。

Spring 是在不斷創新和發展。需要整合對應的內容,只需要引入依賴修改版本即可。

Spring與Spring生態專案有版本的限制,比如Spring5對應Springboot2,而最新的Spring6則對應最新的Spring Boot 3。

以下透過使用jarkata的實現Bean 驗證(JSR 303)這個規範的程式碼例子來說明Spring Framework是如何實現Bean 驗證(JSR 303)的。

package io.yulin.learn.spring.s101;

import jakarta.validation.ConstraintViolation;
import jakarta.validation.Validation;
import jakarta.validation.Validator;
import jakarta.validation.constraints.*;
import lombok.Data;

import java.util.Set;

/**
 * 演示使用jakarta ee 中的api
 * @author nine
 * @since 1.0
 */
public class JakartaEE {

    public static void main(String[] args) {
        System.out.println("hello jakarta ee");
        UserReq userReq = new UserReq();
        userReq.setName("John Doe");
        userReq.setAge(180);
//        userReq.setEmail("john.doe@example.com");
        userReq.setSex("Male");

        Validator validator = Validation.buildDefaultValidatorFactory().getValidator();
        Set<ConstraintViolation<UserReq>> violations = validator.validate(userReq);

        if (violations.isEmpty()) {
            System.out.println("UserReq is valid");
        } else {
            for (ConstraintViolation<UserReq> violation : violations) {
                System.out.println(violation.getPropertyPath() + " " + violation.getMessage());
            }
        }
    }

}

@Data
class UserReq{
    @NotBlank
    private String name;
    @Max(value = 170, message = "年齡不能大於170歲")
    @Min(value = 1,message = "年齡不能小於1歲")
    private Integer age;
    @Email(message = "請輸入郵箱格式")
    @NotEmpty
    private String email;
    @NotBlank
    private String sex;
}

透過輸出可以發現,透過簡單的註解+jarkata工具就可以驗證輸入的正確性。減少了非常多的ifelse的判斷,大大降低了程式碼的複雜度。

email 不能為空
age 年齡不能大於170歲

對業務pojo的驗證介面是這樣的。實現整個驗證過程的一個實現是org.hibernate.validator.internal.engine.ValidatorImpl#validateInContext。透過反射、解析註解等過程驗證業務類。

	/**
	 * 對物件上的所有約束進行驗證。
	 */
	<T> Set<ConstraintViolation<T>> validate(T object, Class<?>... groups);

Java Specification Requests、Jakarta EE、Spring6 的關係

總的來說Spring是一個集大成者,既選擇了部分Java Specification Requests,也選擇了Jakarta EE。

  • JSRs 與 Jakarta EE 直接相關,因為 Jakarta EE 是由一系列 JSRs 組成的,它是按照 JSR 規範實現的企業級平臺標準。
  • Spring Framework 與兩者都有關聯,因為它既支援和遵守了部分 Jakarta EE 中基於 JSR 的規範,又在此基礎上發展了自己的技術和架構,提供了一套不完全依賴於 Jakarta EE 的完整應用框架。

它們三者的區別在於:

  • JSRs 是規範和標準層面的東西,它定義了技術介面和行為,而不提供具體的實現。
  • Jakarta EE 是一套遵循 JSRs 的企業級平臺實現,提供了完整的軟體棧和部署環境。
  • Spring Framework 則是一個獨立於 Jakarta EE 規範之外的應用框架,雖然相容並整合了許多 Jakarta EE 技術,但它的目標是在簡化企業級應用開發的同時,提供更多自由度和靈活性。

三者之間的關係可以mermaid流程圖表示:

flowchart LR JSR --實現--> Jakarta_EE JSR --實現--> Spring Jakarta_EE --開箱即用的--> Spring

Spring的設計原則

當學習一個框架時,重要的不僅是瞭解它能做什麼,還有它遵循的原則。

以下是Spring Framework的指導原則(來自Spring Framework 的文件):

  • 在每個層面提供選擇。Spring允許您儘可能推遲設計決策。例如,您可以透過配置在不更改程式碼的情況下切換永續性提供程式。對於許多其他基礎設施問題和與第三方API整合也是如此。
  • 容納多元化觀點。Spring支援靈活性,不對應該如何完成事務持有固定看法。它支援各種不同觀點的應用需求。
  • 保持強大的向後相容性。Spring的演進經過精心管理,版本之間幾乎沒有重大變化。Spring支援一系列精心選擇的JDK版本和第三方庫,以便維護依賴於Spring的應用程式和庫。
  • 關注API設計。Spring團隊花費大量時間和精力制定直觀且經得起時間考驗的API。
  • 設定高標準的程式碼質量。Spring Framework非常重視有意義、當前且準確的javadoc。它是為數不多的幾個專案之一,可以聲稱具有清晰的程式碼結構,在各個包之間沒有迴圈依賴。

其中第一條原則很顯然就是開發者常常提到的“預設大於配置”。這條原則貫穿了Spring框架的全生命週期。

透過上述原則可以看到Spring之所以如此流行和強大,並不僅僅是因為開源。還因為它優秀的設計與實現。

與程式設計師的業務開發流程相對比,這也是一個很好的開發流程學習模板。透過良好的設計指導更加完美的實現,透過review閉環輸出高質量的程式碼。

工程化的設計在開發者的程式碼和Spring6框架無處不在。透過對Spring的學習可以提升工程化開發的認知和技能。

關於作者

來自一線全棧程式設計師nine的八年探索與實踐,持續迭代中。歡迎關注公眾號“雨林尋北”或新增個人衛星codetrend(備註技術)。

相關文章