Spring Framework 概覽

翻譯 Spring Framework Reference Documentation,有增刪改
Spring 框架是一個輕量級的解決方案,可以一站式地構建企業級應用。Spring 是模塊化的,開發者可以在項目中只使用其中自己需要的部分,例如,可以在任何 web 框架上使用控制反轉(IoC),也可以只使用 Hibernate 集成代碼或 JDBC 抽象層。它支持聲明式事務管理、通過 RMI 或 web 服務實現遠程訪問,并可以使用多種方式持久化數據。它提供了功能全面的 MVC 框架 ,也可以透明地集成 AOP 到軟件中。
Spring 是非侵入式的,這意味著項目的域邏輯代碼通常不會依賴于框架本身。在集成層(比如數據訪問層)可能會同時依賴于數據訪問技術和 Spring,但是這些依賴可以很容易地從代碼中分離出來。
Spring框架是基于 Java 平臺的,為開發Java應用提供了全方位的基礎設施支持,所以你只需要關注你的應用本身。
Spring可以使用 POJO(plain old Java object)創建應用,并且可以將企業服務非侵入式地應用到 POJO。這項功能適用于 Java SE 編程模型以及全部或部分的 Java EE
那么,Spring 框架對 JAVA 開發項目有那些幫助?

  • 不用關心事務 API 就可以執行數據庫事務;
  • 不用關心遠程 API 就可以使用遠程操作;
  • 不用關心 JMX API 就可以進行管理操作;
  • 不用關心 JMS API 就可以進行消息處理。

一. Spring Framework 基本構成

Spring Framework 大致按功能劃分為20個模塊,并可分為 Core Container、Data Access/Integration、Web, AOP (Aspect Oriented Programming)、Instrumentation、Messaging以及 Test 這7組,如下圖所示:


GroupId ArtifactId Description
org.springframework spring-aop 基于代理的AOP支持
org.springframework spring-aspects 基于AspectJ 的切面
org.springframework spring-beans Beans 支持,包含 Groovy
org.springframework spring-context 應用上下文運行時,包括調度和遠程抽象
org.springframework spring-context-support 支持將常見的第三方類庫集成到 Spring 應用上下文
org.springframework spring-core Spring 其他模塊所依賴的核心模塊
org.springframework spring-expression Spring 表達式語言,SpEL
org.springframework spring-instrument JVM 引導的儀表(監測器)代理
org.springframework spring-instrument-tomcat Tomcat 的儀表(監測器)代理
org.springframework spring-jdbc JDBC(Java Data Base Connectivity) 支持包,包括數據源設置和 JDBC 訪問支持
org.springframework spring-jms JMS(Java Message Service) 支持包,包括發送/接收JMS消息的助手類
org.springframework spring-messaging 對消息架構和協議的支持
org.springframework spring-orm 對象/關系映射(Object/Relational Mapping,ORM),包括對 JPA 和 Hibernate 的支持
org.springframework spring-oxm 對象/XML 映射(Object/XML Mapping,OXM)
org.springframework spring-test 單元測試和集成測試支持組件
org.springframework spring-tx 事務基礎組件,包括對 DAO 的支持及 JCA 的集成
org.springframework spring-web web支持包,包括客戶端及web遠程調用
org.springframework spring-webmvc REST web 服務及 web 應用的 MVC 實現
org.springframework spring-webmvc-portlet 用于 Portlet 環境的MVC實現
org.springframework spring-websocket WebSocket 和 SockJS 實現,包括對 STOMP 的支持

以下章節簡要說明各部分內容。

1.1 Core Container - 核心容器

Spring Core Container 包含 spring-core, spring-beans, spring-context, spring-context-support, 以及 spring-expression (Spring Expression Language,Spring 表達式語言,SpEL)模塊。
其中,spring-core 和 spring-beans 模塊提供了 Spring 框架 的基礎功能,包括控制反轉(IoC)和依賴注入。BeanFactory 是工廠模式的一種復雜而精致的實現,它使開發者不再需要編碼式單例(programmatic singletons),并可將配置和依賴從實際編碼邏輯中解耦。
spring-context 模塊使得由 Core 和 Beans 提供的基礎功能真正構建成堅實的地基:這意味著Spring 工程能以框架模式訪問對象,類似于JNDI注冊表。Context 模塊繼承了Beans 模塊的特性并增加了對國際化(例如資源綁定)、事件傳播、資源加載和context 透明化(例如 Servlet container)。同時,也支持JAVA EE 特性,例如 EJB、 JMX 和 基本的遠程訪問。Context 模塊的關鍵是 ApplicationContext 接口。spring-context-support 則提供了對第三方庫集成到 Spring-context 的支持,比如緩存(EhCache, Guava, JCache)、郵件(JavaMail)、調度(CommonJ, Quartz)、模板引擎(FreeMarker, JasperReports, Velocity)等。
spring-expression 模塊為在運行時查詢和操作對象圖提供了強大的表達式語言。它是JSP2.1規范中定義的統一表達式語言的擴展,支持 set 和 get 屬性值、屬性賦值、方法調用、訪問數組集合及索引的內容、邏輯算術運算、命名變量、通過名字從Spring IoC容器檢索對象,還支持列表的投影、選擇以及聚合等。

1.2 Data Access/Integration - 數據訪問與集成

數據訪問與集成層包含 JDBC、ORM、OXM、JMS和事務模塊。
spring-jdbc 模塊提供了 JDBC抽象層,它消除了冗長的 JDBC 編碼和對數據庫供應商特定錯誤代碼的解析。
spring-tx 模塊支持編程式事務和聲明式事務,可用于實現了特定接口的類和所有的 POJO 對象。編程式事務需要自己寫beginTransaction()、commit()、rollback()等事務管理方法,聲明式事務是通過注解或配置由 spring 自動處理,編程式事務粒度更細。
spring-orm 模塊提供了對流行的對象關系映射 API的集成,包括 JPA、JDO 和 Hibernate 等。通過此模塊可以讓這些 ORM 框架和 spring 的其它功能整合,比如前面提及的事務管理。
spring-oxm 模塊提供了對 OXM 實現的支持,比如JAXB、Castor、XML Beans、JiBX、XStream等。
spring-jms 模塊包含生產(produce)和消費(consume)消息的功能。從Spring 4.1開始,集成了 spring-messaging 模塊。

1.3 AOP

spring-aop 模塊提供了面向切面編程(AOP)的實現,可以定義諸如方法攔截器和切入點等,從而使實現功能的代碼徹底的解耦。使用源碼級的元數據,可以用類似于.Net 屬性的方式合并行為信息到代碼中。
spring-aspects 模塊提供了對 AspectJ 的集成。

1.4 Instrumentation

spring-instrument 模塊提供了對檢測類的支持和用于特定的應用服務器的類加載器的實現。
spring-instrument-tomcat 模塊包含了用于 Tomcat 的Spring 檢測代理。

1.5 Messaging - 消息處理

從 Spring 4 開始集成了spring-messaging 模塊,是從一些 Spring 集成項目的關鍵抽象中提取出來的,這些項目包括 Message、MessageChannel、MessageHandler 和其它服務于消息處理的項目。這個模塊也包含一系列的注解用于映射消息到方法,這類似于Spring MVC 基于編碼模型的注解。

1.6 Web

Web 層包括 spring-web、spring-webmvc、spring-websocket、spring-webmvc-portlet 等模塊。
spring-web 模塊提供面向 web 的基本功能和面向 web 的應用上下文,比如 multipart 文件上傳功能、使用 Servlet 監聽器初始化 IoC 容器等。它還包括 HTTP 客戶端以及 Spring 遠程調用中與 web 相關的部分。
spring-webmvc 模塊(即 Web-Servlet 模塊)為 web 應用提供了模型視圖控制(MVC)和 REST Web 服務的實現。Spring 的 MVC 框架可以使領域模型代碼和 web 表單完全地分離,且可以與 Spring 框架的其它所有功能進行集成。
spring-webmvc-portlet 模塊(即Web-Portlet模塊)提供了用于 Portlet 環境的 MVC 實現,并反映了 pring-webmvc 模塊的功能。

1.7 Test

spring-test 模塊通過 JUnit 和 TestNG 組件支持單元測試和集成測試。它提供了一致性地加載和緩存 Spring 上下文,也提供了用于單獨測試代碼的模擬對象(mock object)。

二. 使用場景

不管是資源受限的嵌入式應用還是使用了事務管理和 web 集成框架的成熟的企業級應用,Spring 都是一個值得信賴的選擇。

2.1 經典的 Spring web 應用

Spring 的聲明式事務管理可以使 web 應用完全事務化,就像使用 EJB 容器管理的事務。所有訂制的業務邏輯都可用 POJO 實現,并用 Spring 的 IoC 容器進行管理。此外還包括發郵件和驗證功能,其中驗證功能是從 web 層分離的,由你決定何處執行驗證。Spring 的 ORM 可以集成 JPA、hibernate和 JDO 等。比如使用 Hibernate 時,可以繼續使用已存在的映射文件和標準的 Hibernate SessionFactory 配置。表單控制器無縫地把 web 層和領域模型集成在一起,移除了 ActionForms 和其它把 HTTP 參數轉換成領域模型的類。


2.2 使用第三方 web 框架和 Spring 中間件

一些場景可能不允許你完全切換到另一個框架。然而,Spring 框架不強制你使用它所有的東西,它不是非此即彼(all-or-nothing)的解決方案。前端使用 Struts、Tapestry、JSF 或別的 UI 框架可以和 Spring 中間件集成,從而使用 Spring 的事務管理功能。僅僅只需要使用 ApplicationContext 連接業務邏輯,并使用 WebApplicationContext 集成 web 層即可。

2.3 遠程調用使用場景

當需要通過 web 服務訪問現有代碼時,可以使用Spring的Hessian-,Burlap-,Rmi-或者JaxRpcProxyFactory類,遠程訪問現有的應用并非難事。

2.4 EJBs - 包裝現有的POJO

Spring框架也為 EJB 提供了訪問抽象層,可以重新使用現有的 POJO 并把它們包裝到無狀態的會話 bean 中,以使其用于可擴展的安全的 web 應用中。


三. 依賴管理

依賴管理和依賴注入是兩碼事。為了讓應用程序擁有 Spring 的這些非常棒的功能(如依賴注入),需要在工程中導入所需的jar 包,并在運行時放在classpath 下,有可能的話編譯期也應該放在 classpath 下。這些依賴并不是被注入的虛擬組件,而是文件系統上實在的物理資源。依賴管理的處理過程涉及到定位這些資源、存儲并添加它們到 classpath 下。依賴可能是直接的(比如運行時依賴于 Spring),也可能是間接的(比如依賴于 commons-dbcp,commons-dbcp 又依賴于 commons-pool)。間接的依賴又被稱作“傳遞”,它們是最難識別和管理的。
如果準備使用 Spring,則需要拷貝一份所需 Spring 模塊的 jar 包。為了便于使用,Spring 被打包成一系列的模塊以盡可能地減少依賴,比如,如果不是在寫一個web應用,那就沒必要引入 spring-web 模塊。
每個版本的Spring都會在以下地方發布artifact:

  • Maven中央倉庫,默認的 Maven 查詢倉庫,并且不需要特殊的配置就可以使用。許多 Spring 依賴的公共庫也可以從 Maven 中央倉庫獲得,并且大部分的 Spring 社區也使用 Maven 作為依賴管理工具,所以很方便。Maven中的jar包命名格式為spring-*-<version>.jar,其groupId是org.springframework。
  • 特別托管 Spring 的公共 Maven 倉庫。除了最終的 GA 版本,這個倉庫也托管了開發的快照版本和里程碑版本。jar 包和 Maven 中央倉庫中的命名一致,所以這也是一個獲取 Spring 的開發版本的有用地方,可以和 Maven 中央倉庫中部署的其它庫一起使用。這個倉庫也包含了所有 Spring jar 包的發行版的zip文件,以便于下載。

因此,首先要做的事就是決定如何管理依賴關系,我們一般推薦使用成熟的依賴和構建生命周期管理系統,比如 Maven、Gradle 或 Ivy,當然你也可以手動下載所有的jar包。
Spring 對大部分企業和其它外部工具提供了集成和支持,把強制性的外部依賴降到了最低,這樣就不需要為了簡單地使用 Spring 而去尋找和下載大量的 jar 包了。基本的依賴注入只有一個強制性的外部依賴,那就是日志管理(參考下面關于日志管理選項的詳細描述)。
下面列出依賴于 Spring 的應用的基本配置步驟,首先使用 Maven,然后 Gradle,最后 Ivy。在所有案例中,如果有什么不清楚的地方,參考所用的依賴管理系統的文檔或查看一些范例代碼—— Spring 構建時本身使用 Gradle 管理依賴,所以我們的范例大部分使用 Gradle 或 Maven。

3.1 使用 Maven 管理依賴

如果使用Maven作為依賴管理工具,甚至不需要明確地提供日志管理的依賴。例如,創建應用上下文并使用依賴注入配置應用程序,Maven的依賴關系看起來像下面一樣:
<pre><dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>4.3.9.RELEASE</version>
<scope>runtime</scope>
</dependency>
</dependencies></pre>

就這樣,注意如果不需要編譯Spring API,可以把scope聲明為runtime,這是依賴注入使用的典型案例。
上面的示例使用Maven中央倉庫,如果使用Spring的Maven倉庫(例如,里程碑或開發快照),需要在Maven配置中指定倉庫位置。
release版本:
<pre><repositories>
<repository>
<id>io.spring.repo.maven.release</id>
<url>http://repo.spring.io/release/</url>
<snapshots><enabled>false</enabled></snapshots>
</repository>
</repositories></pre>

里程碑版本:
<pre><repositories>
<repository>
<id>io.spring.repo.maven.milestone</id>
<url>http://repo.spring.io/milestone/</url>
<snapshots><enabled>false</enabled></snapshots>
</repository>
</repositories></pre>

快照版本:

<pre><repositories>
<repository>
<id>io.spring.repo.maven.snapshot</id>
<url>http://repo.spring.io/snapshot/</url>
<snapshots><enabled>true</enabled></snapshots>
</repository>
</repositories></pre>

使用 Maven 時可能會不小心混合了 Spring 不同版本的 jar 包。例如,你可能會發現第三方庫或其它的 Spring 項目存在舊版本的傳遞依賴。如果沒有明確地聲明直接依賴,各種各樣不可預料的情況將會出現。
為了解決這個問題,Maven 提出了“物料清單式”(Bill Of Materials,BOM)依賴的概念。可以在 dependencyManagement 部分導入 spring-framework-bom 以保證所有的 Spring 依賴(不管直接還是傳遞依賴)使用相同的版本。
<pre><dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-framework-bom</artifactId>
<version>4.3.9.RELEASE</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement></pre>

使用 BOM 的另外一個好處是不再需要指定 <version> 屬性了:
<pre><dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
</dependency>
<dependencies></pre>

3.2 使用 Gradle 管理依賴

為了在 Gradle 構建系統中使用 Spring 倉庫,需要在 repositories 部分包含合適的 URL:
<pre>repositories {
mavenCentral()
// and optionally...
maven { url "http://repo.spring.io/release" }
}
</pre>

可以酌情把repositories URL中的/release修改為/milestone/snapshot。一旦倉庫配置好了,就可以按Gradle的方式聲明依賴關系了。
<pre>dependencies {
compile("org.springframework:spring-context:4.3.9.RELEASE")
testCompile("org.springframework:spring-test:4.3.9.RELEASE")
}</pre>

3.3 使用 Ivy 管理依賴

使用 Ivy 管理依賴關系有相似的配置選項。
ivysettings.xml 中添加 resolver 配置使 Ivy 指向 Spring 倉庫:
<pre><resolvers>
<ibiblio name="io.spring.repo.maven.release"
m2compatible="true"
root="http://repo.spring.io/release/"/>
</resolvers></pre>

可以酌情把root URL中的/release修改為/milestone或/snapshot。一旦配置好了,就可以按照慣例添加依賴了(在ivy.xml中):
<pre><dependency org="org.springframework"
name="spring-core" rev="4.3.9.RELEASE" conf="compile->runtime"/></pre>

四. 日志

對于 Spring 來說日志管理是非常重要的依賴關系,因為:

  • 它是唯一的強制性外部依賴;
  • 每個人都喜歡從他們使用的工具看到一些輸出;
  • Spring 集成了許多其它工具,它們都各自選擇了日志管理的依賴。

開發者通常在整個應用程序(包括所有的外部組件)的中心位置統一配置日志管理,這是非常困難的,因為現在有很多日志管理的框架可供選擇。
Spring 中強制的日志管理依賴是 Jakarta Commons Logging API(JCL)。我們編譯了 JCL,并使 JCL 的 Log 對象對繼承了 Spring 框架的類可見。對用戶來說所有版本的 Spring 使用相同的日志管理庫很重要,因為這使得遷移很簡單,因為Spring保存了向后兼容,即使對于擴展了 Spring 的應用也能向后兼容。
這是怎么做到的呢?我們讓 Spring 的一個模塊明確地依賴于 commons-logging(JCL的典型實現),然后讓所有其它模塊都在編譯期依賴于這個模塊。例如,使用 Maven,你想知道哪里依賴了 commons-logging,其實是 Spring 確切地說是其核心模塊 spring-core 依賴了。
commons-logging 的優點是不需要其它任何東西就可以使應用程序運轉起來。它擁有一個運行時發現算法,用于在 classpath 中尋找其它日志管理框架并且適當地選擇一個使用(或者告訴它使用哪個)。如果不需要其它的功能了,你就可以從 JDK(java.util.logging 或 JUL)得到一份看起來很漂亮的日志了。你會發現大多數情況下你的 Spring 應用程序工作得很好且日志很好地輸出到了控制臺,這很重要。

4.1 禁用 Commons Logging

不幸的是,commons-logging 的運行時發現算法雖然對于終端用戶很方便,但存在一定的問題。有兩種方式關掉 commons-logging

  • spring-core 模塊中去除對 commons-logging 的依賴(因為這是唯一明確依賴于 commons-logging 的地方)
  • 依賴于一個特定的 commons-logging 且把其 jar 包換成一個空 jar 包(具體做法參考SLF4J FAQ

如下,在 dependencyManagement 中添加部分代碼就可以排除掉 commons-logging 了:
<pre><dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>4.3.9.RELEASE</version>
<exclusions>
<exclusion>
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
</exclusion>
</exclusions>
</dependency>
</dependencies></pre>

現在這個應用可能是殘缺的,因為在classpath上沒有 JCL API 的實現,所以需要提供一個新的去修復它。

4.2 使用 Log4j

許多人使用 Log4j 作為日志管理框架。它是高效和完善的,實際上在構建和測試 Spring時我們使用的就是它。Spring 也提供了一些工具用于配置和初始化 Log4j,所以某些模塊在編譯期也可以選擇依賴于 Log4j。
為了使用 Log4j 2.x,開發者需要拷貝相應版本的 Log4j 到 classpath 并提供相應的配置文檔(log4j2.xml、log4j2.properties 或 其他支持的配置格式)。對于 Maven 項目而言,最小依賴如下所示:
<pre><dependencies>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-core</artifactId>
<version>2.6.2</version>
</dependency>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-jcl</artifactId>
<version>2.6.2</version>
</dependency>
</dependencies></pre>

如果你也想將 SLF4J 日志委托給 Log4j,例如在項目依賴的其他庫默認使用 SLF4J 的情況下,還需要以下依賴:
<pre><dependencies>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-slf4j-impl</artifactId>
<version>2.6.2</version>
</dependency>
</dependencies></pre>

以下是使用 Logfj2.xml 配置輸出日志到 console 的例子:
<pre><?xml version="1.0" encoding="UTF-8"?>
<Configuration status="WARN">
<Appenders>
<Console name="Console" target="SYSTEM_OUT">
<PatternLayout pattern="%d{HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n"/>
</Console>
</Appenders>
<Loggers>
<Logger name="org.springframework.beans.factory" level="DEBUG"/>
<Root level="error">
<AppenderRef ref="Console"/>
</Root>
</Loggers>
</Configuration></pre>

4.3 使用 SLF4J 與 Log4j 或 Logback

SLF4J(Simple Logging Facade for Java )是 Spring 中其他模塊經常采用的 API,它通常與 Logback 一塊兒使用。后者是 SLF4J API 的一種本地化實現。
SLF4J 提供了與多種其他常見日志框架的綁定橋梁以委托日志輸出,包括Log4j。開發者可以決定將SLF4J管理的日志委托給Log4j輸出,或者反過來。為了使用 SLF4J,需要在依賴中將 commons-logging 替換成 SLF4J-JCL 橋。此后 Spring 內部的日志調用將轉換為調用 SLF4J API,如果應用中的其他依賴也使用了 SLF4J,那么日志就能在一個地方統一管理了。
一個常用的選擇是將 Spring 橋接到 SLF4J 后 再顯式地將 SLF4J 綁定到 Log4j,那么需要提供以下依賴并排除 commons-logging:JCL 橋、SLF4J到Log4j 的綁定、以及 Log4j 本身。
<pre><dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>4.3.9.RELEASE</version>
<exclusions>
<exclusion>
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>jcl-over-slf4j</artifactId>
<version>1.7.21</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
<version>1.7.21</version>
</dependency>
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
</dependencies></pre>

另一個常見選擇是直接綁定到 Logback。這可以移除其他額外步驟和依賴,因為 Logback 直接實現了 SLF4J API:
<pre><dependencies>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>jcl-over-slf4j</artifactId>
<version>1.7.21</version>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>1.1.7</version>
</dependency>
</dependencies></pre>

4.4 使用 JUL (java.util.logging)

如果 classpath 目錄下沒有Log4j,commons-logging 將默認委托給 java.util.logging,所以在這種情況下無需任何其他配置。

4.5 帶有本地 JCL 的運行時容器

許多人在一個容器中運行 Spring 應用程序,而這個容器本身又提供了JCL的實現,例如 IBM 的 Websphere Application Server(WAS)。這本身沒什么問題,但要考慮到以下兩種不同場景:

  • 在 “parent first” 類加載委托模型(WAS即默認如此)下,應用通常選用父容器提供的 commons-logging 版本,委托給 WAS 日志子系統(這個子系統實際上是基于 JUL 的)。而應用本身提供的 JCL 變體(不管是 Spring 默認的 commons-logging 版本還是JCL-over-SLF4J 橋)則被忽略,應用所依賴的其他庫所默認的 日志管理框架也同樣會被完全忽略。
  • 在“parent last” 委托模型(在常規的Servlet容器中既如此,WAS中則需要顯式配置)中,應用會默認選用自身配置的日志管理框架。如果本身沒有特別配置,則和 “parent first” 場景相同。

總而言之,我們推薦采用 “parent last” 場景。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容