Wie rufe ich eine Methode auf, nachdem die Bean-Initialisierung abgeschlossen ist?

237

Ich habe einen Anwendungsfall, in dem ich eine (nicht statische) Methode in der Bean nur einmal beim Laden von ApplicationContext aufrufen muss. Ist es in Ordnung, wenn ich dafür MethodInvokingFactoryBean verwende? Oder haben wir eine bessere Lösung?

Als Randnotiz verwende ich ConfigContextLoaderListener, um den Anwendungskontext in eine Webanwendung zu laden. Und wenn Bean 'A' instanziiert wird, rufen Sie einfach einmal methodA () auf.

Wie kann das gut gemacht werden?

Peakit
quelle

Antworten:

197

Sie können etwas verwenden wie:

<beans>
    <bean id="myBean" class="..." init-method="init"/>
</beans>

Dies ruft die "init" -Methode auf, wenn die Bean instanziiert wird.

Mercer Traieste
quelle
15
postConstruct sollte jedoch in den meisten Fällen besser sein, da wir die Spring Bean-Initialisierung nicht durcheinander bringen wollen.
lwpro2
4
@ lwpro2 Was meinst du hier mit "willst du die Spring Bean Initialisierung nicht durcheinander bringen"?
Yngve Sneen Lindal
@ Mercer Traieste Was soll ich hier für das Klassenattribut geben? Kann ich hier die Controller-Klasse angeben?
KJEjava48
313

Um den @ PostConstruct-Vorschlag in anderen Antworten zu erweitern, ist dies meiner Meinung nach wirklich die beste Lösung.

  • Dadurch bleibt Ihr Code von der Spring-API entkoppelt (@PostConstruct befindet sich in Javax. *)
  • Es kommentiert Ihre init-Methode explizit als etwas, das aufgerufen werden muss, um die Bean zu initialisieren
  • Sie müssen nicht daran denken, das Attribut init-method zu Ihrer Spring-Bean-Definition hinzuzufügen. Spring ruft die Methode automatisch auf (vorausgesetzt, Sie registrieren die Option annotation-config ohnehin an einer anderen Stelle im Kontext).
Skaffman
quelle
9
Danke, das funktioniert. Hinweis: Wenn Sie Spring verwenden möchten, müssen Sie "<context: annotation-config />" angeben, um die CommonAnnotationBeanPostProcessor-Bean (wie oben erwähnt) zu registrieren
khylo
2
Ein geeignetes <context:component-scan>funktioniert auch und kann nützlich sein, um die Startzeit zu verkürzen, wenn Sie große Nicht-Spring-Bibliotheken in Ihrem Klassenpfad haben.
Donal Fellows
5
Das JavaDoc für PostConstruct besagt, dass pro Klasse nur eine Methode mit Anmerkungen versehen werden kann: docs.oracle.com/javaee/5/api/javax/annotation/…
Andrew Swan
@ PostConstruct funktioniert nicht mit einem Transaktionsmanager, siehe: forum.spring.io/forum/spring-projects/data/…
mmm
2
@ PostConstruct wird Ihnen auch nicht viel nützen, wenn die Bean, die Sie instanziieren, keine eigene Klasse ist, sondern eine Klasse von Drittanbietern
John Rix
102

Es sind drei verschiedene Ansätze zu berücksichtigen, wie in der Referenz beschrieben

Verwenden Sie das Attribut init-method

Vorteile:

  • Benötigt keine Bean, um eine Schnittstelle zu implementieren.

Nachteile:

  • Keine sofortige Angabe Diese Methode ist nach der Erstellung erforderlich, um sicherzustellen, dass die Bean korrekt konfiguriert ist.

Implementieren Sie InitializingBean

Vorteile:

  • Sie müssen weder die Init-Methode angeben noch die Verarbeitung von Komponenten-Scans / Anmerkungen aktivieren.
  • Geeignet für Beans, die mit einer Bibliothek geliefert werden, bei denen die Anwendung, die diese Bibliothek verwendet, sich nicht mit dem Bean-Lebenszyklus befassen soll.

Nachteile:

  • Invasiver als der Ansatz der Init-Methode.

Verwenden Sie die Lebenszyklusanmerkung JSR-250 @PostConstruct

Vorteile:

  • Nützlich bei der Verwendung des Komponentenscans zur automatischen Erkennung von Beans.
  • Verdeutlicht, dass für die Initialisierung eine bestimmte Methode verwendet werden soll. Absicht ist näher am Code.

Nachteile:

  • Die Initialisierung ist in der Konfiguration nicht mehr zentral festgelegt.
  • Sie müssen daran denken, die Anmerkungsverarbeitung zu aktivieren (was manchmal vergessen werden kann).
Toolkit
quelle
4
Ich denke, es ist eigentlich eine gute Sache, @PostConstructgenau weil es Teil der Klasse ist, dass es die Methode benötigt, die am Ende der Initialisierungsverarbeitung aufgerufen wird.
Donal Fellows
Wenn diese Klasse es WIRKLICH braucht und Sie es nicht im Konstruktor tun können, dann halte ich es für Code-Geruch.
Benutzer482745
39

Haben Sie versucht zu implementieren InitializingBean? Es klingt genau so, wie Sie es suchen.

Der Nachteil ist, dass Ihre Bohne frühlingsbewusst wird, aber in den meisten Anwendungen ist das nicht so schlimm.

Jon Skeet
quelle
2
Gibt es einen Grund, warum Sie die Implementierung der Schnittstelle anstelle der Angabe einer Init-Methode im XML wählen würden?
Mark
4
Das ist Geschmackssache. Die Schnittstelle ist Teil des Spring-Komponentenmodells und dient nur diesem Zweck, während es für eine benutzerdefinierte benannte Methode möglicherweise nicht wirklich offensichtlich ist, dass sie aufgerufen werden muss, um den Komponentenlebenszyklus abzuschließen. Das dient also hauptsächlich der Kommunikation. Natürlich mit dem Nachteil der eingeführten Abhängigkeit vom Spring Framework. Ein guter Weg dazwischen ist die Verwendung von @PostConstruct, da es eine klare Semantik hat, aber die Abhängigkeit nicht einführt ...
Oliver Drotbohm
7
Oliver gibt mir ein paar nette Ausreden, aber ich hatte die Init-Methode wirklich einfach vergessen :) Ein weiterer Grund ist, dass der Typ selbst weiß, dass er "fertig" sein muss, nachdem alle Eigenschaften festgelegt wurden - ist es nicht im Grunde etwas , das sollte in der Konfiguration sein.
Jon Skeet
8

Sie können dazu einen benutzerdefinierten BeanPostProcessor in Ihrem Anwendungskontext bereitstellen . Wenn es Ihnen nichts ausmacht, eine Spring-Schnittstelle in Ihrer Bean zu implementieren, können Sie die InitializingBean- Schnittstelle oder die Direktive "init-method" (gleicher Link) verwenden.

Rob H.
quelle
Hat jemand Details zum Schreiben eines BeanPostProcessors. Das klingt genau das, was ich brauche. Prost :)
Höhepunkt
Frühlingsschiffe mit vielen Beispielen. Schauen Sie sich einfach die JavaDoc-API für BeanPostProcessor an und Sie finden Links zu vielen implementierenden Klassen. Dann schauen Sie sich den Quellcode für sie an.
Rob H
-7

Um weitere Verwirrung über die beiden Ansätze zu beseitigen, dh die Verwendung von

  1. @PostConstruct und
  2. init-method="init"

Aus persönlicher Erfahrung wurde mir klar, dass die Verwendung von (1) nur in einem Servlet-Container funktioniert, während (2) in jeder Umgebung funktioniert, selbst in Desktop-Anwendungen. Wenn Sie Spring in einer eigenständigen Anwendung verwenden würden, müssten Sie (2) verwenden, um diesen "Aufruf dieser Methode nach der Initialisierung" auszuführen.

Ayorinde
quelle
4
Technisch gesehen ist @PostConstruct(in einer Spring-basierten App) an die Lebensdauer des Spring-Kontexts gebunden. Solche Kontexte können in allen Arten von Anwendungen verwendet werden.
Donal Fellows
Das war das Verhalten, das ich erwartet hatte, aber bei mir nicht funktionierte.
Ayorinde