2013-05-09 10 views
29

Ist es möglich, Bohnen zu einem JPA @Entity mit Spring Abhängigkeit Injektion injizieren?Bean Injektion in eine JPA @Entity

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

@Autowired 
@Transient 
ServletContext servletContext; 
+0

Ich hatte auch die gleiche Art von Problem beim injizieren EntityManager in einen EntityListener und fand eine Lösung und habe es auf einem anderen Beitrag beantwortet http://stackoverflow.com/questions/22171221/how-to-inject-entitymanager-in -entitylisteners/42222592 # 42222592 –

Antwort

31

Sie können Abhängigkeiten in Objekte injizieren nicht durch die Feder Container verwaltet mit @Configurable wie hier erklärt: http://static.springsource.org/spring/docs/3.2.x/spring-framework-reference/html/aop.html#aop-atconfigurable.

Wie Sie bereits festgestellt haben, injiziert Spring keine Abhängigkeiten in Objekte, die mit dem Operator new erstellt wurden, es sei denn, Sie verwenden die @Configurable und die entsprechende AspectJ Webkonfiguration. Tatsächlich injiziert es keine Abhängigkeiten in Objekte, es sei denn, Sie haben sie aus dem ApplicationContext abgerufen, aus dem einfachen Grund, dass es einfach nicht über ihre Existenz weiß. Selbst wenn Sie Ihre Entität mit @Component annotieren, wird die Instanziierung dieser Entität immer noch von einer new-Operation durchgeführt, entweder von Ihnen oder einem Framework wie Hibernate. Denken Sie daran, dass Anmerkungen nur Metadaten sind: Wenn niemand diese Metadaten interpretiert, fügt er kein Verhalten hinzu oder hat Auswirkungen auf ein laufendes Programm.

Alles was gesagt wird, rate ich dringend davon ab, eine ServletContext in eine Entität zu injizieren. Entitäten sind Teil Ihres Domänenmodells und sollten von einem beliebigen Bereitstellungsmechanismus, z. B. einem Servlet-basierten Web-Delivery-Layer, entkoppelt sein. Wie verwenden Sie diese Entität, wenn auf sie über einen Befehlszeilenclient oder etwas anderes zugegriffen wird, das keinen ServletContext enthält? Sie sollten die erforderlichen Daten aus diesem ServletContext extrahieren und sie über traditionelle Methodenargumente an Ihre Entität übergeben. Sie werden durch diesen Ansatz ein viel besseres Design erreichen.

+2

Beide Antworten auf diese Frage sind ein perfektes Beispiel für einen nützlichen SO-Thread (ich habe beides hochgestuft). Während @RaviThapliyals Antwort unten eine Möglichkeit bietet, das Ergebnis zu erzielen, liefert Ihre Antwort einen kontextuellen Hintergrund (für mich war es eine "Aha" -Erfahrung, die dazu beitrug, alle theoretischen Informationen über Spring's DI, die ich in den letzten Wochen gelesen habe, zu transformieren in praktisch anwendbares Wissen). Imo kann es nicht genug von solchen Posts auf SO geben, weil sie tatsächlich etwas lehren (und in den meisten Fällen nicht viel Rep. Verdienen). – sthzg

17

Ja, natürlich können Sie. Sie müssen nur sicherstellen, dass die Entität auch als eine Spring-verwaltete Bean entweder deklarativ unter Verwendung von <bean> Tags (in einigen spring-context.xml) oder durch Anmerkungen wie unten angezeigt wird.

Mithilfe von Anmerkungen können Sie entweder Ihre Entitäten mit @Component markieren (oder einen spezifischeren Stereotyp @Repository, der eine automatische Ausnahmeübersetzung für DAOs ermöglicht und möglicherweise JPA beeinträchtigt).

@Entity 
@Component 
public class MyJAPEntity { 

    @Autowired 
    @Transient 
    ServletContext servletContext; 
    ... 
} 

Sobald Sie das für Ihre Entitäten Sie ihr Paket (oder ein Vorfahre Paket) konfigurieren müssen getan haben für von Spring gescannt werden, so dass die Einheiten sich wie Bohnen und deren Abhängigkeiten verdrahtet bekommen Auto abgeholt bekommen.

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

EDIT: (was schließlich arbeitete und warum)

  • Making the ServletContextstatische. (Entfernen @Autowired)

    @Transient 
    private static ServletContext servletContext; 
    

Da ist JPA eine separate Einheit Instanz das heißt nicht mit der Spring-Bean verwaltet, ist es für den Kontext erforderlich geteilt werden.

  • Hinzufügen eines @PostConstructinit() Methode.

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

Dieser feuert init(), sobald das Entity instanziiert wurde und durch ServletContext innen Referenzierung zwingt sie die Injektion auf der statischen Eigenschaft, wenn nicht bereits injiziert.

  • Verschieben @Autowired zu einer Instanz Methode, aber die statische Feld innerhalb Einstellung.

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

Zitiert meinen letzten Kommentar unten zu beantworten, warum müssen wir diese Spielereien beschäftigen:

Es gibt keine hübsche Weise zu tun, was Sie wollen, da JPA nicht die Federbehälter nicht verwendet um seine Entitäten zu instantiieren. Stellen Sie sich JPA als einen separaten ORM-Container vor, der den Lebenszyklus von Entitäten instanziiert und verwaltet (vollständig getrennt von Spring) und DI ausschließlich auf Entitätsbeziehungen basiert.

+0

Leider ist 'servletContext' nach dem Hinzufügen von' @ Component' zur Entity und '' nach 'app-config.xml' immer noch null. – theblang

+0

Machen Sie es statisch. 'private static ServletContext servletContext;' Wenn es immer noch 'null' ist, erzwinge die Injektion, indem man ein '@PostConstruct public void init() {log.info ("ServletContext als [ServletContext initialisieren] [" + MyJPAEntity.servletContext + "]"); } 'Hoffe das hilft. –

+0

Es ist immer noch null, nachdem es "statisch" gemacht wurde. Ich denke, ich bin verwirrt darüber, was das @ PostConstruct tut. – theblang

0

Nach einer langen Zeit, die ich über this SO answer gestolpert, die mich an eine elegante Lösung denken ließ:

  • auf Ihre Entitäten hinzufügen alle @Transient @Autowired Felder, die Sie benötigen
  • Machen Sie eine @Repository DAO mit diesem autowired Feld: @Autowired private AutowireCapableBeanFactory autowirer;
  • von Ihrem DAO, nachdem das Unternehmen von DB holen, rufen Sie diesen autowiring Code: String beanName = fetchedEntity.getClass().getSimpleName(); autowirer.autowireBean(fetchedEntity); fetchedEntity = (FetchedEntity) autowirer.initializeBean(fetchedEntity, beanName);

Ihre Entity wird dann in der Lage sein, wie jede @Component Dose auf die autowired Felder zuzugreifen.

Verwandte Themen