今天我们将研究Spring依赖注入。Spring框架核心概念是;依赖注入”;和“;面向切面编程“;。我之前写过Java依赖注入以及我们如何使用谷歌指南在我们的应用程序中自动化这个过程的框架。
目录
Spring注射依赖
让我们逐一查看每个组件。
Spring依赖注入和Maven依赖项
我在中添加了Spring和junitmaven依赖项pom.xml文件文件,最终pom.xml文件代码如下。
<project xmlns="https://maven.apache.org/POM/4.0.0" xmlns:xsi="https://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="https://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.journaldev.spring</groupId>
<artifactId>spring-dependency-injection</artifactId>
<version>0.0.1-SNAPSHOT</version>
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>4.0.0.RELEASE</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.8.1</version>
<scope>test</scope>
</dependency>
</dependencies>
</project>
Spring框架的当前稳定版本是4.0.0.放行而JUnit当前的版本是4.8.1条如果你需要其他的小版本的话,你可能需要一些小版本。如果您要构建这个项目,您会注意到由于可传递的依赖项,其他一些jar也被添加到maven依赖项中,就像上图所示。
Spring依赖注入和服务类
假设我们要向用户发送电子邮件和twitter消息。对于依赖注入,我们需要为服务提供一个基类。所以我有MessageService
interface with single method declaration for sending message.
package com.journaldev.spring.di.services;
public interface MessageService {
boolean sendMessage(String msg, String rec);
}
现在我们将有实际的实现类来发送电子邮件和twitter消息。
package com.journaldev.spring.di.services;
public class EmailService implements MessageService {
public boolean sendMessage(String msg, String rec) {
System.out.println("Email Sent to "+rec+ " with Message="+msg);
return true;
}
}
package com.journaldev.spring.di.services;
public class TwitterService implements MessageService {
public boolean sendMessage(String msg, String rec) {
System.out.println("Twitter message Sent to "+rec+ " with Message="+msg);
return true;
}
}
既然我们的服务已经准备好了,我们可以继续研究将使用服务的组件类。
Spring依赖注入和组件类
让我们为上述服务编写一个consumer类。我们将有两个consumer类–;一个带有Spring注解用于自动连接,另一个没有注解和布线配置,将在XML配置文件中提供。
package com.journaldev.spring.di.consumer;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.stereotype.Component;
import com.journaldev.spring.di.services.MessageService;
@Component
public class MyApplication {
//field-based dependency injection
//@Autowired
private MessageService service;
// constructor-based dependency injection
// @Autowired
// public MyApplication(MessageService svc){
// this.service=svc;
// }
@Autowired
public void setService(MessageService svc){
this.service=svc;
}
public boolean processMessage(String msg, String rec){
//some magic like validation, logging etc
return this.service.sendMessage(msg, rec);
}
}
关于MyApplication类的几个要点:
@Component
annotation is added to the class, so that when Spring framework will scan for the components, this class will be treated as component. @Component annotation can be applied only to the class and it’s retention policy is Runtime. If you are not not familiar with Annotations retention policy, I would suggest you to read java注解教程.@Autowired
annotation is used to let Spring know that autowiring is required. This can be applied to field, constructor and methods. This annotation allows us to implement constructor-based, field-based or method-based dependency injection in our components.- 对于我们的示例,我使用基于方法的依赖注入。您可以取消对构造函数方法的注解,以切换到基于构造函数的依赖项注入。
现在让我们编写不带注解的类似类。
package com.journaldev.spring.di.consumer;
import com.journaldev.spring.di.services.MessageService;
public class MyXMLApplication {
private MessageService service;
//constructor-based dependency injection
// public MyXMLApplication(MessageService svc) {
// this.service = svc;
// }
//setter-based dependency injection
public void setService(MessageService svc){
this.service=svc;
}
public boolean processMessage(String msg, String rec) {
// some magic like validation, logging etc
return this.service.sendMessage(msg, rec);
}
}
使用服务的简单应用程序类。对于基于XML的配置,我们可以使用基于构造函数的spring依赖注入或基于方法的spring依赖注入。注意,基于方法和基于setter的注入方法是相同的,只是有些人更喜欢称之为基于setter的,而有些人则称之为基于方法的。
带有注解的Spring依赖注入配置
对于基于注解的配置,我们需要编写一个配置器类,用于将实际的实现bean注入组件属性。
package com.journaldev.spring.di.configuration;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import com.journaldev.spring.di.services.EmailService;
import com.journaldev.spring.di.services.MessageService;
@Configuration
@ComponentScan(value={"com.journaldev.spring.di.consumer"})
public class DIConfiguration {
@Bean
public MessageService getMessageService(){
return new EmailService();
}
}
与上述课程相关的一些要点包括:
@Configuration
annotation is used to let Spring know that it’s a Configuration class.@ComponentScan
annotation is used with@Configuration
annotation to specify the packages to look for Component classes.@Bean
annotation is used to let Spring framework know that this method should be used to get the bean implementation to inject in Component classes.
让我们编写一个简单的程序来测试基于注解的Spring依赖注入示例。
package com.journaldev.spring.di.test;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import com.journaldev.spring.di.configuration.DIConfiguration;
import com.journaldev.spring.di.consumer.MyApplication;
public class ClientApplication {
public static void main(String[] args) {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(DIConfiguration.class);
MyApplication app = context.getBean(MyApplication.class);
app.processMessage("Hi Pankaj", "pankaj@abc.com");
//close the context
context.close();
}
}
AnnotationConfigApplicationContext
is the implementation of AbstractApplicationContext
abstract class and it’s used for autowiring the services to components when annotations are used. AnnotationConfigApplicationContext
constructor takes Class as argument that will be used to get the bean implementation to inject in component classes.
getBean(类)方法返回组件对象,并使用配置自动连接对象。当我们使用资源密集的对象时,我们应该这样做。当我们在程序上面运行时,我们得到的是低于输出的结果。
Dec 16, 2013 11:49:20 PM org.springframework.context.support.AbstractApplicationContext prepareRefresh
INFO: Refreshing org.springframework.context.annotation.AnnotationConfigApplicationContext@3067ed13: startup date [Mon Dec 16 23:49:20 PST 2013]; root of context hierarchy
Email Sent to pankaj@abc.com with Message=Hi Pankaj
Dec 16, 2013 11:49:20 PM org.springframework.context.support.AbstractApplicationContext doClose
INFO: Closing org.springframework.context.annotation.AnnotationConfigApplicationContext@3067ed13: startup date [Mon Dec 16 23:49:20 PST 2013]; root of context hierarchy
基于Spring依赖注入XML的配置
我们将用下面的数据创建Spring配置文件,文件名可以是任何东西。
应用程序上下文.xml代码:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="https://www.springframework.org/schema/beans"
xmlns:xsi="https://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="
https://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans-4.0.xsd">
<!--
<bean id="MyXMLApp">
<constructor-arg>
<bean />
</constructor-arg>
</bean>
-->
<bean id="twitter"></bean>
<bean id="MyXMLApp">
<property name="service" ref="twitter"></property>
</bean>
</beans>
注意,上面的XML包含了基于构造函数和基于setter的spring依赖注入的配置。因为MyXMLApplication
is using setter method for injection, the bean configuration contains 财产注射用元件。对于基于构造函数的注入,我们必须使用构造函数参数元素。
配置XML文件放在源目录中,因此在构建之后它将位于classes目录中。
让我们看看如何在一个简单的程序中使用基于XML的配置。
package com.journaldev.spring.di.test;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import com.journaldev.spring.di.consumer.MyXMLApplication;
public class ClientXMLApplication {
public static void main(String[] args) {
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext(
"applicationContext.xml");
MyXMLApplication app = context.getBean(MyXMLApplication.class);
app.processMessage("Hi Pankaj", "pankaj@abc.com");
// close the context
context.close();
}
}
ClassPathXmlApplicationContext
is used to get the ApplicationContext object by providing the configuration files location. It has multiple overloaded constructors and we can provide multiple config files also.
其余代码类似于基于注解的配置测试程序,唯一不同的是我们根据配置选择获取ApplicationContext对象的方式。
当我们运行上述程序时,我们得到以下输出。
Dec 17, 2013 12:01:23 AM org.springframework.context.support.AbstractApplicationContext prepareRefresh
INFO: Refreshing org.springframework.context.support.ClassPathXmlApplicationContext@4eeaabad: startup date [Tue Dec 17 00:01:23 PST 2013]; root of context hierarchy
Dec 17, 2013 12:01:23 AM org.springframework.beans.factory.xml.XmlBeanDefinitionReader loadBeanDefinitions
INFO: Loading XML bean definitions from class path resource [applicationContext.xml]
Twitter message Sent to pankaj@abc.com with Message=Hi Pankaj
Dec 17, 2013 12:01:23 AM org.springframework.context.support.AbstractApplicationContext doClose
INFO: Closing org.springframework.context.support.ClassPathXmlApplicationContext@4eeaabad: startup date [Tue Dec 17 00:01:23 PST 2013]; root of context hierarchy
请注意,有些输出是由Spring框架编写的。由于Spring框架使用log4j来记录日志,而我还没有配置它,所以输出被写入控制台。
Spring依赖注入JUnit测试用例
spring中依赖注入的一个主要好处是可以轻松地使用模拟服务类,而不是使用实际的服务。因此,我结合了上面的所有学习,并在一个JUnit4测试类中编写了所有内容,以便在spring中进行依赖注入。
package com.journaldev.spring.di.test;
import org.junit.Assert;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import com.journaldev.spring.di.consumer.MyApplication;
import com.journaldev.spring.di.services.MessageService;
@Configuration
@ComponentScan(value="com.journaldev.spring.di.consumer")
public class MyApplicationTest {
private AnnotationConfigApplicationContext context = null;
@Bean
public MessageService getMessageService() {
return new MessageService(){
public boolean sendMessage(String msg, String rec) {
System.out.println("Mock Service");
return true;
}
};
}
@Before
public void setUp() throws Exception {
context = new AnnotationConfigApplicationContext(MyApplicationTest.class);
}
@After
public void tearDown() throws Exception {
context.close();
}
@Test
public void test() {
MyApplication app = context.getBean(MyApplication.class);
Assert.assertTrue(app.processMessage("Hi Pankaj", "pankaj@abc.com"));
}
}
类被注解为@Configuration
and @ComponentScan
annotation because getMessageService()方法返回MessageService
mock implementation. That’s why getMessageService()注解为@Bean
annotation.
因为我在测试MyApplication
class that is configured with annotation, I am using AnnotationConfigApplicationContext
and creating it’s object in the setUp() method. The context is getting closed in 拆卸()方法。测试()方法代码只是从上下文中获取组件对象并对其进行测试。
您想知道Spring框架是如何自动连接和调用Spring框架未知的方法的吗。在大量使用
从上面的URL下载示例Spring依赖注入(DI)项目,并使用它来了解更多。