Iniezione di fagioli primaverili in oggetti non gestiti

1. Forze motrici

In un'applicazione Spring, l'iniezione di un fagiolo in un altro è molto comune. Tuttavia, a volte è desiderabile iniettare un bean in un oggetto normale. Ad esempio, potremmo voler ottenere riferimenti a servizi dall'interno di un oggetto entità.

Fortunatamente, ottenerlo non è così difficile come potrebbe sembrare. Le sezioni seguenti illustreranno come farlo utilizzando l' annotazione @Configurable e un tessitore AspectJ.

2. L' annotazione @Configurable

Questa annotazione consente alle istanze della classe decorata di contenere riferimenti ai bean Spring.

2.1. Definizione e registrazione di un fagiolo di primavera

Prima di trattare l' annotazione @Configurable , impostiamo una definizione di bean Spring:

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

Questa classe è decorata con l' annotazione @Service ; quindi può essere registrato con un contesto Spring tramite la scansione dei componenti.

Ecco una semplice classe di configurazione che abilita quel meccanismo:

@ComponentScan public class AspectJConfig { }

2.2. Utilizzando @Configurable

Nella sua forma più semplice, possiamo usare @Configurable senza alcun elemento:

@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' annotazione @Configurable , in questo caso, contrassegna la classe PersonObject come idonea per la configurazione guidata da Spring.

2.3. Iniettare uno Spring Bean in un oggetto non gestito

Possiamo iniettare IdService in PersonObject , proprio come faremmo in qualsiasi 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(); } }

Tuttavia, un'annotazione è utile solo se riconosciuta ed elaborata da un gestore. È qui che entra in gioco AspectJ weaver. Specificamente, l'AnnotationBeanConfigurerAspect agirà sulla presenza di @Configurable e fa elaborazione necessaria.

3. Abilitazione della tessitura AspectJ

3.1. Dichiarazione del plugin

Per abilitare la tessitura AspectJ, abbiamo prima bisogno del plugin AspectJ Maven:

 org.codehaus.mojo aspectj-maven-plugin 1.11  

E richiede una configurazione aggiuntiva:

 1.8 ignore   org.springframework spring-aspects   

Il primo elemento richiesto è complianceLevel . Un valore di 1,8 imposta sia la versione JDK di origine che quella di destinazione su 1.8. Se non impostato esplicitamente, la versione di origine sarebbe 1.3 e la destinazione sarebbe 1.1. Questi valori sono ovviamente obsoleti e non sufficienti per una moderna applicazione Java.

Per iniettare un bean in un oggetto non gestito, dobbiamo fare affidamento sulla classe AnnotationBeanConfigurerAspect fornita in spring -aspects.jar . Poiché si tratta di un aspetto precompilato, avremmo bisogno di aggiungere l'artefatto contenitore alla configurazione del plugin.

Si noti che un tale artefatto di riferimento deve esistere come dipendenza nel progetto:

 org.springframework spring-aspects 5.2.7.RELEASE 

Possiamo trovare l'ultima versione degli aspetti primaverili su Maven Central.

3.2. Esecuzione del plugin

Per istruire il plugin a tessere tutte le classi rilevanti, abbiamo bisogno di questa configurazione delle esecuzioni :

   compile   

Notare che l' obiettivo di compilazione del plug-in si lega per impostazione predefinita alla fase del ciclo di vita della compilazione.

3.2. Configurazione Bean

L'ultimo passaggio per abilitare la tessitura AspectJ è aggiungere @EnableSpringConfigured alla classe di configurazione:

@ComponentScan @EnableSpringConfigured public class AspectJConfig { }

L'annotazione aggiuntiva configura AnnotationBeanConfigurerAspect , che a sua volta registra le istanze di PersonObject con un contenitore Spring IoC.

4. Test

Ora, verifichiamo che il bean IdService sia stato iniettato con successo in 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. Iniezione di un fagiolo in un'entità JPA

Dal punto di vista del contenitore Spring, un'entità non è altro che un oggetto ordinario. In quanto tale, non c'è niente di speciale nell'iniezione di un bean Spring in un'entità JPA.

Tuttavia, poiché l'iniezione nelle entità JPA è un caso d'uso tipico, copriamolo più in dettaglio.

5.1. Classe di entità

Cominciamo con lo scheletro della classe di entità:

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

Notare l' elemento preConstruction nell'annotazione @Configurable : ci consente di iniettare una dipendenza nell'oggetto prima che sia completamente costruito.

5.2. Iniezione di servizi

Ora possiamo iniettare IdService in PersonEntity , in modo simile a quello che abbiamo fatto con 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

Questo tutorial ha illustrato il processo di iniezione di un bean Spring in un oggetto non gestito. Ha anche menzionato un problema di progettazione associato all'inserimento di dipendenze negli oggetti.

Il codice di implementazione può essere trovato su GitHub.