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 還實現了一個方便的應用程式上下文,它使用萬用字元從 classpath 中載入類,如下所示

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 駕車從大沙丘前往丹佛時,我在捷豹 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 社群即將舉行的所有活動。

檢視全部