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 從 Great Sand Dunes 開車到丹佛時,我在 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 社群中所有即將舉行的活動。

檢視全部