Java依赖注入 - DI设计模式示例教程

Java依赖注入 设计模式允许我们删除硬编码的依赖项,并使我们的应用程序松散耦合,可扩展和可维护。我们可以在java中实现依赖注入,以将依赖项解析从编译时移动到运行时。

目录[ 隐藏 ]

  • 1 Java依赖注入

    • 1.1 Java依赖注入 - 服务组件
    • 1.2 Java依赖注入 - 服务消费者
    • 1.3 Java依赖注入 - 注入器类
    • 1.4 Java依赖注入 - 使用模拟注入器和服务的JUnit测试用例
    • 1.5 Java依赖注入的好处
    • 1.6 Java依赖注入的缺点

Java依赖注入


Java依赖注入似乎很难用理论来理解,所以我将举一个简单的例子然后我们将看到如何使用依赖注入模式来实现应用程序中的松散耦合和可扩展性。

假设我们有一个EmailService用于发送电子邮件的应用程序。通常我们会像下面这样实现它。


package com.journaldev.java.legacy;public class EmailService {public void sendEmail(String message, String receiver){//logic to send emailSystem.out.println("Email sent to "+receiver+ " with Message="+message);}
}

EmailServiceclass拥有将电子邮件消息发送到收件人电子邮件地址的逻辑。我们的应用程序代码如下所示。


package com.journaldev.java.legacy;public class MyApplication {private EmailService email = new EmailService();public void processMessages(String msg, String rec){//do some msg validation, manipulation logic etcthis.email.sendEmail(msg, rec);}
}

我们将使用MyApplication类发送电子邮件的客户端代码如下所示。


package com.journaldev.java.legacy;public class MyLegacyTest {public static void main(String[] args) {MyApplication app = new MyApplication();app.processMessages("Hi Pankaj", "pankaj@abc.com");}}

初看起来,上面的实现似乎没有错。但上面的代码逻辑有一定的局限性。

  • MyApplicationclass负责初始化电子邮件服务然后使用它。这导致了硬编码的依赖性。如果我们希望将来切换到其他一些高级电子邮件服务,则需要在MyApplication类中进行代码更改。这使我们的应用程序难以扩展,如果在多个类中使用电子邮件服务,那将更加困难。
  • 如果我们想扩展我们的应用程序以提供其他消息传递功能,例如SMS或Facebook消息,那么我们需要为此编写另一个应用程序。这将涉及应用程序类和客户端类中的代码更改。
  • 由于我们的应用程序直接创建电子邮件服务实例,因此测试应用程序将非常困难。我们无法在测试类中模拟这些对象。

可以说我们可以MyApplication通过使用需要电子邮件服务作为参数的构造函数来从类中删除电子邮件服务实例创建。


package com.journaldev.java.legacy;public class MyApplication {private EmailService email = null;public MyApplication(EmailService svc){this.email=svc;}public void processMessages(String msg, String rec){//do some msg validation, manipulation logic etcthis.email.sendEmail(msg, rec);}
}

但在这种情况下,我们要求客户端应用程序或测试类初始化不是一个好的设计决策的电子邮件服务。

现在让我们看看我们如何应用java依赖注入模式来解决上述实现的所有问题。java中的依赖注入至少需要以下内容:

  1. 服务组件应设计为基类或接口。最好选择定义服务契约的接口或抽象类。
  2. 消费者类应该根据服务接口编写。
  3. 注入器类将初始化服务,然后是消费者类。

Java依赖注入 - 服务组件

对于我们的情况,我们可以MessageService声明服务实现的合同。


package com.journaldev.java.dependencyinjection.service;public interface MessageService {void sendMessage(String msg, String rec);
}

现在让我们说我们有实现上述接口的电子邮件和短信服务。


package com.journaldev.java.dependencyinjection.service;public class EmailServiceImpl implements MessageService {@Overridepublic void sendMessage(String msg, String rec) {//logic to send emailSystem.out.println("Email sent to "+rec+ " with Message="+msg);}}

package com.journaldev.java.dependencyinjection.service;public class SMSServiceImpl implements MessageService {@Overridepublic void sendMessage(String msg, String rec) {//logic to send SMSSystem.out.println("SMS sent to "+rec+ " with Message="+msg);}}

我们的依赖注入java服务已准备就绪,现在我们可以编写我们的消费者类。

Java依赖注入 - 服务使用者

我们不需要为消费者类提供基本接口,但我将有一个Consumer接口声明消费者类的合同。


package com.journaldev.java.dependencyinjection.consumer;public interface Consumer {void processMessages(String msg, String rec);
}

我的消费者类实现如下。


package com.journaldev.java.dependencyinjection.consumer;import com.journaldev.java.dependencyinjection.service.MessageService;public class MyDIApplication implements Consumer{private MessageService service;public MyDIApplication(MessageService svc){this.service=svc;}@Overridepublic void processMessages(String msg, String rec){//do some msg validation, manipulation logic etcthis.service.sendMessage(msg, rec);}}

请注意,我们的应用程序类只是使用该服务。它没有初始化导致更好的“ 关注点分离 ”的服务。使用服务接口还允许我们通过模拟MessageService轻松测试应用程序,并在运行时而不是编译时绑定服务。

现在我们准备编写初始化服务和消费者类的java依赖注入器类。

Java依赖注入 - 注入器类

让我们有一个MessageServiceInjector返回Consumer类的方法声明接口。


package com.journaldev.java.dependencyinjection.injector;import com.journaldev.java.dependencyinjection.consumer.Consumer;public interface MessageServiceInjector {public Consumer getConsumer();
}

现在,对于每项服务,我们都必须创建如下所示的注入类。


package com.journaldev.java.dependencyinjection.injector;import com.journaldev.java.dependencyinjection.consumer.Consumer;
import com.journaldev.java.dependencyinjection.consumer.MyDIApplication;
import com.journaldev.java.dependencyinjection.service.EmailServiceImpl;public class EmailServiceInjector implements MessageServiceInjector {@Overridepublic Consumer getConsumer() {return new MyDIApplication(new EmailServiceImpl());}}

package com.journaldev.java.dependencyinjection.injector;import com.journaldev.java.dependencyinjection.consumer.Consumer;
import com.journaldev.java.dependencyinjection.consumer.MyDIApplication;
import com.journaldev.java.dependencyinjection.service.SMSServiceImpl;public class SMSServiceInjector implements MessageServiceInjector {@Overridepublic Consumer getConsumer() {return new MyDIApplication(new SMSServiceImpl());}}

现在让我们看看我们的客户端应用程序如何通过一个简单的程序使用该应用程序。


package com.journaldev.java.dependencyinjection.test;import com.journaldev.java.dependencyinjection.consumer.Consumer;
import com.journaldev.java.dependencyinjection.injector.EmailServiceInjector;
import com.journaldev.java.dependencyinjection.injector.MessageServiceInjector;
import com.journaldev.java.dependencyinjection.injector.SMSServiceInjector;public class MyMessageDITest {public static void main(String[] args) {String msg = "Hi Pankaj";String email = "pankaj@abc.com";String phone = "4088888888";MessageServiceInjector injector = null;Consumer app = null;//Send emailinjector = new EmailServiceInjector();app = injector.getConsumer();app.processMessages(msg, email);//Send SMSinjector = new SMSServiceInjector();app = injector.getConsumer();app.processMessages(msg, phone);}}

如您所见,我们的应用程序类仅负责使用该服务。服务类在注入器中创建。此外,如果我们必须进一步扩展我们的应用程序以允许Facebook消息传递,我们将只需编写服务类和注入类。

因此,依赖注入实现解决了硬编码依赖的问题,并帮助我们使应用程序灵活且易于扩展。现在让我们看看我们可以通过模拟注入器和服务类来轻松测试我们的应用程序类。

Java依赖注入 - 使用模拟注入器和服务的JUnit测试用例


package com.journaldev.java.dependencyinjection.test;import org.junit.After;
import org.junit.Before;
import org.junit.Test;import com.journaldev.java.dependencyinjection.consumer.Consumer;
import com.journaldev.java.dependencyinjection.consumer.MyDIApplication;
import com.journaldev.java.dependencyinjection.injector.MessageServiceInjector;
import com.journaldev.java.dependencyinjection.service.MessageService;public class MyDIApplicationJUnitTest {private MessageServiceInjector injector;@Beforepublic void setUp(){//mock the injector with anonymous classinjector = new MessageServiceInjector() {@Overridepublic Consumer getConsumer() {//mock the message servicereturn new MyDIApplication(new MessageService() {@Overridepublic void sendMessage(String msg, String rec) {System.out.println("Mock Message Service implementation");}});}};}@Testpublic void test() {Consumer consumer = injector.getConsumer();consumer.processMessages("Hi Pankaj", "pankaj@abc.com");}@Afterpublic void tear(){injector = null;}}

正如您所看到的,我使用匿名类来模拟注入器和服务类,我可以轻松地测试我的应用程序方法。我在上面的测试类中使用JUnit 4,因此如果你在测试类之上运行,请确保它在你的项目构建路径中。

我们使用构造函数在应用程序类中注入依赖项,另一种方法是使用setter方法在应用程序类中注入依赖项。对于setter方法依赖注入,我们的应用程序类将如下所示实现。


package com.journaldev.java.dependencyinjection.consumer;import com.journaldev.java.dependencyinjection.service.MessageService;public class MyDIApplication implements Consumer{private MessageService service;public MyDIApplication(){}//setter dependency injection  public void setService(MessageService service) {this.service = service;}@Overridepublic void processMessages(String msg, String rec){//do some msg validation, manipulation logic etcthis.service.sendMessage(msg, rec);}}

package com.journaldev.java.dependencyinjection.injector;import com.journaldev.java.dependencyinjection.consumer.Consumer;
import com.journaldev.java.dependencyinjection.consumer.MyDIApplication;
import com.journaldev.java.dependencyinjection.service.EmailServiceImpl;public class EmailServiceInjector implements MessageServiceInjector {@Overridepublic Consumer getConsumer() {MyDIApplication app = new MyDIApplication();app.setService(new EmailServiceImpl());return app;}}

setter依赖注入的最好例子之一是Struts2 Servlet API Aware接口。

是否使用基于构造函数的依赖注入或基于setter是一个设计决策,取决于您的要求。例如,如果我的应用程序在没有服务类的情况下根本无法工作,那么我会更喜欢基于构造函数的DI,否则我会选择基于setter方法的DI来仅在真正需要时使用它。

Java中的依赖注入是一种通过将对象从编译时绑定到运行时来实现应用程序中的控制反转IoC)的方法。我们也可以通过工厂模式,模板方法设计模式,策略模式和服务定位模式来实现IoC 。

Spring依赖注入 Google Guice Java EE CDI框架通过使用 Java Reflection API和 Java注释来促进依赖注入的过程。我们所需要的只是注释字段,构造函数或setter方法,并在配置xml文件或类中配置它们。

Java依赖注入的好处

在Java中使用依赖注入的一些好处是:

  • 关注点分离
  • Boilerplate应用程序类中的代码减少,因为初始化依赖项的所有工作都由注入器组件处理
  • 可配置组件使应用程序易于扩展
  • 使用模拟对象可以轻松进行单元测试

Java依赖注入的缺点

Java依赖注入也有一些缺点:

  • 如果过度使用,则可能导致维护问题,因为更改的影响在运行时已知。
  • java中的依赖注入隐藏了可能导致在编译时捕获的运行时错误的服务类依赖项。

下载依赖注入项目

这就是java中依赖注入模式的全部内容。当我们控制服务时,了解并使用它是很好的。

原创翻译 原文:https://www.journaldev.com/2394/java-dependency-injection-design-pattern-example-tutorial

Java依赖注入 - DI设计模式示例教程相关推荐

  1. 依赖注入(di)模式_Java依赖注入– DI设计模式示例教程

    依赖注入(di)模式 Java Dependency Injection design pattern allows us to remove the hard-coded dependencies ...

  2. Java依赖注入(DI)实例详解

    Java依赖注入模式允许我们摆脱硬编码,使我们的应用更加松耦合.增强扩展性以及可维护性.通过依赖注入我们可以降低从编译到运行时的依赖性. Java依赖注入 Java的依赖注入仅仅通过理论是很难解明白的 ...

  3. Java中的观察者设计模式-示例教程

    观察者模式是行为设计模式之一 . 当您对对象的状态感兴趣并希望在发生任何更改时得到通知时,观察者设计模式很有用. 在观察者模式中,监视另一个对象状态的对象称为Observer ,而正在监视的对象称为S ...

  4. Java中的策略设计模式-示例教程

    策略模式是行为设计模式之一 . 当我们对一个特定任务有多种算法,并且客户端决定在运行时使用的实际实现时,将使用策略模式. 策略模式也称为策略模式 . 我们定义了多种算法,并让客户端应用程序将算法用作参 ...

  5. Java中的状态设计模式–示例教程

    状态模式是行为设计模式之一 . 当对象根据其内部状态更改其行为时,将使用状态设计模式. 如果必须根据对象的状态更改其行为,则可以在对象中使用状态变量,并使用if-else条件块根据状态执行不同的操作. ...

  6. Java中的Memento设计模式-示例教程

    记忆模式是行为设计模式之一 . 当我们要保存对象的状态以便以后可以恢复时,可以使用Memento设计模式. 记忆模式用于以这种方式实现该目的,即在对象外部无法访问该对象的已保存状态数据,从而保护了已保 ...

  7. Java中的访问者设计模式–示例教程

    访客模式是行为设计模式之一 . 当我们必须对一组相似类型的对象执行操作时,将使用访问者模式. 借助访问者模式,我们可以将操作逻辑从对象移动到另一个类. 例如,假设有一个购物车,我们可以在其中添加不同类 ...

  8. Java中的迭代器设计模式–示例教程

    迭代器模式是一种行为模式,用于提供遍历一组对象的标准方式. Iterator模式在Java Collection Framework中得到了广泛使用,其中Iterator接口提供了遍历集合的方法. 根 ...

  9. java ee cdi_Java EE CDI依赖注入(@Inject)教程

    java ee cdi 在本教程中,我们将向您展示如何在CDI管理的Bean中实现依赖注入. 特别是,我们将利用CDI API提供的@Inject批注将CDI bean注入另一个bean. 通过这种方 ...

最新文章

  1. python使用openCV把原始彩色图像转化为灰度图、使用矩阵索引的方式对数据数据进行剪裁(image cropping)
  2. JBPM深入解析之变量设计
  3. 阿里钉钉,马云旗下的又一个千亿美金产品?
  4. [导入]相片: 23853741.jpg
  5. 【性能测试】基本入门(包含mysql锁)
  6. 默认权限umask、文件系统权限、特殊权限
  7. Spring Swagger URL传参问题(转)
  8. 英语总结系列(十八):六月一波三折的英语
  9. 韦东山linux学习之ubuntu 9.10 软件源 问题
  10. ubuntu14.04/Mint17上Gitolite的搭建过程
  11. Java常量不能二次赋值与常量不能修改值意思一样吗?
  12. Material Design 控件知识梳理(4) FloatingActionButton
  13. 多元统计分析基于r课后答案_(完整版)多元统计分析课后练习答案
  14. spss常态检验_科学网—如何在SPSS中进行正态分布检验?
  15. 用python编写一个弹球游戏
  16. mysql数据可视化 1
  17. GB28181设备端PS流封装和发送
  18. 操作系统-程序执行时间分析
  19. css中文字段落对齐,CSS段落对齐方式
  20. 通用搜索引擎的垂直化倾向

热门文章

  1. jquery mobile : 外链接失效 a href not working
  2. jQuery hash 插件
  3. click() bind() live() delegate()区别
  4. linux压缩和解压缩命令汇总
  5. Java—多线程创建详解
  6. 事务的隔离级别 mysql
  7. 学习日报 1026 使用属性升级MyBank
  8. 安装虚拟环境virtualenv 适用于windows操作系统
  9. linux-查看用户id-查看文件目录所有者-查看进程操作者
  10. ACM团队周赛题解(2)