我有一个带有Spring IoC容器的Java EE项目。
我刚刚在Utils
类静态方法sendMail(long list of params)
中找到了。我不知道为什么,但我觉得如果我们有单独的类(具有单例范围的Spring bean)将负责发送电子邮件会更好看。但是我找不到任何可以证明我立场的论据。
那么,在这种(相当普遍的)情况下使用DI是否有任何优点(或缺点)?
答案 0 :(得分:6)
如果需要发送电子邮件的类使用MailSender
接口(示例名称)而不是static Utils.sendMail(...)
方法,那么您可以通过交换模拟/不同的实现来轻松地对这些类进行单元测试单元测试MailSender
而不是使用真正的实现。
这允许您在邮件发送成功,捕获异常/错误等时测试这些类的行为。
如果类使用的是静态方法,就像你所描述的那样,那么你就不能在不实际发送电子邮件的情况下单独测试这些类的行为 - 此时它不是一个有效的,孤立的单元测试,因为你现在将取决于1)邮件发送代码2)外发邮件服务器等的行为
答案 1 :(得分:5)
是的!我会根据自己最近的经历给你一个例子。
我们最近将一堆应用从直接发送电子邮件切换到使用数据库队列发送消息。消息在DB中排队,然后通过批处理发送。这使我们能够处理SMTP服务器中断,重新发送消息,控制可以从我们的开发服务器发送的消息,验证消息是否实际发送等等。
像发送电子邮件这样简单的事情当然可能是您将来想要改变的事情。注入实现会使这更容易。
答案 2 :(得分:4)
包含通常静态函数混合的Util类型类通常不是好事。我甚至在我参与的一个项目中看到过那种名为UglyGlobals的类。至少他们是诚实的!一般来说,你是对的。像邮件这样的东西很适合变成注入的单例bean。
答案 3 :(得分:0)
春季邮件
首先,我想建议您查看org.springframework.mail package。它提供了一个有用的实用程序库,用于发送电子邮件,并用作抽象来隐藏底层邮件系统的细节。
由于你已经在使用spring,因此只要它提供了你的应用程序发送邮件所需的一切,就可以毫不费力地使用这个库。
静态方法与依赖注入
使用依赖注入使您可以轻松切换到其他实现。静态方法调用将您的消费者实现紧密绑定到消费者正在使用的服务。
测试使用者意味着您将集成测试使用者与服务,而不是单独测试您的消费者。这使得您始终依赖于这些静态方法的基础逻辑,并且由于其中一个静态方法中的错误,测试可能会失败。
答案 4 :(得分:0)
DI可以轻松模拟您的实现以编写测试。例如,假设您要测试密码重置流程。使用硬编码的Utils.sendMail(),您的测试代码将被强制制作模拟SMTP服务器,读取并解析电子邮件,然后单击密码重置链接。如果您使用过DI,则可以传递模拟Emailer
对象。这样,您就可以编写超快速的单元测试,而无需考虑外部集成。
您还可以轻松交换实施。例如,Google App Engine有一个自定义的sendMail API - 因此您很难同时支持GAE版本的代码和非GAE版本。 (为了论证,只是假设迁移到GAE很容易。显然不是。)
最后,您的代码更加模块化。一个特定的类(ResetPassword)可能只依赖于Util.sendMail(),但Util可能是一个厨房水槽,其方法可以在阳光下完成所有事情。因此,如果要在另一个项目中重用ResetPassword,则必须复制整个Utils类,以及Utils需要工作的几个相关jar。这不是一个好的选择。
答案 5 :(得分:0)
您希望避免依赖于状态的静态实用程序方法或具有您在某些情况下(通常是测试)可能由于任何原因而想要避免的外部影响。
在这种情况下,发送邮件可能取决于外部状态(邮件服务器可用性)并产生您可能希望避免的外部效果(发送电子邮件)。对于开发和测试,您通常不希望发送电子邮件。在其他情况下,您可能希望测试是否已发送电子邮件,但如果实际发送邮件,您如何执行此操作?设置一些复杂的系统来检查电子邮件的实际电子邮件收件箱?
如果您注入一个代表MailSender
的界面,您可以提供一个NoopMailSender
,当您不关心所发送的电子邮件时,该StubMailSender
不会执行任何操作,并且会收集一个List<EmailMessage>
假的当您希望能够测试代码发送的某些电子邮件时,通过它发送的{{1}}封电子邮件。