搶佔先機
VMware 提供培訓和認證,助您快速提升。
瞭解更多幾個月前,我們開始在 www.springframework.org 上釋出民意調查,邀請大家就 Spring、其部分特性以及如何使用這些特性提供反饋。我釋出的第一個問題是,大家是否檢查了必需的依賴項,如果檢查了,使用了什麼機制。我很快就這個問題繼續詢問社群使用了什麼事務管理策略。
令我高興的是,當我第一次檢視三月份的投票結果時,許多人在第一次投票中表示他們正在使用 @Required 註解。關於事務管理的第二次投票很快顯示,許多人正在使用 @Transactional 註解。您可以在下面找到關於檢查必需依賴項的部分投票結果。這些結果連同關於事務管理的投票(約 30% 的受訪者使用 @Transactional 註解來劃分事務邊界)一致表明,人們大量使用了 Spring 2.0,這對我們來說是個非常好的訊息。由於將使用 Spring 1.x 的應用程序升級到 Spring 2.0 應該不是問題,我們真心希望人們不要停留在 Spring 1.x 上,事實上,人們大規模地進行了升級。
8% | 我在業務方法中檢查它們 |
9% | 使用 init-method 和斷言機制(參閱 Assert) |
9% | 在 XML 中使用 dependency-check 屬性 |
13% | 我不需要,我使用建構函式注入 |
15% | 使用 InitializingBean 和斷言機制 |
17% | 使用 Spring 2.0 @Required 註解 |
29% | 我不檢查必需的依賴項 |
然而有趣的是,有 29% 的人沒有檢查必需的依賴項。在隨討論釋出的論壇帖中,出現了一些有趣的建議,說明為什麼有些人沒有這樣做以及人們如何透過其他方式解決這個問題。讓我們回顧其中的一些。
換句話說,我們可以強制我們類的一個使用者(同樣,這可能是 Spring,但也可能是直接例項化你的類的單元測試)在例項化時傳入引數。
public class Service {
public Collaborator collaborator;
// constructor with arguments, you *have* to
// satisfy the argument to instantiate this class
public Service(Collaborator collaborator) {
this.collaborator = collaborator;
}
}
當需要檢查必需的依賴項時,我們可以利用這一點。如果我們修改上面的程式碼示例以包含斷言,我們可以 100% 確定該類永遠不會在沒有注入其協作者的情況下例項化。
public Service(Collaborator collaborator) {
if (collaborator == null) {
throw new IllegalArgumentException("Collaborator cannot be null");
}
this.collaborator = collaborator;
}
換句話說,如果我們使用建構函式注入並結合我上面展示的斷言機制,我們就不需要依賴檢查機制。
這就是為什麼您在 Spring Framework 本身中看到大量 setter 注入的原因之一。Spring 本身使用了 setter 注入的事實,以及我們主要倡導它,也導致許多第三方軟體開始使用 setter 注入,以及部落格和文章開始提及 setter 注入。
(順便問一下,大家還記得控制反轉的 1 類、2 類和 M 類嗎 ;-) )
正因為這兩個原因,我認為建構函式注入對於應用程式程式碼比對於框架程式碼更具可用性。在應用程式程式碼中,你天生對需要配置的可選值的需求較少(你的應用程式程式碼不太可能在許多情況下使用,這需要可配置的屬性)。其次,應用程式程式碼使用類繼承的頻率遠低於框架程式碼。例如,在應用程式中,特化在應用程式程式碼中發生的頻率不如在框架程式碼中那樣頻繁——再次強調,應用程式程式碼的使用場景數量要少得多。
不使用建構函式注入的另一個論點是建構函式中缺少引數名稱,以及這些名稱不出現在 XML 中。我認為在大多數應用程式中,這並沒有太大關係。首先考慮使用 setter 注入的變體。
<bean id="authenticator" class="com.mycompany.service.AuthenticatorImpl"/>
<bean id="accountService" class="com.mycompany.service.AccountService">
<property name="authenticator" ref="authenticator"/>
</bean>
這個版本將 authenticator 作為屬性名和 bean 名提及。這是我經常遇到的模式。我認為在使用建構函式注入時,建構函式引數名稱的缺失(以及它們不出現在 XML 中)並不會讓我們感到困惑。
<bean id="authenticator" class="com.mycompany.service.AuthenticatorImpl"/>
<bean id="accountService" class="com.mycompany.service.AccountService">
<constructor-arg ref="authenticator"/>
</bean>
public class Service {
private Collaborator collaborator;
@Required
public void setCollaborator(Collaborator c) {
this.collaborator = c;
}
}
<bean class="org.sfw.beans.factory.annotation.RequiredAnnotationBeanFactoryPostProcessor"/>
public class Service implements InitializingBean {
private Collaborator collaborator;
public void setCollaborator(Collaborator c) {
this.collaborator = c;
}
// from the InitializingBean interface
public void afterPropertiesSet() {
if (collaborator == null) {
throw new IllegalStateException("Collaborator must be set in order for service to work");
}
}
}
另一個類似於 Java 中的 @Required 的機制是 XML 中的 dependency-check 屬性,奇怪的是它並沒有被大量使用。透過修改此屬性(預設關閉)啟用依賴檢查,將告訴 Spring 開始檢查 bean 的某些依賴項。請參閱參考資料以獲取有關此特性的更多資訊。
有些情況下我不會使用建構函式注入。例如,其中一種情況是具有大量依賴項或其他可配置值的類。我個人認為一個帶有 20 個引數的建構函式不是一個好程式碼示例。當然,問題是,一個擁有 20 個依賴項的類是否承擔了過多的職責...
有一件事是肯定的——在業務方法中檢查必需的依賴項來強制執行,這是我絕對不會做的事情。