Spring 的 Java 配置選項

工程 | Rod Johnson | 2006 年 11 月 28 日 | ...

得益於我們的可插拔理念以及實現上的大量努力,Spring IoC 容器(以及 Spring 的大部分其他元件)具有極高的靈活性。

經常被忽略的一點是,Spring 配置不一定非得是 XML 格式,儘管 XML 格式是迄今為止最常用的。Spring 有自己的內部元資料格式,即 BeanDefinition 介面及其子介面。代表 IoC 容器例項的 BeanFactory 和 ApplicationContext 實現由這種 Java 元資料驅動,並且與元資料解析(通常由 BeanDefinitionReader 實現執行)相當獨立。

BeanDefinition 元資料最初並不是面向最終開發者的。隨著 Spring 2.0 的推出,NamespaceHandlers(處理 XML 擴充套件名稱空間的類)生成 BeanDefinition 元資料,我們引入了 BeanDefinitionBuilder,透過流式 API 使這變得更容易。但是,生成 BeanDefinition 元資料仍然屬於基礎設施編碼的範疇,而不是在編寫業務邏輯和定義常規 Spring Bean 時每天要做的事情。

今天我想介紹一種在 Java 程式碼中定義 Bean 的新選擇,這種選擇*是*面向終端使用者(開發者)而非基礎設施提供者的。這目前是一個里程碑式的釋出,是 Spring 核心的一個附加元件,但可能會被整合到 Spring 本身。

讓我們從一個例子開始。

@Configuration
public class MyConfig {
   @Bean
   public Person rod() {
      return new Person("Rod Johnson");
   }

   @Bean(scope = Scope.PROTOTYPE)
   public Book book() {
      Book book = new Book("Expert One-on-One J2EE Design and Development");
      book.setAuthor(rod());  // rod() method is actually a bean reference !
      return book;
   }
}

@Configuration 註解標識這個物件為一個特殊的配置類。每個 @Bean 方法定義一個 Bean。Bean 的名稱就是方法的名稱。可以透過註解定義額外的別名,但最好從方法名中獲取名稱,而不是從註解中獲取,因為這意味著編譯器可以確保名稱的唯一性。

Bean 在 Java 程式碼中配置,使用建構函式、屬性或任意方法呼叫。請注意,呼叫另一個 Bean 方法會建立從“book”Bean 到“rod”Bean 的依賴關係。但這與在沒有框架支援的情況下在 Java 中例項化物件相比,具有關鍵優勢:例如

  • 每個 @Bean 都是一個 Spring 元件,可以利用所有 Spring 服務,例如宣告式事務管理。
  • 每個公共 @Bean 方法都會新增到 Spring 容器中,因此它有資格注入到其他物件中,進行 JMX 匯出以及其他好處。
  • 它能夠無縫地融入現有的 Spring 環境。

將此與實現相同結果的 XML 定義進行比較,可能會更容易理解發生了什麼,XML 定義看起來如下:

<bean id="rod" class="Person" scope="singleton">
   <constructor-arg>Rod Johnson</constructor-arg>
</bean>

<bean id="book" class="Book" scope="prototype">
   <constructor-arg>Expert One-on-One J2EE Design and Development</constructor-arg>
   <property name="author" ref="rod"/>
</bean>

雖然它基於註解,但這種 Java 配置機制在我見過的註解用法中是獨一無二的,因為註解不包含在核心業務邏輯中,而是包含在單獨的配置類中。實際上,它是一種配置的 DSL。因此,它保留了 Spring 的非侵入性承諾:您無需更改 Java 程式碼即可使用它。

配置類類似於 XML Bean 定義檔案,因此 @Configuration 註解包含一些與 <beans> 元素相似的選項,例如預設的自動注入或延遲初始化。例如:


@Configuration(defaultAutowire = Autowire.BY_TYPE, defaultLazy = Lazy.FALSE)
public class DataSourceConfiguration  extends ConfigurationSupport {
}

@Bean 註解允許像 <bean> 元素一樣,在本地設定作用域和延遲初始化等選項。預設作用域是 Singleton,與 XML 相同。

這種風格的 Java 配置具有一些有趣的特點。例如:

  • 引用(例如示例中對“rod”Bean 的引用)在重構後仍然有效;任何好的 IDE 都提供強大的工具支援。
  • <li>Because configurations are Java classes, they can participate in inheritance relationships. For example, you could define a superclass that demands some abstract @Beans to be implemented in subclasses.</li>
    <li>It creates a new visibility option. An @Bean method can be protected, it which case it benefits from the usual characteristics of the Spring component, but is not visible externally--that is it not injectable and cannot be obtained by calling getBean() on the IoC context.</li>
    

我的經驗表明,當向人們展示這一點時(一年多了),他們有時會花點時間來理解,但通常最終會非常興奮。

這*不是*為了取代 Spring 的 XML 格式。就像 Spring 2.0 的擴充套件名稱空間一樣——以及自 Spring 1.0 以來就可能使用的屬性檔案——它是一個額外的選擇。複雜的應用程式需要多種型別的配置,Spring 旨在為配置提供最佳的整體解決方案。我們將繼續探索額外的配置形式。

您通常會混合使用 Java 和 XML 配置。您可以在同一個應用程式上下文中任意數量的 Java 配置類。

下面的示例使用 XML Bean 定義來定義一個 MyConfig Bean,如上所示,它可以像任何普通 Bean 一樣被注入。ConfigurationPostProcessor 處理所有帶有 @Configuration 註解的 Bean,生成必要的 Bean 定義。

<beans>

 <bean class="..MyConfig"/>


 <bean class="org.springframework.beans.factory.java.ConfigurationPostProcessor"/>
 
 <bean class="SomeRandomBean">
 	<property...
 </bean>
</beans>

當然,您可以在同一個 XML 中擁有普通的 Bean 定義,例如此示例中的“SomeRandomBean”。您可以構建一個由 Java 配置和現有 XML 配置組成的上下文。

Costin 還實現了一個方便的應用程式上下文,它使用萬用字元從類路徑中載入類,如下所示:

ApplicationContext oneConfig = new  AnnotationApplicationContext(SimpleConfiguration.class.getName());
ApplicationContext aBunchOfConfigs = new AnnotationApplicationContext("**/configuration/*Configuration.class");

使用 ASM 檢查類,而無需載入它們。在未來的版本中,我們可能會提供額外的自動檢測場景。

釋出版本在此。Costin Leau 現在是專案負責人。該程式碼應被視為 Alpha 質量。我們包含了一個修改後的 Spring Pet Store 的示例,但毫無疑問,將從實際專案中使用中吸取教訓。

Costin 和我都非常希望您對這項功能提供反饋。

這段程式碼會怎麼樣?嗯,這取決於你們。它當然需要反饋(歡迎提出建議),並且透過實際使用,任何技術都會展現出各種可能性(和實現上的改進)。它目前不在路線圖上,但如果引起足夠的興趣,可能會被整合到 Spring 核心的未來版本中。

此外,它還需要一個響亮的名字。歡迎提出建議!


儘管今天是第一個(Alpha)釋出版本,但這個功能已經有了驚人的悠久歷史——一年多。我在 2005 年 8 月參加科羅拉多州克雷斯特德位元舉行的軟體峰會時,與 Spring.NET 專案的 Mark Pollack 和 Aleks Seovic 一起,陷入了一陣瘋狂的編碼。我記得在 Aleks 開車從大沙丘前往丹佛的途中,在 Jaguar XJ8 的後座寫了很多程式碼。我可能需要寫程式碼來分散我對危險的注意力。我認為這個想法的起源可能可以追溯到 2005 年 JavaOne 大會與 Howard Lewis Ship 的一次談話……

遺憾的是,自從那以後,我沒有時間再對它進行更多的斷斷續續的開發,所以一直沒有達到可以釋出的程度。幸運的是,Spring Modules 的負責人兼 Spring 方面的全能專家 Costin Leau,自從今年早些時候加入 Interface21 以來,有更多的時間用於 Spring 編碼,他已經挺身而出,推動這項工作。

該實現不需要對 Spring 核心進行任何修改。正如我所說,IoC 容器非常靈活。如果您有興趣,它將配置物件視為一個工廠 Bean,並且每個 Bean 定義都由該物件上的例項工廠方法支援:這是自 Spring 1.1 以來就可用的機制。它對配置例項進行了一些位元組碼操作,目前使用 CGLIB,以確保對單例作用域的 @Bean 方法的重複呼叫始終返回相同的物件。

獲取 Spring 新聞通訊

透過 Spring 新聞通訊保持聯絡

訂閱

領先一步

VMware 提供培訓和認證,助您加速進步。

瞭解更多

獲得支援

Tanzu Spring 提供 OpenJDK™、Spring 和 Apache Tomcat® 的支援和二進位制檔案,只需一份簡單的訂閱。

瞭解更多

即將舉行的活動

檢視 Spring 社群所有即將舉行的活動。

檢視所有