2008-11-01 130 views
19

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

+1

除了关于DI提出的观点之外,像Spring remoting这样的东西完全依赖于使用接口。 – Robin 2008-11-03 14:44:11

+0

这是一个非常好的问题。正如你从目前为止的答案中可以看到的,没有人给出具体的答案。 – 2012-06-06 15:53:34

回答

25

当你定义为你的类的接口,它有助于依赖注入。你的Spring配置文件本身没有关于接口的任何东西 - 你只需要输入类的名字。

但是,如果你想注入另一个提供“等效”功能的类,使用接口确实有帮助。

例如,说你有一个分析网站内容的类,并且你正在使用Spring进行注入。如果你注入的类知道实际的类是什么,那么为了改变它,你将不得不改变很多代码来使用不同的具体类。但是如果你创建了一个Analyzer接口,你可以轻松地注入你的原始DefaultAnalyzer,就像你可以嘲笑DummyAnalyzer甚至是另一个基本上做同样事情的东西,比如PageByPageAnalyzer或其他任何东西。为了使用其中的一个,你只需要改变你在Spring配置文件中注入的类名,而不是遍历你的代码改变类。

在我真的开始看到它的用处之前,我花了一个半天的时间。就像大多数(在企业语言中)最终变得有用的东西一样,在开始增长项目之前,它似乎毫无意义地增加了一些工作,然后通过多做一些工作,发现您节省了多少时间。

+6

虽然你说的是正确的,但它不是以春天为中心的。它是以Java为中心的。 – 2012-06-06 15:52:30

2

您可能想要尝试使用它来更好地看到这一点,从文档中可能不清楚Spring是如何鼓励使用接口的。

这里有几个例子:

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

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

这些都是我个人有经验的例子。我相信还有更多。

31

Dependency Inversion Principle解释得很好。特别是,图4.

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

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而无需对其代码进行任何修改。

现在同春:

<bean id="copy" class="Copy"> 
    <constructor-arg ref="reader" /> 
    <constructor-arg ref="writer" /> 
</bean> 

<bean id="reader" class="KeyboardReader" /> 
<bean id="writer" class="PrinterWriter" /> 

或者是:

<bean id="reader" class="RemoteDeviceReader" /> 
<bean id="writer" class="DatabaseWriter" /> 
9

这里的大多数答案都是某种形式的“你可以轻松地换出实现”,但我认为他们未能回答的原因是为什么?部分。对此,我认为答案几乎是可测试性。无论您是否使用Spring或任何其他IOC框架,使用依赖注入都会使您的代码更易于测试。在说作者而不是PrinterWriter的情况下,您可以在单元测试中模拟Writer接口,并确保您的代码按照您期望的方式调用它。如果您直接依赖于课程实施,唯一的选择是走到打印机并检查它,这不是非常自动的。此外,如果您依赖于对类的调用结果,无法模拟它可能会阻止您在测试中访问所有代码路径,从而降低其质量(可能)简单地说,您应该将对象从应用程序逻辑创建图表。这样做使您的代码更易于测试。

0

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

如果您使用例如Hibernate支持类,您可以为您的DAO定义一个接口,然后单独实现它;拥有接口的好处是你可以使用Spring拦截器来配置它,这将允许你简化你的代码;您不必编写任何代码来引用HibernateExceptions并在最后一段中关闭会话,而且您也不必通过编程来定义任何事务,只需使用Spring声明性地配置所有这些东西即可。

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

1

很容易从接口生成代理。

如果您查看任何Spring应用程序,您会看到服务和持久性界面。使得春天的成语确实鼓励使用界面。它没有比这更明确。

1

没有人提到过,在很多情况下不需要创建一个接口,以便实现类可以快速切换,因为不会有一个以上的实现类。

当不需要创建接口时,类将由对(接口和实现)创建,添加不必要的样板接口并创建潜在的依赖关系混淆,因为在XML配置文件中,组件有时会被其接口引用,它的实现,在运行时没有任何影响,但在代码约定方面却不一致。

1

如果您不使用接口,将会导致自动装配失败: 有时Spring会为Bean创建Proxy类。 此代理类不是服务实现的子类,但它重新实现了它的所有接口。 Spring将尝试自动调用此Bean的实例,但是此Proxy类与Bean类不兼容。所以用Bean类声明一个字段会导致“不安全的字段分配”异常。你不能合理地知道Spring何时要代理一个服务(也不应该),所以为了保护自己免受这些意外,你最好的办法是声明一个接口并在声明自动装配字段时使用这个接口。