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

Java Dependency Injection的设计模式允许我们删除硬代码的依赖性,使我们的应用程序松散地连接,可扩展和可维护。

Java 依赖性注射

Java 依赖性注射似乎很难掌握理论,所以我会采取一个简单的例子,然后我们将看到如何使用依赖性注射模式来实现应用程序中的松散的连接和可扩展性。

1package com.journaldev.java.legacy;
2
3public class EmailService {
4
5    public void sendEmail(String message, String receiver){
6    	//logic to send email
7    	System.out.println("Email sent to "+receiver+ " with Message="+message);
8    }
9}

电子邮件服务类持有将电子邮件发送到收件人的电子邮件地址的逻辑。

 1package com.journaldev.java.legacy;
 2
 3public class MyApplication {
 4
 5    private EmailService email = new EmailService();
 6    
 7    public void processMessages(String msg, String rec){
 8    	//do some msg validation, manipulation logic etc
 9    	this.email.sendEmail(msg, rec);
10    }
11}

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

 1package com.journaldev.java.legacy;
 2
 3public class MyLegacyTest {
 4
 5    public static void main(String[] args) {
 6    	MyApplication app = new MyApplication();
 7    	app.processMessages("Hi Pankaj", "[email protected]");
 8    }
 9
10}

乍一看,上面的实现似乎没有什么不对劲,但上面的代码逻辑有一定的局限性。

*MyApplication类负责初始化电子邮件服务,然后使用它。这导致了硬代码的依赖性。如果我们想在未来切换到其他先进的电子邮件服务,它将需要MyApplication类的代码更改。这使得我们的应用程序难以扩展,如果电子邮件服务在多个类中使用,那么这将更难 *如果我们想要扩展我们的应用程序以提供额外的消息功能,如SMS或Facebook消息,那么我们需要为此写另一个应用程序。这将涉及应用程序类和客户类的代码更改 *测试应用程序将非常困难,因为我们的应用程序正在直接创建电子邮件服务实例。在我们的类测试中,我们无法嘲笑这些对象

可以说,我们可以将电子邮件服务实例创建从MyApplication类中删除,如果有一个需要电子邮件服务的构建程序作为一个论点。

 1package com.journaldev.java.legacy;
 2
 3public class MyApplication {
 4
 5    private EmailService email = null;
 6    
 7    public MyApplication(EmailService svc){
 8    	this.email=svc;
 9    }
10    
11    public void processMessages(String msg, String rec){
12    	//do some msg validation, manipulation logic etc
13    	this.email.sendEmail(msg, rec);
14    }
15}

但是在这种情况下,我们正在询问客户端应用程序或测试课程以初始化电子邮件服务,这不是一个好的设计决定。现在让我们看看如何应用Java依赖注射模式来解决上述实现的所有问题。

  1. 服务组件应采用基础类或接口设计,最好选择为服务定义合同的接口或抽象类
  2. 消费者类应用服务接口 进行编写。

Java 依赖注射 - 服务组件

对于我们的情况,我们可以有一个MessageService,它将宣布服务实施的合同。

1package com.journaldev.java.dependencyinjection.service;
2
3public interface MessageService {
4
5    void sendMessage(String msg, String rec);
6}

现在假设我们有电子邮件和SMS服务,这些服务实现了上述界面。

 1package com.journaldev.java.dependencyinjection.service;
 2
 3public class EmailServiceImpl implements MessageService {
 4
 5    @Override
 6    public void sendMessage(String msg, String rec) {
 7    	//logic to send email
 8    	System.out.println("Email sent to "+rec+ " with Message="+msg);
 9    }
10
11}
 1package com.journaldev.java.dependencyinjection.service;
 2
 3public class SMSServiceImpl implements MessageService {
 4
 5    @Override
 6    public void sendMessage(String msg, String rec) {
 7    	//logic to send SMS
 8    	System.out.println("SMS sent to "+rec+ " with Message="+msg);
 9    }
10
11}

我们的依赖性注射Java服务已经准备好了,现在我们可以写我们的消费类。

Java 依赖注射 - 服务消费者

我们不需要为消费类提供基本接口,但我会为消费类提供消费者接口。

1package com.journaldev.java.dependencyinjection.consumer;
2
3public interface Consumer {
4
5    void processMessages(String msg, String rec);
6}

我的消费类应用程序如下。

 1package com.journaldev.java.dependencyinjection.consumer;
 2
 3import com.journaldev.java.dependencyinjection.service.MessageService;
 4
 5public class MyDIApplication implements Consumer{
 6
 7    private MessageService service;
 8    
 9    public MyDIApplication(MessageService svc){
10    	this.service=svc;
11    }
12    
13    @Override
14    public void processMessages(String msg, String rec){
15    	//do some msg validation, manipulation logic etc
16    	this.service.sendMessage(msg, rec);
17    }
18
19}

请注意,我们的应用程序类只是使用服务。它不会初始化服务,导致更好的_separation of concerns_。 此外,使用服务界面允许我们轻松地通过嘲笑MessageService来测试应用程序,并在运行时绑定服务,而不是编译时间。

Java 依赖性注射 - 注射器类

让我们有一个界面MessageServiceInjector与方法声明,返回消费者类。

1package com.journaldev.java.dependencyinjection.injector;
2
3import com.journaldev.java.dependencyinjection.consumer.Consumer;
4
5public interface MessageServiceInjector {
6
7    public Consumer getConsumer();
8}

现在,对于每个服务,我们将不得不创建如下所示的注射器类。

 1package com.journaldev.java.dependencyinjection.injector;
 2
 3import com.journaldev.java.dependencyinjection.consumer.Consumer;
 4import com.journaldev.java.dependencyinjection.consumer.MyDIApplication;
 5import com.journaldev.java.dependencyinjection.service.EmailServiceImpl;
 6
 7public class EmailServiceInjector implements MessageServiceInjector {
 8
 9    @Override
10    public Consumer getConsumer() {
11    	return new MyDIApplication(new EmailServiceImpl());
12    }
13
14}
 1package com.journaldev.java.dependencyinjection.injector;
 2
 3import com.journaldev.java.dependencyinjection.consumer.Consumer;
 4import com.journaldev.java.dependencyinjection.consumer.MyDIApplication;
 5import com.journaldev.java.dependencyinjection.service.SMSServiceImpl;
 6
 7public class SMSServiceInjector implements MessageServiceInjector {
 8
 9    @Override
10    public Consumer getConsumer() {
11    	return new MyDIApplication(new SMSServiceImpl());
12    }
13
14}

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

 1package com.journaldev.java.dependencyinjection.test;
 2
 3import com.journaldev.java.dependencyinjection.consumer.Consumer;
 4import com.journaldev.java.dependencyinjection.injector.EmailServiceInjector;
 5import com.journaldev.java.dependencyinjection.injector.MessageServiceInjector;
 6import com.journaldev.java.dependencyinjection.injector.SMSServiceInjector;
 7
 8public class MyMessageDITest {
 9
10    public static void main(String[] args) {
11    	String msg = "Hi Pankaj";
12    	String email = "[email protected]";
13    	String phone = "4088888888";
14    	MessageServiceInjector injector = null;
15    	Consumer app = null;
16    	
17    	//Send email
18    	injector = new EmailServiceInjector();
19    	app = injector.getConsumer();
20    	app.processMessages(msg, email);
21    	
22    	//Send SMS
23    	injector = new SMSServiceInjector();
24    	app = injector.getConsumer();
25    	app.processMessages(msg, phone);
26    }
27
28}

正如你所看到的,我们的应用类只对使用服务负责。服务类在注射器中创建。如果我们需要进一步扩展我们的应用程序以允许Facebook消息传递,我们只需要写服务类和注射器类,所以依赖注射实现解决了硬代码依赖问题,并帮助我们使我们的应用程序灵活和易于扩展。

Java 依赖性注射 - JUnit 测试案例与 Mock 注射器和服务

 1package com.journaldev.java.dependencyinjection.test;
 2
 3import org.junit.After;
 4import org.junit.Before;
 5import org.junit.Test;
 6
 7import com.journaldev.java.dependencyinjection.consumer.Consumer;
 8import com.journaldev.java.dependencyinjection.consumer.MyDIApplication;
 9import com.journaldev.java.dependencyinjection.injector.MessageServiceInjector;
10import com.journaldev.java.dependencyinjection.service.MessageService;
11
12public class MyDIApplicationJUnitTest {
13
14    private MessageServiceInjector injector;
15    @Before
16    public void setUp(){
17    	//mock the injector with anonymous class
18    	injector = new MessageServiceInjector() {
19    		
20    		@Override
21    		public Consumer getConsumer() {
22    			//mock the message service
23    			return new MyDIApplication(new MessageService() {
24    				
25    				@Override
26    				public void sendMessage(String msg, String rec) {
27    					System.out.println("Mock Message Service implementation");
28    					
29    				}
30    			});
31    		}
32    	};
33    }
34    
35    @Test
36    public void test() {
37    	Consumer consumer = injector.getConsumer();
38    	consumer.processMessages("Hi Pankaj", "[email protected]");
39    }
40    
41    @After
42    public void tear(){
43    	injector = null;
44    }
45
46}

正如你所看到的,我正在使用 匿名类 mock injector 和 service classes,我可以很容易地测试我的应用方法。我正在使用 JUnit 4 用于上述测试类,所以请确保它是在您的项目构建路径中,如果你在测试类上运行。

 1package com.journaldev.java.dependencyinjection.consumer;
 2
 3import com.journaldev.java.dependencyinjection.service.MessageService;
 4
 5public class MyDIApplication implements Consumer{
 6
 7    private MessageService service;
 8    
 9    public MyDIApplication(){}
10
11    //setter dependency injection	
12    public void setService(MessageService service) {
13    	this.service = service;
14    }
15
16    @Override
17    public void processMessages(String msg, String rec){
18    	//do some msg validation, manipulation logic etc
19    	this.service.sendMessage(msg, rec);
20    }
21
22}
 1package com.journaldev.java.dependencyinjection.injector;
 2
 3import com.journaldev.java.dependencyinjection.consumer.Consumer;
 4import com.journaldev.java.dependencyinjection.consumer.MyDIApplication;
 5import com.journaldev.java.dependencyinjection.service.EmailServiceImpl;
 6
 7public class EmailServiceInjector implements MessageServiceInjector {
 8
 9    @Override
10    public Consumer getConsumer() {
11    	MyDIApplication app = new MyDIApplication();
12    	app.setService(new EmailServiceImpl());
13    	return app;
14    }
15
16}

确定依赖性注射的最佳例子是Struts2 Servlet API 了解接口. 是否使用基于建构器的依赖性注射或基于设置是设计决定,取决于您的需要. 举例来说,如果我的应用没有服务课就根本无法工作,那么我更喜欢建筑师基于DI,否则我就会选择基于设计师的方法,只在真正需要的时候使用它. ** Java** 中的依赖性注射,是通过将绑定对象从编译时间移到运行时间来实现我们应用程序中的** 控制权倒置** (** IoC**)的方法. 我们可以通过事实模式,模式方法设计模式,战略模式和服务定位模式来实现IoC。 ** 春季依赖注射, ** Google GuiceJava EE CDI框架通过使用Java Reflectionion API教区说明,促进依赖性注射的过程。 我们只需要对字段、构造器或设置方法进行注释,并在配置 xml 文件或类中配置它们.

Java 依赖注射的好处

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

  • 关切的分离
  • 应用类中的锅板代码减少,因为所有开始依赖的工作都由注射器组件处理
  • 可配置的组件使应用程序易于扩展
  • 单元测试易于使用模拟对象

Java 依赖性注射的缺点

Java 依赖性注射也有一些缺点:

  • 如果过度使用,可能会导致维护问题,因为变更的效果在运行时是已知的 *在java中的依赖注入隐藏了可能导致运行时错误的服务类依赖

[下载《无产阶级工程》(LINK0)]

这就是在java中的依赖性注射模式的一切,当我们控制服务时,了解并使用它是很好的。

Published At
Categories with 技术
comments powered by Disqus