Java 弹簧和接口
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/256255/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
spring and interfaces
提问by lowellk
I read all over the place about how Spring encourages you to use interfaces in your code. I don't see it. There is no notion of interface in your spring xml configuration. What part of Spring actually encourages you to use interfaces (other than the docs)?
我读了很多关于 Spring 如何鼓励您在代码中使用接口的内容。我没看到。您的 spring xml 配置中没有接口的概念。Spring 的哪一部分实际上鼓励您使用接口(文档除外)?
采纳答案by Sean Schulte
When you define an interface for your classes, it helps with dependency injection. Your Spring configuration files don't have anything about interfaces in them themselves -- you just put in the name of the class.
当你为你的类定义一个接口时,它有助于依赖注入。您的 Spring 配置文件本身没有关于接口的任何内容——您只需输入类的名称。
But if you want to inject another class that offers "equivalent" functionality, using an interface really helps.
但是如果你想注入另一个提供“等效”功能的类,使用接口真的很有帮助。
For example, saying you've got a class that analyzes a website's content, and you're injecting it with Spring. If the classes you're injecting it into know what the actual class is, then in order to change it out you'll have to change a whole lot of code to use a different concrete class. But if you created an Analyzer
interface, you could just as easily inject your original DefaultAnalyzer
as you could a mocked up DummyAnalyzer
or even another one that does essentially the same thing, like a PageByPageAnalyzer
or anything else. In order to use one of those, you just have to change the classname you're injecting in your Spring config files, rather than go through your code changing classes around.
例如,假设您有一个分析网站内容的类,并且您正在向它注入 Spring。如果您注入它的类知道实际的类是什么,那么为了改变它,您必须更改大量代码以使用不同的具体类。但是如果你创建了一个Analyzer
界面,你可以像注入一个模型一样轻松地注入你的原件DefaultAnalyzer
,DummyAnalyzer
甚至可以像另一个做本质上相同的事情的人一样,比如 aPageByPageAnalyzer
或其他任何东西。为了使用其中之一,您只需要更改您在 Spring 配置文件中注入的类名,而不是通过代码更改类。
It took me about a project and a half before I really started to see the usefulness. Like most things (in enterprise languages) that end up being useful, it seems like a pointless addition of work at first, until your project starts to grow and then you discover how much time you saved by doing a little bit more work up front.
在我真正开始看到有用之前,我花了大约一个半的时间。就像大多数最终有用的东西(在企业语言中)一样,起初似乎是毫无意义的增加工作,直到您的项目开始增长,然后您发现通过预先做更多的工作节省了多少时间。
回答by Hyman Leow
You may probably want to try using it for yourself to be better able to see this, it may not be clear from the docs how Spring encourages interface use.
您可能想自己尝试使用它以便更好地看到这一点,从文档中可能不清楚 Spring 如何鼓励界面使用。
Here are a couple of examples:
下面是几个例子:
Say you're writing a class that needs to read from a resource (e.g., file) that may be referenced in several ways (e.g., in classpath, absolute file path, as a URL etc). You'd want to define a
org.springframework.core.io.Resource
(interface) property on your class. Then in your Spring configuration file, you simply select the actual implementation class (e.g.,org.springframework.core.io.ClassPathResource
,org.springframework.core.io.FileSystemResource
,org.springframework.core.io.UrlResource
etc). Spring is basically functioning as an extremely generic factory.If you want to take advantage of Spring's AOP integration (for adding transaction interceptors for instance), you'll pretty much need to define interfaces. You define the interception points in your Spring configuration file, and Spring generates a proxy for you, based on your interface.
假设您正在编写一个类,该类需要从可能以多种方式(例如,在类路径、绝对文件路径、作为 URL 等)中引用的资源(例如,文件)读取。你想
org.springframework.core.io.Resource
在你的类上定义一个(接口)属性。然后在您的 Spring 配置文件中,您只需选择实际的实现类(例如org.springframework.core.io.ClassPathResource
,org.springframework.core.io.FileSystemResource
、org.springframework.core.io.UrlResource
等)。Spring 基本上是一个非常通用的工厂。如果您想利用 Spring 的 AOP 集成(例如添加事务拦截器),您几乎需要定义接口。您在 Spring 配置文件中定义拦截点,Spring 会根据您的接口为您生成代理。
These are examples I personally have experience with. I'm sure there are much more out there.
这些都是我亲身经历过的例子。我敢肯定还有更多。
回答by toolkit
The Dependency Inversion Principleexplains this well. In particular, figure 4.
该依赖倒置原则解释了这一点。特别是图 4。
A. High level modules should not depend on low level modules. Both should depend upon abstractions.
B. Abstraction should not depend upon details. Details should depend upon abstractions.
A. 高级模块不应依赖于低级模块。两者都应该依赖于抽象。
B. 抽象不应该依赖于细节。细节应该取决于抽象。
Translating the examples from the link above into java:
将上面链接中的示例翻译成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);
}
}
}
Now with dependency inversion:
现在有了依赖倒置:
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;
}
}
Now Copy
supports more than just copying from a keyboard to a printer.
现在Copy
支持的不仅仅是从键盘复制到打印机。
It is capable of copying from any Reader
to any Writer
without requiring any modifications to its code.
它能够从任何复制Reader
到任何,Writer
而无需对其代码进行任何修改。
And now with Spring:
现在有了 Spring:
<bean id="copy" class="Copy">
<constructor-arg ref="reader" />
<constructor-arg ref="writer" />
</bean>
<bean id="reader" class="KeyboardReader" />
<bean id="writer" class="PrinterWriter" />
or perhaps:
也许:
<bean id="reader" class="RemoteDeviceReader" />
<bean id="writer" class="DatabaseWriter" />
回答by Sean Schulte
Most of the answers here are some form of "You can easily swap out implementations", but what I think they fail to answer is the why? part. To that I think the answer is almost definitively testability. Regardless of whether or not you use Spring or any other IOC framework, using Dependency Injection makes your code easier to test. In the case of say a writer rather than a PrinterWriter, you can Mock the Writer interface in a Unit test, and ensure that your code is calling it the way you expect it to. If you depend directly on the class implementation, your only option is to walk to the printer and check it, which isn't very automated. Furthermore, if you depend upon the result of a call to a class, not being able to Mock it may prevent you from being able to reach all code paths in your test, thus reducing their quality (potentially) Simply put, you should decouple Object graph creation from application logic. Doing so makes your code easier to test.
这里的大多数答案都是某种形式的“您可以轻松更换实现”,但我认为他们未能回答的是为什么?部分。对此,我认为答案几乎是可测试性。无论您是否使用 Spring 或任何其他 IOC 框架,使用依赖注入都会使您的代码更易于测试。如果是 writer 而不是 PrinterWriter,您可以在单元测试中模拟 Writer 接口,并确保您的代码以您期望的方式调用它。如果您直接依赖于类实现,您唯一的选择就是走到打印机前检查它,这不是很自动化。此外,如果您依赖于对类的调用结果,则无法模拟它可能会阻止您访问测试中的所有代码路径,从而降低它们的质量(可能) 简而言之,您应该将对象图创建与应用程序逻辑分离。这样做会使您的代码更易于测试。
回答by Chochos
Spring won't force you to use interfaces anywhere, it's just good practice. If you have a bean that has a some properties that are interfaces instead of concrete classes, then you can simply switch out some objects with mockups that implement the same interface, which is useful for certain test cases.
Spring 不会强迫您在任何地方使用接口,这只是一种很好的做法。如果你有一个 bean 有一些属性是接口而不是具体类,那么你可以简单地用实现相同接口的模型来切换一些对象,这对某些测试用例很有用。
If you use for example the Hibernate support clases, you can define an interface for your DAO, then implement it separately; the advantage of having the interface is that you will be able to configure it using the Spring interceptors, which will allow you to simplify your code; you won't have to write any code cathing HibernateExceptions and closing the session in a finally segment, and you won't have to define any transactions programmatically either, you just configure all that stuff declaratively with Spring.
例如,如果您使用 Hibernate 支持类,您可以为您的 DAO 定义一个接口,然后单独实现它;拥有该接口的优点是您将能够使用 Spring 拦截器对其进行配置,这将允许您简化代码;您不必编写任何包含 HibernateExceptions 并在 finally 段中关闭会话的代码,也不必以编程方式定义任何事务,只需使用 Spring 声明性地配置所有这些内容。
When you're writing quick and dirty apps, you can implement some simple DAO using JDBC or some simple framework which you won't end up using in the final version; you will be able to easily switch those components out if they implement some common interfaces.
当您编写快速而肮脏的应用程序时,您可以使用 JDBC 或一些您最终不会在最终版本中使用的简单框架来实现一些简单的 DAO;如果这些组件实现了一些通用接口,您将能够轻松地将它们切换出来。
回答by duffymo
it's easy to generate proxies from interfaces.
从接口生成代理很容易。
if you look at any spring app, you'll see service and persistence interfaces. making that the spring idiom certainly does encourage the use of interfaces. it doesn't get any more explicit than that.
如果您查看任何 spring 应用程序,您会看到服务和持久性接口。使得 spring 习语确实鼓励使用接口。没有比这更明确的了。
回答by AxeEffect
No one has mention yet that in many occasions won't be necessary to create an interface so that the implementing class can be switched quickly because simply there won't be more than one implementing class.
还没有人提到在很多情况下不需要创建接口以便实现类可以快速切换,因为不会有多个实现类。
When interfaces are created without need, classes will be created by pairs (interface plus implementation), adding unnecessary boilerplate interfaces and creating potential dependency confusions because, on XML configuration files, components will be sometimes referenced by its interface and sometimes by its implementation, with no consequences at runtime but being incoherent regarding code conventions.
当不需要创建接口时,类将成对创建(接口加实现),添加不必要的样板接口并造成潜在的依赖混淆,因为在 XML 配置文件中,组件有时会被其接口引用,有时会被其实现引用,在运行时没有任何后果,但在代码约定方面不连贯。
回答by Thim Anneessens
If you don't use interfaces you risk an autowiring failure: Sometime Spring creates a Proxy class for a Bean. This Proxy class is not a child class of the service implementationbut it re-implements all of its interfaces. Spring will try to autowire instances of this Bean, however this Proxy class is incompatible with the Bean class. So declaring a field with Bean class can lead to "unsafe field assignement" exceptions.
如果您不使用接口,您可能会面临自动装配失败的风险:有时 Spring 会为 Bean 创建一个 Proxy 类。这个 Proxy 类不是服务实现的子类,但它重新实现了它的所有接口。Spring 将尝试自动装配此 Bean 的实例,但是此 Proxy 类与 Bean 类不兼容。所以用 Bean 类声明一个字段会导致“不安全的字段分配”异常。
You cannot reasonably know when Spring is going to Proxy a service (nor should you), so to protect yourself against those surprises, your best move is to declare an interface and use this interface when declaring autowired fields.
您无法合理地知道 Spring 何时将代理服务(您也不应该),因此为了保护自己免受这些意外的影响,您最好的做法是声明一个接口并在声明自动装配字段时使用该接口。
回答by mrog
Writing separate interfaces adds complexity and boilerplate code that's normally unnecessary. It also makes debugging harder because when you click a method call in your IDE, it shows the interface instead of the implementation. Unless you're swapping implementations at runtime, there's no need to go down that path.
编写单独的接口会增加通常不必要的复杂性和样板代码。它还使调试更加困难,因为当您在 IDE 中单击方法调用时,它显示的是界面而不是实现。除非您在运行时交换实现,否则没有必要沿着这条路走下去。
Tools like Mockito make it very easy to test code using dependency injection without piling on interfaces.
像 Mockito 这样的工具可以很容易地使用依赖注入测试代码,而无需在接口上堆积。