Injection de dépendance Spring

Aujourd’hui, nous allons examiner l’injection de dépendance Spring. Les concepts de base du Framework Spring sont « l’Injection de Dépendance » et la « Programmation Orientée Aspect« . J’ai déjà écrit sur l’Injection de Dépendance en Java et comment nous pouvons utiliser le Framework Google Guice pour automatiser ce processus dans nos applications.

L’Injection de Dépendance Spring

Ce tutoriel vise à fournir des détails sur l’exemple d’injection de dépendance Spring avec une configuration basée sur les annotations et une configuration basée sur un fichier XML. Je fournirai également un exemple de cas de test JUnit pour l’application, car la facilité de testabilité est l’un des principaux avantages de l’injection de dépendance. J’ai créé le projet maven spring-dependency-injection dont la structure ressemble à l’image ci-dessous. Examinons chacun des composants un par un.

Injection de dépendance Spring – Dépendances Maven

I have added Spring and JUnit maven dependencies in pom.xml file, final pom.xml code is below.

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

La version stable actuelle du framework Spring est 4.0.0.RELEASE et la version actuelle de JUnit est 4.8.1, si vous utilisez d’autres versions, il pourrait y avoir une petite chance que le projet nécessite des modifications. Si vous construisez le projet, vous remarquerez que d’autres jars sont également ajoutés aux dépendances maven en raison des dépendances transitives, tout comme sur l’image ci-dessus.

Injection de dépendance Spring – Classes de service

Disons que nous voulons envoyer un message électronique et un message Twitter aux utilisateurs. Pour l’injection de dépendance, nous devons avoir une classe de base pour les services. J’ai donc une interface MessageService avec une déclaration de méthode unique pour l’envoi de message.

package com.journaldev.spring.di.services;

public interface MessageService {

	boolean sendMessage(String msg, String rec);
}

Nous aurons maintenant des classes d’implémentation réelle pour envoyer des messages électroniques et 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;
	}

}

Maintenant que nos services sont prêts, nous pouvons passer aux classes de composants qui consommeront le service.

Injection de dépendance Spring – Classes de composants

Écrivons une classe consommatrice pour les services ci-dessus. Nous aurons deux classes consommatrices – une avec des annotations Spring pour l’autocâblage et une autre sans annotation et la configuration de câblage sera fournie dans le fichier de configuration 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 {

	//Injection de dépendance basée sur les champs
	//@Autowired
	private MessageService service;
	
//Injection de dépendance basée sur le constructeur	
//	@Autowired
//	public MyApplication(MessageService svc){
//		this.service=svc;
//	}
	
	@Autowired
	public void setService(MessageService svc){
		this.service=svc;
	}
	
	public boolean processMessage(String msg, String rec){
		//quelques opérations magiques comme la validation, le journalisation, etc.
		return this.service.sendMessage(msg, rec);
	}
}

Quelques points importants concernant la classe MyApplication :

  • L’annotation @Component est ajoutée à la classe, de sorte que lorsque le framework Spring analysera les composants, cette classe sera traitée comme un composant. L’annotation @Component ne peut être appliquée qu’à la classe et sa politique de rétention est Runtime. Si vous n’êtes pas familier avec la politique de rétention des annotations, je vous suggère de lire le tutoriel sur les annotations en Java.
  • L’annotation @Autowired est utilisée pour indiquer à Spring que l’autocâblage est requis. Cela peut être appliqué au champ, au constructeur et aux méthodes. Cette annotation nous permet de mettre en œuvre une injection de dépendance basée sur le constructeur, le champ ou la méthode dans nos composants.
  • Pour notre exemple, j’utilise une injection de dépendance basée sur la méthode. Vous pouvez décommenter la méthode du constructeur pour passer à une injection de dépendance basée sur le constructeur.

Maintenant, écrivons une classe similaire sans annotations.

package com.journaldev.spring.di.consumer;

import com.journaldev.spring.di.services.MessageService;

public class MyXMLApplication {

	private MessageService service;

	// Injection de dépendance basée sur le constructeur 
//	public MyXMLApplication(MessageService svc) {
//		this.service = svc;
//	}
	
	// Injection de dépendance basée sur le setter 
	public void setService(MessageService svc){
		this.service=svc;
	}

	public boolean processMessage(String msg, String rec) {
		// quelque magie comme la validation, la journalisation, etc.
		return this.service.sendMessage(msg, rec);
	}
}

A simple application class consuming the service. For XML based configuration, we can use implement either constructor-based spring dependency injection or method-based spring dependency injection. Note that method-based and setter-based injection approaches are same, it’s just that some prefer calling it setter-based and some call it method-based.

Configuration de l’injection de dépendance Spring avec des annotations

Pour une configuration basée sur les annotations, nous devons écrire une classe Configurator qui sera utilisée pour injecter la véritable implémentation du bean dans la propriété du composant.

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

Quelques points importants liés à la classe ci-dessus sont :

  • La notation @Configuration est utilisée pour informer Spring qu’il s’agit d’une classe de configuration.
  • La notation @ComponentScan est utilisée avec la notation @Configuration pour spécifier les packages à rechercher pour les classes de composants.
  • La notation @Bean est utilisée pour informer le framework Spring que cette méthode doit être utilisée pour obtenir l’implémentation du bean à injecter dans les classes de composants.

Écrivons un programme simple pour tester notre exemple d’injection de dépendance Spring basé sur les annotations.

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", "[email protected]");
		
		// Fermer le contexte
		context.close();
	}

}

AnnotationConfigApplicationContext est l’implémentation de la classe abstraite AbstractApplicationContext et elle est utilisée pour effectuer le câblage automatique des services vers les composants lorsque des annotations sont utilisées. Le constructeur de AnnotationConfigApplicationContext prend une classe en argument qui sera utilisée pour obtenir l’implémentation du bean à injecter dans les classes de composants. La méthode getBean(Class) renvoie l’objet Component et utilise la configuration pour effectuer le câblage automatique des objets. Les objets de contexte sont intensifs en ressources, nous devrions donc les fermer lorsque nous avons fini avec eux. Lorsque nous exécutons le programme ci-dessus, nous obtenons la sortie suivante.

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 [email protected] 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

Configuration basée sur XML pour l’injection de dépendances Spring

Nous allons créer un fichier de configuration Spring avec les données suivantes, le nom du fichier peut être n’importe quoi. Code du fichier applicationContext.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" class="com.journaldev.spring.di.consumer.MyXMLApplication">
		<constructor-arg>
			<bean class="com.journaldev.spring.di.services.TwitterService" />
		</constructor-arg>
	</bean>
-->
	<bean id="twitter" class="com.journaldev.spring.di.services.TwitterService"></bean>
	<bean id="MyXMLApp" class="com.journaldev.spring.di.consumer.MyXMLApplication">
		<property name="service" ref="twitter"></property>
	</bean>
</beans>

Remarquez que le XML ci-dessus contient une configuration pour l’injection de dépendances Spring basée à la fois sur le constructeur et sur le setter. Étant donné que MyXMLApplication utilise la méthode setter pour l’injection, la configuration du bean contient l’élément property pour l’injection. Pour l’injection basée sur le constructeur, nous devons utiliser l’élément constructor-arg. Le fichier de configuration XML est placé dans le répertoire source, il sera donc dans le répertoire classes après la construction. Voyons comment utiliser la configuration basée sur XML avec un programme simple.

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", "[email protected]");

		// fermer le contexte
		context.close();
	}

}

La classe ClassPathXmlApplicationContext est utilisée pour obtenir l’objet ApplicationContext en fournissant l’emplacement des fichiers de configuration. Elle dispose de plusieurs constructeurs surchargés et nous pouvons également fournir plusieurs fichiers de configuration. Le reste du code est similaire au programme de test de configuration basé sur les annotations, la seule différence est la manière dont nous obtenons l’objet ApplicationContext en fonction de notre choix de configuration. Lorsque nous exécutons le programme ci-dessus, nous obtenons la sortie suivante.

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 [email protected] 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

Remarquez que certaines sorties sont écrites par le Framework Spring. Étant donné que le Framework Spring utilise log4j à des fins de journalisation et que je ne l’ai pas configuré, la sortie est écrite dans la console.

Cas de test JUnit pour l’injection de dépendances Spring

Un des principaux avantages de l’injection de dépendances dans Spring est la facilité d’avoir des classes de service simulées plutôt que d’utiliser de véritables services. J’ai donc combiné tout ce que j’ai appris ci-dessus et écrit tout dans une seule classe de test JUnit 4 pour l’injection de dépendances dans 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", "[email protected]"));
	}

}

La classe est annotée avec les annotations @Configuration et @ComponentScan car la méthode getMessageService() retourne l’implémentation factice de MessageService. C’est pourquoi getMessageService() est annotée avec l’annotation @Bean. Comme je teste la classe MyApplication qui est configurée avec une annotation, j’utilise AnnotationConfigApplicationContext et crée son objet dans la méthode setUp(). Le contexte est fermé dans la méthode tearDown(). Le code de la méthode test() consiste simplement à obtenir l’objet du composant du contexte et à le tester. Vous vous demandez peut-être comment le framework Spring réalise le câblage automatique et appelle les méthodes inconnues au framework Spring. Cela se fait grâce à l’utilisation intensive de la réflexion Java que nous pouvons utiliser pour analyser et modifier les comportements des classes à l’exécution.

Télécharger le projet Spring Dependency Injection

Téléchargez le projet d’injection de dépendance (DI) Spring exemple à partir de l’URL ci-dessus et jouez avec pour en apprendre davantage.

Source:
https://www.digitalocean.com/community/tutorials/spring-dependency-injection