領先一步
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 機制 (c.f. Assert) |
9% | 在 XML 中使用 dependency-check 屬性 |
13% | 我不需要,我使用建構子注入 |
15% | 使用 InitializingBean 和 assert 機制 |
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 注入的原因之一。setter 注入在 Spring 本身中使用的事實,以及我們主要提倡它的事實也導致許多第三方軟體開始使用 setter 注入,以及部落格和文章開始提及 setter 注入。
(順便問一下,人們還記得 type 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>
這個版本將驗證器提及為屬性名稱和 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 個相依性的類別是否承擔了太多的責任...
有一件事可以確定,那就是我絕對不會在業務方法中,透過檢查的方式來強制執行必要的相依性。