it-swarm.com.de

Bohneninjektion in eine JPA @Entity

Ist es möglich, Beans in eine JPA @Entity mit Spring's Abhängigkeitsinjektion zu injizieren?

Ich habe versucht, @Autowire ServletContext zu verwenden, aber während der Server erfolgreich gestartet wurde, erhielt ich eine NullPointerException, als ich versuchte, auf die Bean-Eigenschaft zuzugreifen.

@Autowired
@Transient
ServletContext servletContext;
34
theblang

Sie können Abhängigkeiten in Objekte einfügen, die nicht vom Spring-Container verwaltet werden. Verwenden Sie hierzu @Configurable: http://static.springsource.org/spring/docs/3.2.x/spring-framework-reference/html/aop.html# aop-atconfigurable .

Wie Sie inzwischen erkannt haben, fügt Spring keine Abhängigkeiten in Objekte ein, die mit dem Operator new erstellt wurden, es sei denn, Sie verwenden @Configurable und die entsprechende AspectJ-Webkonfiguration. Tatsächlich fügt es keine Abhängigkeiten in Objekte ein, es sei denn, Sie haben sie aus ApplicationContext abgerufen, aus dem einfachen Grund, dass sie nichts über ihre Existenz weiß. Selbst wenn Sie Ihre Entität mit @Component annotieren, wird die Instantiierung dieser Entität immer noch durch eine new-Operation durchgeführt, entweder von Ihnen oder einem Framework wie Hibernate. Denken Sie daran, dass Annotationen nur Metadaten sind: Wenn diese Metadaten nicht interpretiert werden, fügen sie kein Verhalten hinzu und wirken sich nicht auf ein laufendes Programm aus.

Trotzdem rate ich dringend davon ab, einen ServletContext in eine Entität einzufügen. Entitäten sind Teil Ihres Domänenmodells und sollten von jedem Übermittlungsmechanismus, beispielsweise einer Servlet-basierten Webbereitstellungsschicht, entkoppelt sein. Wie werden Sie diese Entität verwenden, wenn auf einen Befehlszeilenclient oder auf etwas anderes zugegriffen wird, an dem kein ServletContext beteiligt ist? Sie sollten die erforderlichen Daten aus diesem ServletContext extrahieren und diese über herkömmliche Methodenargumente an Ihre Entität übergeben. Mit diesem Ansatz erreichen Sie ein viel besseres Design.

36
Spiff

Natürlich kannst du. Sie müssen lediglich sicherstellen, dass die Entität auch als verwalteter Spring-Bean registriert ist, entweder deklarativ mit <bean>-Tags (in einigen Spring-context.xml) oder durch Anmerkungen (siehe unten).

Mithilfe von Anmerkungen können Sie Ihre Entitäten entweder mit @Component (oder einem spezifischeren Stereotyp @Repository, der die automatische Ausnahmeübersetzung für DAOs aktiviert und JPA möglicherweise nicht unterstützt) markieren. 

@Entity
@Component
public class MyJAPEntity {

  @Autowired
  @Transient
  ServletContext servletContext;
  ...
}

Wenn Sie dies für Ihre Entitäten getan haben, müssen Sie ihr Paket (oder ein Vorfahrpaket) so konfigurieren, dass es von Spring gescannt wird, damit die Entitäten als Beans aufgenommen werden und ihre Abhängigkeiten automatisch verbunden werden.

<beans ... xmlns:context="..." >
  ...
  <context:component-scan base-package="pkg.of.your.jpa.entities" />
<beans>

EDIT: (was hat zuletzt funktioniert und warum)

  • ServletContextstatic machen. (Entfernen von @Autowired)

    @Transient
    private static ServletContext servletContext;
    

Da die JPA eine separate Entitätsinstanz erstellt, d. H. Keine verwaltete Spring-Bean verwendet, muss der context gemeinsam genutzt werden.

  • Hinzufügen einer @PostConstructinit()-Methode.

    @PostConstruct
    public void init() {
        log.info("Initializing ServletContext as [" +
                    MyJPAEntity.servletContext + "]");
    }
    

Dies löst init() aus, sobald die Entity instanziiert wurde und durch Bezugnahme auf ServletContext im Inneren die Injection auf die Eigenschaft static erzwungen wird, falls dies nicht bereits erfolgt ist.

  • Verschieben von @Autowired in eine instance -Methode, wobei jedoch das static -Feld darin festgelegt wird.

    @Autowired
    public void setServletContext(ServletContext servletContext) {
        MyJPAEntity.servletContext = servletContext;
    }
    

Ich zitiere meinen letzten Kommentar, um zu antworten, warum wir diese Phänomene einsetzen müssen:

Es gibt keine hübsche Möglichkeit, das zu tun, was Sie möchten, da JPA den Spring-Container nicht zum Instanziieren seiner Entitäten verwendet. Stellen Sie sich JPA als separaten ORM-Container vor, der den Lebenszyklus von Entitäten instanziiert und verwaltet (vollständig von Spring getrennt) und DI nur auf Entitätsbeziehungen basiert.

18
Ravi Thapliyal

Nach langer Zeit stolperte ich über diese SO Antwort die mich an eine elegante Lösung denken ließ:

  • Fügen Sie Ihren Entitäten alle @Transient @Autowired-Felder hinzu, die Sie benötigen
  • Erstellen Sie ein @Repository-DAO mit diesem automatisch verwendeten Feld: @Autowired private AutowireCapableBeanFactory autowirer;
  • Rufen Sie von Ihrem DAO nach dem Abrufen der Entität aus der Datenbank diesen automatisch verwendeten Wankcode auf: String beanName = fetchedEntity.getClass().getSimpleName(); autowirer.autowireBean(fetchedEntity); fetchedEntity = (FetchedEntity) autowirer.initializeBean(fetchedEntity, beanName);

Ihre Entität kann dann auf die automatisch genutzten Felder wie auf @Component zugreifen.

0
xtian