依赖注入(di)模式

Java Dependency Injection design pattern allows us to remove the hard-coded dependencies and make our application loosely coupled, extendable and maintainable. We can implement dependency injection in java to move the dependency resolution from compile-time to runtime.

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

Java依赖注入 (Java Dependency Injection)

Java Dependency injection seems hard to grasp with theory, so I would take a simple example and then we will see how to use dependency injection pattern to achieve loose coupling and extendability in the application.

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

Let’s say we have an application where we consume EmailService to send emails. Normally we would implement this like below.

假设我们有一个应用程序,我们在其中使用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);}
}

EmailService class holds the logic to send an email message to the recipient email address. Our application code will be like below.

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

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);}
}

Our client code that will use MyApplication class to send email messages will be like below.

我们的客户端代码将使用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");}}

At first look, there seems nothing wrong with the above implementation. But above code logic has certain limitations.

乍一看,上述实现似乎没有错。 但是以上代码逻辑有一定的局限性。

  • MyApplication class is responsible to initialize the email service and then use it. This leads to hard-coded dependency. If we want to switch to some other advanced email service in the future, it will require code changes in MyApplication class. This makes our application hard to extend and if email service is used in multiple classes then that would be even harder.MyApplication类负责初始化电子邮件服务,然后使用它。 这导致硬编码的依赖性。 如果将来想切换到其他高级电子邮件服务,则需要在MyApplication类中更改代码。 这使我们的应用程序难以扩展,如果在多个类中使用电子邮件服务,则将更加困难。
  • If we want to extend our application to provide an additional messaging feature, such as SMS or Facebook message then we would need to write another application for that. This will involve code changes in application classes and in client classes too.如果我们想扩展应用程序以提供其他消息传递功能,例如SMS或Facebook消息,那么我们需要为此编写另一个应用程序。 这将涉及应用程序类和客户端类中的代码更改。
  • Testing the application will be very difficult since our application is directly creating the email service instance. There is no way we can mock these objects in our test classes.由于我们的应用程序是直接创建电子邮件服务实例,因此测试该应用程序将非常困难。 我们无法在测试类中模拟这些对象。

One can argue that we can remove the email service instance creation from MyApplication class by having a constructor that requires email service as an argument.

可以说,我们可以通过使用需要电子邮件服务作为参数的构造函数来从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);}
}

But in this case, we are asking client applications or test classes to initializing the email service that is not a good design decision.

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

Now let’s see how we can apply java dependency injection pattern to solve all the problems with the above implementation. Dependency Injection in java requires at least the following:

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

  1. Service components should be designed with base class or interface. It’s better to prefer interfaces or abstract classes that would define contract for the services.服务组件应使用基类或接口进行设计。 最好选择为服务定义合同的接口或抽象类。
  2. Consumer classes should be written in terms of service interface.消费者类应根据服务接口编写。
  3. Injector classes that will initialize the services and then the consumer classes.将初始化服务的注入器类,然后将初始化使用者类。

Java依赖注入–服务组件 (Java Dependency Injection – Service Components)

For our case, we can have MessageService that will declare the contract for service implementations.

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

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

Now let’s say we have Email and SMS services that implement the above interfaces.

现在,我们有实现上述接口的电子邮件和SMS服务。

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);}}

Our dependency injection java services are ready and now we can write our consumer class.

我们的依赖项注入Java服务已准备就绪,现在我们可以编写我们的使用者类。

Java依赖注入–服务使用者 (Java Dependency Injection – Service Consumer)

We are not required to have base interfaces for consumer classes but I will have a Consumer interface declaring contract for consumer classes.

我们不需要具有用于消费者类的基本接口,但是我将具有一个用于声明消费者类合同的Consumer接口。

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

My consumer class implementation is like below.

我的消费者类实现如下。

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);}}

Notice that our application class is just using the service. It does not initialize the service that leads to better “separation of concerns“. Also use of service interface allows us to easily test the application by mocking the MessageService and bind the services at runtime rather than compile time.

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

Now we are ready to write java dependency injector classes that will initialize the service and also consumer classes.

现在,我们准备编写java依赖注入程序类该类将初始化服务以及使用者类。

Java依赖注入–注入器类 (Java Dependency Injection – Injectors Classes)

Let’s have an interface MessageServiceInjector with method declaration that returns the Consumer class.

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

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

Now for every service, we will have to create injector classes like below.

现在,对于每项服务,我们将必须创建如下所示的喷射器类。

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());}}

Now let’s see how our client applications will use the application with a simple program.

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

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);}}

As you can see that our application classes are responsible only for using the service. Service classes are created in injectors. Also if we have to further extend our application to allow facebook messaging, we will have to write Service classes and injector classes only.

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

So dependency injection implementation solved the problem with hard-coded dependency and helped us in making our application flexible and easy to extend. Now let’s see how easily we can test our application class by mocking the injector and service classes.

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

Java依赖注入–带有模拟注入器和服务的JUnit测试用例 (Java Dependency Injection – JUnit Test Case with Mock Injector and Service)

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;}}

As you can see that I am using anonymous classes to mock the injector and service classes and I can easily test my application methods. I am using JUnit 4 for the above test class, so make sure it’s in your project build path if you are running above test class.

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

We have used constructors to inject the dependencies in the application classes, another way is to use a setter method to inject dependencies in application classes. For setter method dependency injection, our application class will be implemented like below.

我们使用构造函数将依赖项注入应用程序类中,另一种方法是使用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;}}

One of the best example of setter dependency injection is Struts2 Servlet API Aware interfaces.

设置程序依赖项注入的最佳示例之一是Struts2 Servlet API Aware接口 。

Whether to use Constructor based dependency injection or setter based is a design decision and depends on your requirements. For example, if my application can’t work at all without the service class then I would prefer constructor based DI or else I would go for setter method based DI to use it only when it’s really needed.

使用基于构造函数的依赖项注入还是基于setter是设计决策,取决于您的要求。 例如,如果我的应用程序如果没有服务类就无法工作,那么我会更喜欢基于构造函数的DI,否则我会选择基于setter方法的DI来仅在确实需要它时才使用它。

Dependency Injection in Java is a way to achieve Inversion of control (IoC) in our application by moving objects binding from compile time to runtime. We can achieve IoC through Factory Pattern, Template Method Design Pattern, Strategy Pattern and Service Locator pattern too.

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

Spring Dependency Injection, Google Guice and Java EE CDI frameworks facilitate the process of dependency injection through use of Java Reflection API and java annotations. All we need is to annotate the field, constructor or setter method and configure them in configuration xml files or classes.

Spring Dependency InjectionGoogle GuiceJava EE CDI框架通过使用Java Reflection API和Java注释简化了依赖项注入的过程。 我们所需要做的就是注释字段,构造函数或设置方法,并在配置xml文件或类中对其进行配置。

Java依赖注入的好处 (Benefits of Java Dependency Injection)

Some of the benefits of using Dependency Injection in Java are:

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

  • Separation of Concerns关注点分离
  • Boilerplate Code reduction in application classes because all work to initialize dependencies is handled by the injector component减少应用程序类中的代码,因为初始化依赖项的所有工作都由注入器组件处理
  • Configurable components makes application easily extendable可配置的组件使应用程序易于扩展
  • Unit testing is easy with mock objects使用模拟对象可以轻松进行单元测试

Java依赖注入的缺点 (Disadvantages of Java Dependency Injection)

Java Dependency injection has some disadvantages too:

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

  • If overused, it can lead to maintenance issues because the effect of changes are known at runtime.如果使用过度,则会导致维护问题,因为在运行时知道更改的效果。
  • Dependency injection in java hides the service class dependencies that can lead to runtime errors that would have been caught at compile time.Java中的依赖项注入隐藏了服务类依赖关系,这可能导致运行时错误,而这些错误在编译时就会被捕获。
Download Dependency Injection Project下载依赖注入项目

That’s all for dependency injection pattern in java. It’s good to know and use it when we are in control of the services.

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

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

依赖注入(di)模式

依赖注入(di)模式_Java依赖注入– DI设计模式示例教程相关推荐

  1. memento模式_Java中的Memento设计模式-示例教程

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

  2. java设计模式迭代器模式_Java中的迭代器设计模式–示例教程

    java设计模式迭代器模式 迭代器模式是一种行为模式,它用于提供遍历一组对象的标准方式. Iterator模式在Java Collection Framework中得到了广泛使用,其中Iterator ...

  3. java memento_memento模式_Java中的Memento设计模式-示例教程

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

  4. java 观察者模式示例_Java中的观察者设计模式-示例教程

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

  5. java 设计模式 示例_Java中的策略设计模式-示例教程

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

  6. java 设计模式 示例_Java中的状态设计模式–示例教程

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

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

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

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

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

  9. java状态模式和策略模式_Java状态和策略设计模式之间的差异

    java状态模式和策略模式 为了在Core Java应用程序中正确使用状态和策略设计模式,对于Java开发人员清楚地了解它们之间的区别很重要. 尽管状态和策略设计模式的结构相似,并且都基于开放式封闭设 ...

最新文章

  1. John的农场(最小生成树)
  2. 使用C#编写STM32对接物联网平台IoTSharp发送遥测数据
  3. 【有奖征文】WEB前端大作战,走在技术最前端!
  4. 使C#代码现代化——第二部分:方法
  5. c语言中cnthe普通变量,不得不说,关于 *(unsigned long *) 和 (unsigned long)
  6. 上岸后如何选择一个好的导师
  7. MTK手机 获取 Serial Number(barcode)
  8. Xilinx_JESD204B
  9. JS 解决IE浏览器出现Automation 服务器不能创建对象
  10. 答题器真的什么问题都能答出来吗?
  11. [心得]怪诞心理学精华笔记
  12. 2021年终总结——工作第四年
  13. 酒仙桥 asp.net 面试
  14. 10+ 条 Go 官方谚语,你知道几条?
  15. Exp10 Final 类CTF(Webug3.0漏洞靶场—渗透基础)
  16. Excel之UPPER、LOWER、IFERROR和SUBSTITUTE函数
  17. 【Shader】色调映射、视差贴图与实时阴影
  18. 12.04怎样查看更改屏幕显示的刷新频率
  19. 【Mendax1234】ThinkpadX390 完美Big Sur(附详细安装教程)
  20. Navicat Premium 免费

热门文章

  1. 对报表模型项应用安全筛选器
  2. [转载] python 时间sleep() 的方法
  3. [转载] python字符串只留数字_Python工匠:数字与字符串(下)
  4. [转载] 【Python进阶】4-2 多态 | 什么是多态 / 静态语言vs动态语言 / python中多态
  5. python里我最容易搞不清楚问题之一的encode和decode
  6. django mariadb filter month 失败
  7. 头条号【编编成程】开通
  8. Struts2学习笔记 - Action篇定义逻辑Action
  9. HDU4686——Arc of Dream矩阵快速幂
  10. 数据结构上机实践第九周项目2 - 二叉树遍历的递归算法