Injection de Spring Beans dans des objets non gérés

1. Forces motrices

Dans une application Spring, l'injection d'un bean dans un autre bean est très courante. Cependant, il est parfois souhaitable d'injecter un bean dans un objet ordinaire. Par exemple, nous pouvons souhaiter obtenir des références à des services à partir d'un objet entité.

Heureusement, y parvenir n'est pas aussi difficile qu'il y paraît. Les sections suivantes présenteront comment le faire en utilisant l' annotation @Configurable et un tisserand AspectJ.

2. L' annotation @Configurable

Cette annotation permet aux instances de la classe décorée de contenir des références aux beans Spring.

2.1. Définition et enregistrement d'un Spring Bean

Avant de couvrir l' annotation @Configurable , configurons une définition de Spring bean:

@Service public class IdService { private static int count; int generateId() { return ++count; } }

Cette classe est décorée de l' annotation @Service ; il peut donc être enregistré avec un contexte Spring via l'analyse des composants.

Voici une classe de configuration simple permettant ce mécanisme:

@ComponentScan public class AspectJConfig { }

2.2. Utiliser @Configurable

Dans sa forme la plus simple, nous pouvons utiliser @Configurable sans aucun élément:

@Configurable public class PersonObject { private int id; private String name; public PersonObject(String name) { this.name = name; } // getters and other code shown in the next subsection }

L' annotation @Configurable , dans ce cas, marque la classe PersonObject comme étant éligible pour la configuration Spring.

2.3. Injecter un Spring Bean dans un objet non géré

Nous pouvons injecter IdService dans PersonObject , comme nous le ferions dans n'importe quel bean Spring:

@Configurable public class PersonObject { @Autowired private IdService idService; // fields, constructor and getters - shown in the previous subsection void generateId() { this.id = idService.generateId(); } }

Cependant, une annotation n'est utile que si elle est reconnue et traitée par un gestionnaire. C'est là qu'intervient le tisserand AspectJ. Plus précisément, le AnnotationBeanConfigurerAspect agira sur la présence de @Configurable et fait le traitement nécessaire.

3. Activation du tissage AspectJ

3.1. Déclaration du plugin

Pour activer le tissage AspectJ, nous avons d'abord besoin du plugin AspectJ Maven:

 org.codehaus.mojo aspectj-maven-plugin 1.11  

Et cela nécessite une configuration supplémentaire:

 1.8 ignore   org.springframework spring-aspects   

Le premier élément requis est complianceLevel . Une valeur de 1,8 définit les versions JDK source et cible sur 1,8. Si elle n'est pas définie explicitement, la version source serait 1.3 et la cible serait 1.1. Ces valeurs sont évidemment obsolètes et insuffisantes pour une application Java moderne.

Pour injecter un bean dans un objet non managé, nous devons nous fier à la classe AnnotationBeanConfigurerAspect fournie dans le fichier spring-aspects.jar . Puisqu'il s'agit d'un aspect pré-compilé, nous aurions besoin d' ajouter l'artefact contenant à la configuration du plugin.

Notez qu'un tel artefact référencé doit exister en tant que dépendance dans le projet:

 org.springframework spring-aspects 5.2.7.RELEASE 

Nous pouvons trouver la dernière version de Spring-aspects sur Maven Central.

3.2. Exécution du plugin

Pour demander au plugin de tisser toutes les classes pertinentes, nous avons besoin de cette configuration d' exécutions :

   compile   

Notez que l' objectif de compilation du plugin se lie par défaut à la phase du cycle de vie de compilation.

3.2. Configuration du haricot

La dernière étape pour activer le tissage AspectJ consiste à ajouter @EnableSpringConfigured à la classe de configuration:

@ComponentScan @EnableSpringConfigured public class AspectJConfig { }

L'annotation supplémentaire configure AnnotationBeanConfigurerAspect , qui à son tour enregistre les instances PersonObject avec un conteneur Spring IoC.

4. Test

Maintenant, vérifions que le bean IdService a été correctement injecté dans un PersonObject :

@RunWith(SpringRunner.class) @ContextConfiguration(classes = AspectJConfig.class) public class PersonUnitTest { @Test public void givenUnmanagedObjects_whenInjectingIdService_thenIdValueIsCorrectlySet() { PersonObject personObject = new PersonObject("Baeldung"); personObject.generateId(); assertEquals(1, personObject.getId()); assertEquals("Baeldung", personObject.getName()); } }

5. Injection d'un bean dans une entité JPA

Du point de vue du conteneur Spring, une entité n'est rien d'autre qu'un objet ordinaire. En tant que tel, il n'y a rien de spécial à injecter un bean Spring dans une entité JPA.

Cependant, comme l'injection dans des entités JPA est un cas d'utilisation typique, couvrons-le plus en détail.

5.1. Classe d'entité

Commençons par le squelette de la classe d'entité:

@Entity @Configurable(preConstruction = true) public class PersonEntity { @Id private int id; private String name; public PersonEntity() { } // other code - shown in the next subsection }

Notez l' élément preConstruction dans l' annotation @Configurable : il nous permet d'injecter une dépendance dans l'objet avant qu'il ne soit entièrement construit.

5.2. Injection de service

Nous pouvons maintenant injecter IdService dans PersonEntity , comme nous l'avons fait avec PersonObject :

// annotations public class PersonEntity { @Autowired @Transient private IdService idService; // fields and no-arg constructor public PersonEntity(String name) { id = idService.generateId(); this.name = name; } // getters }

The @Transient annotation is used to tell JPA that idService is a field not to be persisted.

5.3. Test Method Update

Finally, we can update the test method to indicate that the service can be injected into the entity:

@Test public void givenUnmanagedObjects_whenInjectingIdService_thenIdValueIsCorrectlySet() { // existing statements PersonEntity personEntity = new PersonEntity("Baeldung"); assertEquals(2, personEntity.getId()); assertEquals("Baeldung", personEntity.getName()); }

6. Caveats

Although it's convenient to access Spring components from an unmanaged object, it's often not a good practice to do so.

The problem is that unmanaged objects, including entities, are usually part of the domain model. These objects should carry data only to be reusable across different services.

Injecting beans into such objects could tie components and objects together, making it harder to maintain and enhance the application.

7. Conclusion

Ce didacticiel a décrit le processus d'injection d'un bean Spring dans un objet non géré. Il a également mentionné un problème de conception associé à l'injection de dépendances dans les objets.

Le code d'implémentation se trouve à l'adresse over sur GitHub.