抢先一步
VMware 提供培训和认证,助您快速提升。
了解更多几个月前,我们开始在www.springframework.org上发布调查,请大家提供关于Spring、其一些特性以及他们如何使用这些特性的反馈。我发布的第一个问题是人们是否在检查必需的依赖项,如果是,他们使用了什么机制。我很快对这个问题进行了跟进,询问社区使用了什么事务管理策略。
令我高兴的是,当我第一次查看3月份的结果时,许多人通过投票参与第一个调查告诉我们,他们正在使用@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框架本身中看到大量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>
此版本将身份验证器作为属性名称和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个依赖项,它是否承担了过多的责任……
有一件事是肯定的——通过在业务方法中检查它们来强制执行必需的依赖项是我绝对不会做的事情。