弹簧和接口

我读到了关于Spring如何鼓励你在代码中使用接口的地方。 我没有看到它。 spring xml配置中没有接口概念。 Spring的哪一部分实际上鼓励您使用接口(除了文档)?

为类定义接口时,它有助于dependency injection。 您的Spring配置文件中没有关于接口的任何内容 – 您只需输入类的名称即可。

但是如果你想注入另一个提供“等效”function的类,那么使用界面确实有帮助。

例如,说你有一个分析网站内容的课程,你用Spring注入它。 如果您注入的类知道实际的类是什么,那么为了更改它,您将不得不更改大量代码以使用不同的具体类。 但是如果你创建了一个Analyzer接口,你就可以轻松地注入原始的DefaultAnalyzer就像你可以DummyAnalyzer或者甚至是另一个基本相同的东西,比如PageByPageAnalyzer或其他东西。 为了使用其中的一个,你只需要更改你在Spring配置文件中注入的类名,而不是通过你的代码更改类。

在我真正开始看到它的用处之前,它花了我一个半个项目。 就像大多数事物(在企业语言中)最终有用一样,一开始似乎是毫无意义的工作,直到你的项目开始增长,然后你通过做更多的工作预先发现你节省了多少时间。

依赖性倒置原则很好地解释了这一点。 特别是图4。

A.高级模块不应该依赖于低级模块。 两者都应该取决于抽象。

B.抽象不应该依赖于细节。 细节应取决于抽象。

将上面链接中的示例翻译成java:

 public class Copy { private Keyboard keyboard = new Keyboard(); // concrete dependency private Printer printer = new Printer(); // concrete dependency public void copy() { for (int c = keyboard.read(); c != KeyBoard.EOF) { printer.print(c); } } } 

现在有依赖倒置:

 public class Copy { private Reader reader; // any dependency satisfying the reader interface will work private Writer writer; // any dependency satisfying the writer interface will work public void copy() { for (int c = reader.read(); c != Reader.EOF) { writer.write(c); } } public Copy(Reader reader, Writer writer) { this.reader = reader; this.writer = writer; } } 

现在Copy支持的不仅仅是从键盘复制到打印机。

它能够从任何Reader复制到任何Writer而无需对其代码进行任何修改。

现在有了Spring:

       

也许:

   

这里的大部分答案都是某种forms的“你可以轻松换掉实现”,但我认为他们没有回答的是为什么? 部分。 对此我认为答案几乎是可测试性。 无论您是否使用Spring或任何其他IOC框架,使用dependency injection都可以使您的代码更容易测试。 如果是编写器而不是PrinterWriter,您可以在unit testing中模拟Writer接口,并确保您的代码以您期望的方式调用它。 如果您直接依赖于类实现,您唯一的选择是走到打印机并检查它,这不是很自动化。 此外,如果你依赖于调用类的结果,不能模拟它可能会阻止你能够到达测试中的所有代码路径,从而降低它们的质量(可能)简单地说,你应该解耦对象从应用程序逻辑创建图形。 这样做可以使您的代码更容易测试。

没有人提到在很多情况下创建一个接口是不必要的,这样就可以快速切换实现类,因为只是不会有多个实现类。

在不需要创建接口的情况下,将通过对(接口和实现)创建类,添加不必要的样板接口并创建潜在的依赖混淆,因为在XML配置文件中,组件有时会被其接口引用,有时也会通过其实现引用,在运行时没有任何后果,但在代码约定方面却不连贯。

你可能想尝试自己使用它来更好地看到这一点,从文档中可能不清楚Spring如何鼓励界面使用。

以下是几个例子:

  1. 假设您正在编写一个需要从可以通过多种方式引用的资源(例如,文件)中读取的类(例如,在类路径,绝对文件路径中,作为URL等)。 您需要在类上定义org.springframework.core.io.Resource (接口)属性。 然后在Spring配置文件中,您只需选择实际的实现类(例如, org.springframework.core.io.ClassPathResourceorg.springframework.core.io.FileSystemResourceorg.springframework.core.io.UrlResource等)。 Spring基本上是一个非常通用的工厂。

  2. 如果你想利用Spring的AOP集成(例如添加事务拦截器),你几乎需要定义接口。 您可以在Spring配置文件中定义拦截点,Spring会根据您的界面为您生成代理。

这些是我个人经验丰富的例子。 我相信那里还有更多。

从接口生成代理很容易。

如果您查看任何spring应用程序,您将看到服务和持久性接口。 使spring成语确实鼓励使用界面。 它没有比那更明确。

如果您不使用接口,则可能会导致自动assembly失败 :有时Spring会为Bean创建一个Proxy类。 此Proxy类不是服务实现的子类,但它重新实现其所有接口。 Spring会尝试自动assembly这个Bean的实例,但是这个Proxy类与Bean类不兼容。 因此,使用Bean类声明一个字段可能会导致“不安全的字段分配”exception。

你不能合理地知道Spring什么时候开始代理服务(你也不应该),所以为了保护自己不受这些意外的影响,你最好的办法就是声明一个接口并在声明自动assembly字段时使用这个接口。

Spring不会强迫你在任何地方使用接口,这只是一个好习惯。 如果你有一个bean有一些属性是接口而不是具体的类,那么你可以简单地使用实现相同接口的模型切换出一些对象,这对某些测试用例很有用。

如果你使用例如Hibernate支持条款,你可以为你的DAO定义一个接口,然后单独实现它; 具有该接口的优点是您可以使用Spring拦截器对其进行配置,这将允许您简化代码; 您不必编写任何代码,使用HibernateExceptions并在finally段中关闭会话,也不必以编程方式定义任何事务,您只需使用Spring以声明方式配置所有这些内容。

当您编写快速且脏的应用程序时,您可以使用JDBC或一些简单的框架来实现一些简单的DAO,这些框架最终不会在最终版本中使用; 如果它们实现了一些通用接口,您将能够轻松地切换这些组件。

编写单独的接口会增加通常不必要的复杂性和样板代码。 它还使调试变得更难,因为当您在IDE中单击方法调用时,它会显示接口而不是实现。 除非您在运行时交换实现,否则无需沿着这条路走下去。

像Mockito这样的工具可以很容易地使用dependency injection来测试代码,而不会在接口上堆积。