Injizieren von Spring Beans in nicht verwaltete Objekte

1. Antriebskräfte

In einer Spring-Anwendung ist das Injizieren einer Bohne in eine andere Bohne sehr häufig. Manchmal ist es jedoch wünschenswert, eine Bohne in ein gewöhnliches Objekt zu injizieren. Beispielsweise möchten wir möglicherweise Verweise auf Dienste aus einem Entitätsobjekt abrufen.

Glücklicherweise ist es nicht so schwer, dies zu erreichen, wie es aussehen mag. In den folgenden Abschnitten wird die Verwendung der Annotation @Configurable und eines AspectJ-Webers erläutert .

2. Die @Configurable Annotation

Mit dieser Annotation können Instanzen der dekorierten Klasse Verweise auf Spring Beans enthalten.

2.1. Definieren und Registrieren einer Spring Bean

Bevor wir die Annotation @Configurable behandeln , richten wir eine Spring Bean-Definition ein:

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

Diese Klasse ist mit der Annotation @Service versehen . Daher kann es über das Scannen von Komponenten in einem Spring-Kontext registriert werden.

Hier ist eine einfache Konfigurationsklasse, die diesen Mechanismus aktiviert:

@ComponentScan public class AspectJConfig { }

2.2. Verwenden von @Configurable

In seiner einfachsten Form können wir @Configurable ohne jedes Element verwenden:

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

In der Annotation @Configurable wird in diesem Fall die PersonObject- Klasse als für die Spring-gesteuerte Konfiguration geeignet markiert .

2.3. Injizieren einer Spring Bean in ein nicht verwaltetes Objekt

Wir können IdService in PersonObject einfügen , genau wie in jeder Spring Bean:

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

Eine Anmerkung ist jedoch nur dann nützlich, wenn sie von einem Handler erkannt und verarbeitet wird. Hier kommt der AspectJ-Weber ins Spiel. Insbesondere reagiert der AnnotationBeanConfigurerAspect auf das Vorhandensein von @Configurable und führt die erforderliche Verarbeitung durch.

3. Aktivieren von AspectJ Weaving

3.1. Plugin-Erklärung

Um das Weben von AspectJ zu ermöglichen, benötigen wir zuerst das AspectJ Maven-Plugin:

 org.codehaus.mojo aspectj-maven-plugin 1.11  

Und es erfordert einige zusätzliche Konfiguration:

 1.8 ignore   org.springframework spring-aspects   

Das erste erforderliche Element ist ComplianceLevel . Ein Wert von 1,8 setzt sowohl die Quell- als auch die Ziel-JDK-Version auf 1,8. Wenn nicht explizit festgelegt, wäre die Quellversion 1.3 und das Ziel 1.1. Diese Werte sind offensichtlich veraltet und für eine moderne Java-Anwendung nicht ausreichend.

Um eine Bean in ein nicht verwaltetes Objekt einzufügen , müssen wir uns auf die AnnotationBeanConfigurerAspect- Klasse verlassen, die in der Datei spring-aspects.jar bereitgestellt wird . Da dies ein vorkompilierter Aspekt ist, müssten wir das enthaltende Artefakt zur Plugin-Konfiguration hinzufügen.

Beachten Sie, dass ein solches referenziertes Artefakt als Abhängigkeit im Projekt vorhanden sein muss:

 org.springframework spring-aspects 5.2.7.RELEASE 

Wir finden die neueste Version der Frühlingsaspekte auf Maven Central.

3.2. Plugin-Ausführung

Um das Plugin anzuweisen, alle relevanten Klassen zu weben, benötigen wir diese Ausführungskonfiguration :

   compile   

Beachten Sie, dass das Kompilierungsziel des Plugins standardmäßig an die Phase des Kompilierungslebenszyklus gebunden ist.

3.2. Bean-Konfiguration

Der letzte Schritt zum Aktivieren des AspectJ-Webens besteht darin , der Konfigurationsklasse @EnableSpringConfigured hinzuzufügen :

@ComponentScan @EnableSpringConfigured public class AspectJConfig { }

Die zusätzliche Annotation konfiguriert AnnotationBeanConfigurerAspect , das wiederum PersonObject- Instanzen mit einem Spring IoC-Container registriert .

4. Testen

Lassen Sie uns nun überprüfen, ob die IdService- Bean erfolgreich in ein PersonObject eingefügt wurde :

@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. Injizieren einer Bean in eine JPA-Entität

Aus Sicht des Spring-Containers ist eine Entität nichts anderes als ein gewöhnliches Objekt. Daher ist es nichts Besonderes, eine Spring Bean in eine JPA-Entität zu injizieren.

Da das Injizieren in JPA-Entitäten ein typischer Anwendungsfall ist, wollen wir ihn genauer behandeln.

5.1. Entitätsklasse

Beginnen wir mit dem Skelett der Entitätsklasse:

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

Beachten Sie das Element preConstruction in der Annotation @Configurable : Es ermöglicht uns, eine Abhängigkeit in das Objekt einzufügen, bevor es vollständig erstellt ist.

5.2. Service-Injektion

Jetzt können wir IdService in PersonEntity einfügen , ähnlich wie bei 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

Dieses Tutorial hat den Prozess des Injizierens einer Spring Bean in ein nicht verwaltetes Objekt durchlaufen. Es wurde auch ein Entwurfsproblem erwähnt, das mit der Abhängigkeitsinjektion in Objekte verbunden ist.

Der Implementierungscode ist auf GitHub zu finden.