【问题标题】:What are Dependency Injection & Spring Framework about? [duplicate]什么是依赖注入和 Spring 框架? [复制]
【发布时间】:2010-12-31 02:42:16
【问题描述】:

可能的重复:
What is dependency injection?
What exactly is Spring for?

我想知道 什么是 Spring 框架?为什么以及何时应该在 Java 企业开发中使用它? 答案是“依赖注入框架”。 好吧,我们在使用依赖注入框架时有什么优势呢? 用 setter 值和/或构造函数参数描述类的想法对我来说似乎很奇怪。 为什么要这样做?因为我们可以在不重新编译项目的情况下更改属性?这就是我们的全部收获吗?

那么,我们应该在beans.xml 中描述哪些对象?所有对象还是只有少数?

欢迎最简单的答案

【问题讨论】:

  • 那么您要查找的是有关此事的文章,而不是 SO 答案。
  • 顺便说一句,书籍和文章是由在日常工作中使用它的人编写的。所以你可能错过了那里的一些东西。
  • 关于 DI 的原始文章:martinfowler.com/articles/injection.html - 如果您无法从中掌握概念,那么 SO 不会改善问题。
  • @EugeneP:不需要使用大写字母。但您似乎确实在特别询问 Spring 之前需要了解 DI 是什么。
  • @EugeneP: Petulance 也不会把你带到任何地方。 @Bozho 可能只在这里待了 55 天,但在那段时间里他获得了 7k 的代表,并且有权在他认为合适的时候结束问题。

标签: java spring dependency-injection


【解决方案1】:

我们使用依赖注入 (DI) 来实现松散耦合。任何特定的 DI 容器的选择并不那么重要。

每次使用 new 关键字创建类的实例时,您的代码都会与该类紧密耦合,并且您将无法用不同的实现替换该特定实现(至少在不重新编译的情况下)代码)。

这在 C# 中看起来像这样(但在 Java 中是等价的):

public class MyClass
{
    public string GetMessage(int key)
    {
        return new MessageService().GetMessage(key)
    }
}

这意味着如果您以后想使用不同的 MessageService,则不能。

另一方面,如果您将接口注入到类中并遵守Liskov Substition Principle,您将能够独立地改变消费者和服务。

public class MyClass
{
    private readonly IMessageService messageService;

    public MyClass(IMessageService messageService)
    {
        if(messageService == null)
        {
            throw new ArgumentNullException("messageService");
        }

        this.messageService = messageService;
    }

    public string GetMessage(int key)
    {
        return this.messageService.GetMessage(key)
    }
}

虽然这看起来更复杂,但我们现在已经设法遵循Single Responsibility Principle,确保每个协作者只做一件事,并且我们可以相互独立地改变两者。

此外,我们现在可以在不更改类本身的情况下更改 MyClass 的行为,从而遵守Open/Closed Principle

【讨论】:

    【解决方案2】:

    重新配置被高估了。使用 DI 获得的最重要的东西是可测试性。由于您的类不依赖于实现,而是依赖于抽象,因此您可以在单元测试中用模拟/存根替换它们。

    例子

    没有 DI:

    class SaleAction{
    
     private BillingService billingService;
    
     public SaleAction(){
       billingService = new CreditCardService(); //dependency is hardcoded
     }
    
     public void pay(long amount){
       //pre payment logic
       billingService.pay(amount);
       //post payment logic
     }
    
    }
    

    在该示例中,假设您想要对SaleAction 的预付款逻辑和后付款逻辑进行单元测试...您不能因为SaleActionCreditCardService 耦合,并且运行您的测试可能会生成虚假付款。

    现在与 DI 相同的示例:

     class SaleAction{
    
         private BillingService billingService;
    
         public SaleAction(BillingService service){
           billingService = service; //DI
         }
    
         public void pay(long amount){
           //pre payment logic
           billingService.pay(amount);
           //post payment logic
         }
    
        }
    

    现在SaleAction 与任何实现都解耦了,这意味着在您的测试中您可以执行SaleAction action = new SaleAction(new DummyBillingService());

    希望对您有所帮助,还有一篇关于 DI 的 文章,由 Martin Fowler 撰写,您可以找到 here

    【讨论】:

    • 实际上您是在说 DI 框架,例如Spring 被高估了,因为在没有框架的代码中用模拟替换依赖项是微不足道的。这些模拟的创建可能相当复杂,但我认为能够将它们换入和换出替代模拟没有任何优势。
    • 不依赖具体实现是 OOP 的核心,而不仅仅是 DI。
    • 我并不是说框架被高估了。 DI是单元测试的强制要求,而单元测试是软件开发的强制要求。
    • DI 不是单元测试的强制要求。
    • @Pablo - “软件开发必须进行单元测试” - 好吧,没有单元测试我就无法工作,但我认为即使是大多数软件也没有单元测试覆盖率。跨度>
    【解决方案3】:

    Spring 已经变成了一个庞大的框架,如果您只是想了解依赖注入,这可能会让您感到困惑。 Google Guice 项目很小,只使用纯 Java 进行 DI——没有 XML,也没有额外的东西。也有一个很好的介绍性视频来解释 DI。 http://code.google.com/p/google-guice/

    【讨论】:

    • @Ken Fox。不,不。我将使用 Spring。提到 DI 是因为它构成了 Spring Framework 的基础。我对 DI 不感兴趣,我主要对 Spring 功能感兴趣。但我需要知道基地里有什么。
    • Spring 一直是一个庞大的框架,远比 DI 多。从第一天开始,AOP 就和库一样收费。说 Spring 和 Guice 一对一的映射与对 Spring MVC 和 Struts 的映射一样不正确。 Spring 包含的功能等同于两者,甚至更多。
    • @Ken Fox。好吧,我还不太了解 AOP,我读过它处理“横切关注点”,例如几乎在类的每个方法中都会发生的日志记录。但是告诉我,好吧,有 AOP。但它不能替代 DI,是吗?
    • @EugeneP:不要选择框架然后看看它做了什么。有一个名为 YAGNI(You Ain't Gonna Need It)的 XP 原则一直对我很好。如果您有问题,请找到解决方案,如果这会将您带到像 Spring 这样的框架,至少您知道从哪里开始吃大象。
    • @Eugene - 我认为这里更大的问题是“阅读是什么”。我很难相信有人会声称自己读过任何主题的最佳书籍,然后来这里问这样一个基本的问题。你读了什么书?标题,请。
    【解决方案4】:

    Here's a good article 解释春天的概念。 (作者:Rod Johnson,Spring 框架的创始人)

    【讨论】:

      【解决方案5】:

      您在这里已经有了一些很好的答案,我想解决您的几个具体问题:

      用 setter 值和/或构造函数参数描述类的想法对我来说似乎很奇怪。为什么要这样做?因为我们可以在不重新编译项目的情况下更改属性?这就是我们的全部收获吗?

      起初看起来确实很奇怪,但关键是容器负责插入对象的依赖项,对象本身并不负责。 beans.xml中配置的对象的范围是由Spring管理的,所以我们不用太担心没有正确依赖的东西被实例化(只要配置正确,我就知道写单元测试检查弹簧配置是否在做我想要的。)

      那么,我们应该在 beans.xml 中描述哪些对象?所有对象还是只有几个?

      beans.xml 中描述的对象种类主要是组件——控制器、服务、数据访问对象以及它们需要的东西,如事务管理器、休眠会话工厂、数据源等。域对象通常从数据访问对象中检索或直接实例化,因为它们不依赖于除其他域对象(或比域类更独立的实用程序类)之外的任何东西。

      【讨论】:

        【解决方案6】:

        我想我可以解决这个问题,虽然我不确定任何答案都会令人满意。

        简单的答案是 Spring 是技术的组合:

        1. 依赖注入,它使用好莱坞原则来帮助您保持接口和实现分离;
        2. 面向方面的编程,将横切关注点隔离到可以声明式应用的模块中。 AOP 的“hello world”是日志记录,但它遍布 Spring(例如事务、用于远程处理的动态代理、安全性等)。想想 servlet 过滤器,你就会明白;
        3. 帮助处理常见任务的库,例如持久性(JDBC、Hibernate、iBatis、JDO、JPA 等)、远程处理(RMI、HTTP、Web 服务)、异步消息传递(消息驱动的 POJO)、验证和绑定、Web MVC (Spring 或 Struts)、电子邮件、日程安排、安全等实用程序。

        但更深层次的答案是,您将受益于 Rod Johnson 作为 Java EE 项目顾问的经验。他将在他的演出中对他有用的东西提炼到 Interface 21/Spring 中,现在您可以免费享受所有这些好处。

        Spring 团队编写的代码在设计、测试和遵循标准方面比我编写的任何代码都更加严格。 (想象一下,Juergen Hoeller 威胁 Rod Johnson,因为他的代码在签入之前不符合标准。)当我使用它并专注于我的业务问题时,我可以依靠他们的框架代码。我不会一次又一次地编写样板代码。

        对我来说,Spring 更多的是一种架构模板,可作为创建 Web 应用程序的指南。有些人可能会说它是过度设计的,对于某些问题他们是正确的,但对于我经常遇到的那种问题,Spring 只是门票。

        至于子问题:

        使用依赖注入框架有什么优势?

        对象不必负责管理它们的依赖关系。 Spring 的应用程序上下文实际上只是来自 GoF 的一个大工厂模式。它鼓励您设计一个接口,以便您可以根据需要更改实现。您的持久性接口今天可能使用 JDBC 实现,明天可能使用 Hibernate;您可能决定自动生成代理来管理其事务行为。 如果您对接口进行编码,则无需更改任何客户端代码。

        用 setter 值和/或构造函数参数描述类的想法对我来说似乎很奇怪。为什么要这样做?因为我们可以在不重新编译的情况下更改属性 项目?这就是我们的全部收获吗?

        奇怪?您不在代码中使用属性或构造函数?您这样做是因为这是大多数 Java 类的编写方式。 Spring 仅使用这些机制作为向类提供依赖项的一种方式。

        那么,我们应该在 beans.xml 中描述哪些对象?所有对象还是只有几个?

        仅具有依赖关系的 bean。对于特定方法的本地对象,我仍然称其为“新”。它们在方法范围内被创建、使用和垃圾收集。这些不需要在 Spring 的控制之下。

        【讨论】:

        • 我不能说你的答案涵盖了我所有的子问题,但阅读它仍然很有趣。 +1
        • 看看添加的是否更好。
        【解决方案7】:

        意见:找出您尝试使用 DI 解决的问题,并采用最适合的框架。 Spring 可能会增加您的问题的复杂性。

        first articles about DI by Martin Fowler 之一。我认为 DI 一词是在本文中创造的。)

        【讨论】:

          【解决方案8】:

          这是 Bob Lee 和他正在研究的两个关于 Guice 的good video (Guice 是一个类似 Spring 的依赖注入框架)。前 10 分钟左右是关于为什么 Guice(或依赖注入)会比替代方案(工厂)更好,所以不仅仅是 Guice。

          【讨论】:

            【解决方案9】:

            也许您不应该在不了解基本架构和设计问题的情况下尝试进行“Java 企业开发”?我建议你要么找一个愿意帮助你的有经验的同事,要么花更多的精力阅读那些书,或者按照其他方式学习。

            无论如何,您的问题没有“简单的答案”。

            【讨论】:

              【解决方案10】:

              依赖注入是缺少虚拟类的一种解决方法!

               

              注意:如果你不知道什么是虚拟课程,请参考"Virtual classes, anyone?"

              【讨论】:

              • C++ 多重虚拟继承的恐怖将永远伴随着我。我喜欢那篇文章,但你需要一个新名字。 DI 框架现在使用注释,因此它们与语言特性几乎无法区分——直到两个不同的框架发生冲突并打破这种错觉。
              猜你喜欢
              • 2011-06-08
              • 2013-09-27
              • 2011-05-16
              • 1970-01-01
              • 1970-01-01
              • 2021-01-07
              • 2014-09-06
              相关资源
              最近更新 更多