Jak wywołać metodę po zakończeniu inicjowania komponentu bean?

237

Mam przypadek użycia, w którym muszę wywołać metodę (niestatyczną) w komponencie bean tylko raz podczas ładowania ApplicationContext. Czy to w porządku, jeśli użyję do tego MethodInvokingFactoryBean? Czy mamy jakieś lepsze rozwiązanie?

Na marginesie, używam ConfigContextLoaderListener, aby załadować kontekst aplikacji w aplikacji internetowej. I chcę, aby jeśli utworzono instancję komponentu bean „A”, wystarczy wywołać metodęA () raz.

Jak można to zrobić ładnie?

szczyt
źródło

Odpowiedzi:

196

Możesz użyć czegoś takiego jak:

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

Spowoduje to wywołanie metody „init” po utworzeniu instancji komponentu bean.

Mercer Traieste
źródło
15
postConstruct powinien być jednak lepszy w większości przypadków, ponieważ nie chcemy zepsuć się inicjalizacją fasoli wiosennej.
lwpro2
4
@ lwpro2 Co rozumiesz przez „nie chcę zepsuć się inicjalizacją fasoli wiosennej” tutaj?
Yngve Sneen Lindal
@Mercer Traieste, co powinienem tutaj podać dla atrybutu klasy? Czy mogę podać tutaj klasę kontrolera?
KJEjava48,
314

Aby rozwinąć sugestię @PostConstruct w innych odpowiedziach, moim zdaniem jest to naprawdę najlepsze rozwiązanie.

  • Utrzymuje twój kod oddzielony od Spring API (@PostConstruct jest w javax. *)
  • Wyraźnie opisuje twoją metodę init jako coś, co należy wywołać w celu zainicjowania komponentu bean
  • Nie musisz pamiętać, aby dodać atrybut init-method do definicji fasoli wiosennej, wiosna automatycznie wywoła tę metodę (zakładając, że zarejestrujesz opcję adnotacji-konfiguracji gdzie indziej w kontekście).
skaffman
źródło
9
Dzięki, to działa. Uwaga: jeśli chcesz używać ze Springem, musisz dołączyć „<context: annotation-config />”, aby zarejestrować komponent CommonAnnotationBeanPostProcessor (jak wspomniano powyżej)
khylo
2
Odpowiedni <context:component-scan>również działa i może być przydatny w celu skrócenia czasu uruchamiania, jeśli masz duże biblioteki inne niż Spring na ścieżce klasy.
Donal Fellows
5
JavaDoc dla PostConstruct mówi, że można przypisać do niej tylko jedną metodę na klasę: docs.oracle.com/javaee/5/api/javax/annotation/…
Andrew Swan
@ PostConstruct nie działa z menedżerem transakcji, patrz: forum.spring.io/forum/spring-projects/data/…
mmm
2
@ PostConstruct również nie przyda ci się, gdy tworzona instancja fasoli nie jest klasą własną, ale klasą strony trzeciej
John Rix
102

Istnieją trzy różne podejścia do rozważenia, jak opisano w odnośniku

Użyj atrybutu metody init

Plusy:

  • Nie wymaga komponentu bean do implementacji interfejsu.

Cons:

  • Nie ma bezpośredniego wskazania, że ​​ta metoda jest wymagana po zakończeniu budowy, aby zapewnić prawidłowe skonfigurowanie fasoli.

Wdrożenie InitializingBean

Plusy:

  • Nie trzeba określać metody init ani włączać skanowania / przetwarzania adnotacji składników.
  • Odpowiedni dla ziaren dostarczanych z biblioteką, gdzie nie chcemy, aby aplikacja korzystająca z tej biblioteki zajmowała się cyklem życia fasoli.

Cons:

  • Bardziej inwazyjne niż podejście oparte na metodzie init.

Użyj adnotacji JSR-250 @PostConstruct Lifecyle

Plusy:

  • Przydatne przy użyciu skanowania komponentów do automatycznego wykrywania ziaren.
  • Wyjaśnia, że ​​do inicjalizacji należy zastosować określoną metodę. Cel jest bliższy kodowi.

Cons:

  • Inicjalizacja nie jest już centralnie określona w konfiguracji.
  • Musisz pamiętać o włączeniu przetwarzania adnotacji (które czasem można zapomnieć)
zestaw narzędzi
źródło
4
Myślę, że właściwie warto używać @PostConstructwłaśnie dlatego, że należy do klasy, która potrzebuje wywołania metody na końcu przetwarzania inicjalizacji.
Donal Fellows
Jeśli ta klasa NAPRAWDĘ tego potrzebuje i nie możesz tego zrobić w konstruktorze, to uważam, że to zapach kodu.
user482745,
39

Czy próbowałeś wdrożyć InitializingBean? Brzmi jak dokładnie to, czego szukasz.

Minusem jest to, że twoja fasola staje się świadoma wiosny, ale w większości aplikacji nie jest tak źle.

Jon Skeet
źródło
2
Czy istnieje powód, dla którego wolisz zaimplementować interfejs zamiast określać metodę init w pliku XML?
Mark
4
To kwestia gustu. Interfejs jest częścią modelu komponentu Spring i służy temu i tylko temu celowi, podczas gdy dla niestandardowej metody nazwanej może nie być tak naprawdę oczywiste, że należy ją wywołać w celu zakończenia cyklu życia komponentu. Służy to głównie komunikacji. Oczywiście z wadą wprowadzonej zależności w ramach Spring. Dobrym sposobem pomiędzy jest użycie @PostConstruct, ponieważ ma wyraźną semantykę, ale nie wprowadza zależności ...
Oliver Drotbohm
7
Oliver podaje mi kilka fajnych wymówek, ale tak naprawdę zapomniałem o metodzie init :) Innym powodem jest to, że sam typ wie, że należy go „skończyć” po ustawieniu wszystkich właściwości - to nie jest zasadniczo coś, co powinno być w konfiguracji.
Jon Skeet
8

Aby to zrobić, możesz wdrożyć niestandardowy procesor BeanPost w kontekście aplikacji. Lub jeśli nie masz nic przeciwko zaimplementowaniu interfejsu Spring w fasoli, możesz użyć interfejsu InitializingBean lub dyrektywy „init-method” (ten sam link).

Rob H.
źródło
Czy ktoś ma szczegółowe informacje na temat pisania BeanPostProcessor. Brzmi dokładnie tak, jak potrzebuję. Pozdrawiam :)
szczyt
Wiosenne statki z wieloma przykładami. Wystarczy spojrzeć na API JavaDoc dla BeanPostProcessor, a znajdziesz linki do wielu klas implementujących. Następnie spójrz na ich kod źródłowy.
Rob H
-7

Aby jeszcze bardziej usunąć wszelkie nieporozumienia dotyczące dwóch metod, tj. Zastosowania

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

Z własnego doświadczenia zdałem sobie sprawę, że użycie (1) działa tylko w kontenerze serwletu, podczas gdy (2) działa w dowolnym środowisku, nawet w aplikacjach komputerowych. Tak więc, jeśli używasz Springa w samodzielnej aplikacji, musisz użyć (2), aby wykonać to wywołanie tej metody po inicjalizacji.

Ayorinde
źródło
4
Technicznie @PostConstruct(w przypadku korzystania z aplikacji Spring) jest powiązany z długością życia kontekstu Springa. Takie konteksty mogą być używane we wszelkiego rodzaju aplikacjach.
Donal Fellows
Takiego zachowania się spodziewałem, ale nie działało dla mnie.
Ayorinde