Spring JavaConfig和常見Annotation

Java 5 的推出,加上當年基於純 Java Annotation 的依賴注入框架 Guice 的出現,使得 Spring 框架及其社區也“順應民意”,推出並持續完善了基於 Java 代碼和 Annotation 元信息的依賴關係綁定描述方式,即 JavaConfig 項目。基於 JavaConfig 方式的依賴關係綁定描述基本上映射了最早的基於 XML 的配置方式,比如:

1)表達形式層面

基於 XML 的配置方式是這樣的:

<code>
<beans> xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">


/<beans>/<code>

而基於 JavaConfig 的配置方式是這樣的:

<code>@Configuration
public class MockConfiguration{
// bean定義
} /<code>

任何一個標註了 @Configuration 的 Java 類定義都是一個 JavaConfig 配置類。

2)註冊 bean 定義層面

基於 XML 的配置形式是這樣的:

<code><bean> .../<bean>/<code>

而基於 JavaConfig 的配置形式是這樣的:

<code>@Configuration
public class MockConfiguration {
@Bean
public MockService mockService() {
return new MockServiceImpl();
}
}/<code>

任何一個標註了 @Bean 的方法,其返回值將作為一個 bean 定義註冊到 Spring 的 IoC 容器,方法名將默認成為該 bean 定義的 id。

3)表達依賴注入關係層面

為了表達 bean 與 bean 之間的依賴關係,在 XML 形式中一般是這樣的:

<code><bean>
<property>
/<bean>
<bean> /<code>

而在 JavaConfig 中則是這樣的:

<code>@Configuration
public class MockConfiguration {
@Bean
public MockService mockService() {
return new MockServiceImpl(dependencyService());
}
@Bean
public DependencyService dependencyService() {
return new DependencyServiceImpl();
}
}/<code>

如果一個 bean 的定義依賴其他 bean,則直接調用對應 JavaConfig 類中依賴 bean 的創建方法就可以了。在 JavaConfig 形式的依賴注入過程中,我們使用方法調用的形式注入依賴,如果這個方法返回的對象實例只被一個 bean 依賴注入,那也還好,如果多於一個 bean 需要依賴這個方法調用返回的對象實例,那是不是意味著我們就會創建多個同一類型的對象實例?從代碼表述的邏輯來看,直覺上應該是會創建多個同一類型的對象實例,但實際上最終結果卻不是這樣,依賴注入的都是同一個 Singleton 的對象實例,那這是如何做到的?筆者一開始以為 Spring 框架會通過解析 JavaConfig 的代碼結構,然後通過解析器轉換加上反射等方式完成這一目的,但實際上 Spring 框架的設計和實現者採用了另一種更通用的方式,這在 Spring 的參考文檔中有說明。即通過攔截配置類的方法調用來避免多次初始化同一類型對象的問題,一旦擁有攔截邏輯的子類發現當前方法沒有對應的類型實例時才會去請求父類的同一方法來初始化對象實例,否則直接返回之前的對象實例。所以,原來 Spring IoC 容器中有的特性(features)在 JavaConfig 中都可以表述,只是換了一種形式而已,而且,通過聲明相應的 Java Annotation 反而“內聚”一處,變得更加簡潔明瞭了。

那些高曝光率的 Annotation

至於 @Configuration,我想前面已經提及過了,這裡不再贅述,下面我們看幾個其他比較常見的 Annotation,便於為後面更好地理解 SpringBoot 框架的奧秘做準備。

1. @ComponentScan

@ComponentScan 對應 XML 配置形式中的 <context> 元素,用於配合一些元信息 Java Annotation,比如 @Component 和 @Repository 等,將標註了這些元信息 Annotation 的 bean 定義類批量採集到 Spring 的 IoC 容器中。我們可以通過 basePackages 等屬性來細粒度地定製 @ComponentScan 自動掃描的範圍,如果不指定,則默認 Spring 框架實現會從聲明 @ComponentScan 所在類的 package 進行掃描。@ComponentScan 是 SpringBoot 框架魔法得以實現的一個關鍵組件,大家可以重點關注,我們後面還會遇到它。/<context>

2. @PropertySource 與 @PropertySources

@PropertySource 用於從某些地方加載 *.properties 文件內容,並將其中的屬性加載到 IoC 容器中,便於填充一些 bean 定義屬性的佔位符(placeholder),當然,這需要 PropertySourcesPlaceholderConfigurer 的配合。如果我們使用 Java 8 或者更高版本開發,那麼,我們可以並行聲明多個 @PropertySource:

<code>@Configuration
@PropertySource("classpath:1.properties")
@PropertySource("classpath:2.properties")
@PropertySource("...")
public class XConfiguration{
...
}/<code>

如果我們使用低於 Java 8 版本的 Java 開發 Spring 應用,又想聲明多個 @PropertySource,則需要藉助 @PropertySources 的幫助了,代碼如下所示:

<code>@PropertySources({ @PropertySource("classpath:1.properties"), @PropertySource("classpath:2.properties"), ...})
public class XConfiguration{
...
}/<code>

3. @Import 與 @ImportResource

在 XML 形式的配置中,我們通過 <import> 的形式將多個分開的容器配置合到一個配置中,在 JavaConfig 形式的配置中,我們則使用 @Import 這個 Annotation 完成同樣目的:

<code>@Configuration
@Import(MockConfiguration.class)
public class XConfiguration {
...
}/<code>

@Import 只負責引入 JavaConfig 形式定義的 IoC 容器配置,如果有一些遺留的配置或者遺留系統需要以 XML 形式來配置(比如 dubbo 框架),我們依然可以通過 @ImportResource 將它們一起合併到當前 JavaConfig 配置的容器中。


分享到:


相關文章: