今天我们将探讨春季依赖注射。 春季框架的核心概念是**依赖注射**
和**面向编程**
。我之前曾写过关于(Java依赖注射)(/社区/教程/java-依赖注射设计模式示例教程Java依赖注射
)以及我们如何使用(Google Guice)(/社区/教程/google-guice-依赖注射示例教程Google Guice依赖注射示例教程
)框架来在我们的应用程序中自动化这个过程。
春季依赖性注射
This tutorial is aimed to provide details about Spring Dependency Injection example with both annotation based configuration and XML file based configuration. I will also provide JUnit test case example for the application, since easy testability is one of the major benefits of dependency injection. I have created spring-dependency-injection maven project whose structure looks like below image.
Let's look at each of the components one by one.
春季依赖注射 - Maven 依赖
我在 pom.xml 文件中添加了 Spring 和 JUnit maven 依赖,最后的 pom.xml 代码在下面。
1<project xmlns="https://maven.apache.org/POM/4.0.0" xmlns:xsi="https://www.w3.org/2001/XMLSchema-instance"
2 xsi:schemaLocation="https://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
3 <modelVersion>4.0.0</modelVersion>
4 <groupId>com.journaldev.spring</groupId>
5 <artifactId>spring-dependency-injection</artifactId>
6 <version>0.0.1-SNAPSHOT</version>
7
8 <dependencies>
9 <dependency>
10 <groupId>org.springframework</groupId>
11 <artifactId>spring-context</artifactId>
12 <version>4.0.0.RELEASE</version>
13 </dependency>
14 <dependency>
15 <groupId>junit</groupId>
16 <artifactId>junit</artifactId>
17 <version>4.8.1</version>
18 <scope>test</scope>
19 </dependency>
20 </dependencies>
21
22</project>
目前的稳定版本的 Spring Framework 是 4.0.0.RELEASE 和 JUnit 的当前版本是 4.8.1,如果您正在使用任何其他版本,那么可能会有很小的可能性,该项目将需要一些变化。
春季成瘾注射 - 服务类
假设我们想向用户发送电子邮件和推特消息,对于成瘾注射,我们需要为服务提供一个基本类,所以我有MessageService
接口,用单一方法声明发送消息。
1package com.journaldev.spring.di.services;
2
3public interface MessageService {
4
5 boolean sendMessage(String msg, String rec);
6}
现在,我们将有实际的实施类来发送电子邮件和推特消息。
1package com.journaldev.spring.di.services;
2
3public class EmailService implements MessageService {
4
5 public boolean sendMessage(String msg, String rec) {
6 System.out.println("Email Sent to "+rec+ " with Message="+msg);
7 return true;
8 }
9
10}
1package com.journaldev.spring.di.services;
2
3public class TwitterService implements MessageService {
4
5 public boolean sendMessage(String msg, String rec) {
6 System.out.println("Twitter message Sent to "+rec+ " with Message="+msg);
7 return true;
8 }
9
10}
现在我们的服务已经准备好了,我们可以转到将消耗服务的组件类。
春季成瘾注射 - 组件类
我们将有两个消费类 - 一个有春季注释用于自动连接,另一个没有注释和连接配置将在XML配置文件中提供。
1package com.journaldev.spring.di.consumer;
2
3import org.springframework.beans.factory.annotation.Autowired;
4import org.springframework.context.annotation.ComponentScan;
5import org.springframework.stereotype.Component;
6
7import com.journaldev.spring.di.services.MessageService;
8
9@Component
10public class MyApplication {
11
12 //field-based dependency injection
13 //@Autowired
14 private MessageService service;
15
16// constructor-based dependency injection
17// @Autowired
18// public MyApplication(MessageService svc){
19// this.service=svc;
20// }
21
22 @Autowired
23 public void setService(MessageService svc){
24 this.service=svc;
25 }
26
27 public boolean processMessage(String msg, String rec){
28 //some magic like validation, logging etc
29 return this.service.sendMessage(msg, rec);
30 }
31}
关于 MyApplication 类的一些重要点:
@Component
注释被添加到类中,所以当 Spring framework 对组件进行扫描时,这个类将被视为组件。 @Component 注释只能应用于类,其保留策略是 Runtime. 如果您不熟悉注释保留策略,我建议您阅读 [java 注释教程](/社区/教程/java-注释Java 注释教程与自定义注释示例和使用反思分析
)@Autowired
注释被用来让 Spring 知道需要自动化。这可以应用于领域、构建者和方法。这个注释允许我们在我们的组件中实施基于构建者、基于领域或基于方法的依赖注入- 例如,
现在让我们写类似的类别,没有注释。
1package com.journaldev.spring.di.consumer;
2
3import com.journaldev.spring.di.services.MessageService;
4
5public class MyXMLApplication {
6
7 private MessageService service;
8
9 //constructor-based dependency injection
10// public MyXMLApplication(MessageService svc) {
11// this.service = svc;
12// }
13
14 //setter-based dependency injection
15 public void setService(MessageService svc){
16 this.service=svc;
17 }
18
19 public boolean processMessage(String msg, String rec) {
20 // some magic like validation, logging etc
21 return this.service.sendMessage(msg, rec);
22 }
23}
一个简单的应用类消耗服务. 对于基于XML的配置,我们可以使用实现基于构建器的春季依赖注射或基于方法的春季依赖注射。
春季依赖性注射配置与注释
对于基于注释的配置,我们需要写一个 Configurator 类,用来将实际的实施本注入到组件属性。
1package com.journaldev.spring.di.configuration;
2
3import org.springframework.context.annotation.Bean;
4import org.springframework.context.annotation.ComponentScan;
5import org.springframework.context.annotation.Configuration;
6
7import com.journaldev.spring.di.services.EmailService;
8import com.journaldev.spring.di.services.MessageService;
9
10@Configuration
11@ComponentScan(value={"com.journaldev.spring.di.consumer"})
12public class DIConfiguration {
13
14 @Bean
15 public MessageService getMessageService(){
16 return new EmailService();
17 }
18}
与上一类有关的一些重要点是:
@Configuration
注释用于让 Spring 知道它是 Configuration 类@ComponentScan
注释用于与@Configuration
注释一起指定要搜索 Component 类的包@Bean
注释用于让 Spring 框架知道该方法应该用来让 Bean 实现在 Component 类中注入
让我们写一个简单的程序来测试我们基于注释的春季依赖注射示例。
1package com.journaldev.spring.di.test;
2
3import org.springframework.context.annotation.AnnotationConfigApplicationContext;
4
5import com.journaldev.spring.di.configuration.DIConfiguration;
6import com.journaldev.spring.di.consumer.MyApplication;
7
8public class ClientApplication {
9
10 public static void main(String[] args) {
11 AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(DIConfiguration.class);
12 MyApplication app = context.getBean(MyApplication.class);
13
14 app.processMessage("Hi Pankaj", "[email protected]");
15
16 //close the context
17 context.close();
18 }
19
20}
AnnotationConfigApplicationContext
是AbstractApplicationContext
抽象类的实现,它用于在使用注释时自动将服务发送到组件中。 AnnotationConfigApplicationContext
构建器将Class作为被用来在组件类中注入豆子实现的论点。 _getBean(Class)_方法返回组件对象并使用配置来自动发送对象。
1Dec 16, 2013 11:49:20 PM org.springframework.context.support.AbstractApplicationContext prepareRefresh
2INFO: Refreshing org.springframework.context.annotation.AnnotationConfigApplicationContext@3067ed13: startup date [Mon Dec 16 23:49:20 PST 2013]; root of context hierarchy
3Email Sent to [email protected] with Message=Hi Pankaj
4Dec 16, 2013 11:49:20 PM org.springframework.context.support.AbstractApplicationContext doClose
5INFO: Closing org.springframework.context.annotation.AnnotationConfigApplicationContext@3067ed13: startup date [Mon Dec 16 23:49:20 PST 2013]; root of context hierarchy
Spring Dependency Injection 基于 XML 的配置
我们将创建 Spring 配置文件,下面的数据,文件名可以是任何东西。
1<?xml version="1.0" encoding="UTF-8"?>
2<beans xmlns="https://www.springframework.org/schema/beans"
3 xmlns:xsi="https://www.w3.org/2001/XMLSchema-instance"
4 xsi:schemaLocation="
5https://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans-4.0.xsd">
6
7<!--
8 <bean id="MyXMLApp" class="com.journaldev.spring.di.consumer.MyXMLApplication">
9 <constructor-arg>
10 <bean class="com.journaldev.spring.di.services.TwitterService" />
11 </constructor-arg>
12 </bean>
13-->
14 <bean id="twitter" class="com.journaldev.spring.di.services.TwitterService"></bean>
15 <bean id="MyXMLApp" class="com.journaldev.spring.di.consumer.MyXMLApplication">
16 <property name="service" ref="twitter"></property>
17 </bean>
18</beans>
请注意,上面所述的 XML 包含构建者基于和 setter 基于的春季依赖注射的配置。由于MyXMLApplication
正在使用 setter 方法进行注射,所以豆类配置包含 property 元素进行注射。对于构建者基于的注射,我们必须使用 constructor-arg 元素。 配置 XML 文件放置在源目录中,所以它会在构建后的类目录中。
1package com.journaldev.spring.di.test;
2
3import org.springframework.context.support.ClassPathXmlApplicationContext;
4
5import com.journaldev.spring.di.consumer.MyXMLApplication;
6
7public class ClientXMLApplication {
8
9 public static void main(String[] args) {
10 ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext(
11 "applicationContext.xml");
12 MyXMLApplication app = context.getBean(MyXMLApplication.class);
13
14 app.processMessage("Hi Pankaj", "[email protected]");
15
16 // close the context
17 context.close();
18 }
19
20}
「ClassPathXmlApplicationContext」是用来通过提供配置文件的位置来获取ApplicationContext对象的。它有多个过载的构建器,我们也可以提供多个配置文件。代码的其余部分类似于基于注释的配置测试程序,唯一的区别是我们根据我们的配置选择获得ApplicationContext对象的方式。
1Dec 17, 2013 12:01:23 AM org.springframework.context.support.AbstractApplicationContext prepareRefresh
2INFO: Refreshing org.springframework.context.support.ClassPathXmlApplicationContext@4eeaabad: startup date [Tue Dec 17 00:01:23 PST 2013]; root of context hierarchy
3Dec 17, 2013 12:01:23 AM org.springframework.beans.factory.xml.XmlBeanDefinitionReader loadBeanDefinitions
4INFO: Loading XML bean definitions from class path resource [applicationContext.xml]
5Twitter message Sent to pankaj@abc.com with Message=Hi Pankaj
6Dec 17, 2013 12:01:23 AM org.springframework.context.support.AbstractApplicationContext doClose
7INFO: Closing org.springframework.context.support.ClassPathXmlApplicationContext@4eeaabad: startup date [Tue Dec 17 00:01:23 PST 2013]; root of context hierarchy
请注意,部分输出是由Spring Framework编写的,因为Spring Framework 使用log4j进行日志记录,而我没有配置它,所以输出正在被编写到控制台。
春季成瘾注射 JUnit 测试案例
春季成瘾注射的主要好处之一是很容易有伪装服务班,而不是使用实际服务,所以我已经结合了上面的所有学习,并在一个单一的 JUnit 4 测试班中写入了春季成瘾注射。
1package com.journaldev.spring.di.test;
2
3import org.junit.Assert;
4import org.junit.After;
5import org.junit.Before;
6import org.junit.Test;
7import org.springframework.context.annotation.AnnotationConfigApplicationContext;
8import org.springframework.context.annotation.Bean;
9import org.springframework.context.annotation.ComponentScan;
10import org.springframework.context.annotation.Configuration;
11
12import com.journaldev.spring.di.consumer.MyApplication;
13import com.journaldev.spring.di.services.MessageService;
14
15@Configuration
16@ComponentScan(value="com.journaldev.spring.di.consumer")
17public class MyApplicationTest {
18
19 private AnnotationConfigApplicationContext context = null;
20
21 @Bean
22 public MessageService getMessageService() {
23 return new MessageService(){
24
25 public boolean sendMessage(String msg, String rec) {
26 System.out.println("Mock Service");
27 return true;
28 }
29
30 };
31 }
32
33 @Before
34 public void setUp() throws Exception {
35 context = new AnnotationConfigApplicationContext(MyApplicationTest.class);
36 }
37
38 @After
39 public void tearDown() throws Exception {
40 context.close();
41 }
42
43 @Test
44 public void test() {
45 MyApplication app = context.getBean(MyApplication.class);
46 Assert.assertTrue(app.processMessage("Hi Pankaj", "[email protected]"));
47 }
48
49}
由于 _getMessageService() 方法返回了MessageService
模仿实现,所以类被注明了@Configuration
和@ComponentScan
注释,因为我正在测试MyApplication
类以注释配置,所以我正在使用AnnotationConfigApplicationContext
并在 setUp() 方法中创建它的对象,所以 _getMessageService() 代码被注释了@Bean
注释。由于我正在测试MyApplication
类,我正在使用AnnotationConfigApplicationContext
并在 setUp() 方法中创建它的对象,所以背景在 tearDown() 方法中被关闭。 _test() 方法代码只是从文本中获取组件对象并测试它吗?你
下载上面的 URL 上的 Spring Dependency Injection (DI) 项目样本,并与它一起玩,了解更多。