Dlaczego warto korzystać z @PostConstruct?

294

W zarządzanym komponencie bean @PostConstructjest wywoływany po zwykłym konstruktorze obiektów Java.

Dlaczego miałbym używać @PostConstructinicjowania za pomocą komponentu bean zamiast zwykłego konstruktora?

Jan
źródło
4
Mam wrażenie, że zastrzyk konstruktora był ogólnie preferowany, aby umożliwić zależności final. Biorąc pod uwagę ten wzorzec, dlaczego jest @PostConstructdodawany do J2EE - z pewnością widzieli inny przypadek użycia?
mjaggard,

Odpowiedzi:

409
  • ponieważ gdy wywoływany jest konstruktor, komponent bean nie jest jeszcze inicjowany - tzn. nie są wstrzykiwane żadne zależności. W @PostConstructmetodzie fasola jest w pełni inicjowana i można korzystać z zależności.

  • ponieważ jest to umowa gwarantująca, że ​​ta metoda zostanie wywołana tylko raz w cyklu życia komponentu bean. Może się zdarzyć (choć mało prawdopodobne), że fasola jest wielokrotnie inicjowana przez kontener w swoim wewnętrznym działaniu, ale gwarantuje, że @PostConstructzostanie wywołany tylko raz.

Bozho
źródło
17
w przypadku, gdy sam konstruktor automatycznie rejestruje wszystkie zależności - wówczas można również w pełni zainicjować komponent bean w konstruktorze (po ręcznym ustawieniu wszystkich pól autowiredowanych).
yair
7
jaki jest przypadek, w którym konstruktor komponentu bean można wywołać więcej niż jeden raz?
yair
1
Prawdopodobnie coś w rodzaju „pasywacji”. Jeśli kontener zdecyduje się zapisać komponent bean w magazynie dysków, a następnie przywróć go stamtąd.
Bozho,
13
Nie jest wykluczone, że konstruktor jest wywoływany wiele razy. Gdy kontener utworzy instancję proxy, zobaczysz, że konstruktor jest wywoływany co najmniej raz dla proxy i raz dla prawdziwego komponentu bean.
Marcus
Należy pamiętać, że metody @PostConstruct nie są wywoływane, gdy strona jest ładowana natychmiast po ponownym uruchomieniu serwera. (Może to być błąd JBoss.)
Dave Jarvis
96

Głównym problemem jest to, że:

w konstruktorze wstrzyknięcie zależności jeszcze nie nastąpiło *

* oczywiście z wyłączeniem wtrysku konstruktora


Przykład ze świata rzeczywistego:

public class Foo {

    @Inject
    Logger LOG;

    @PostConstruct
    public void fooInit(){
        LOG.info("This will be printed; LOG has already been injected");
    }

    public Foo() {
        LOG.info("This will NOT be printed, LOG is still null");
        // NullPointerException will be thrown here
    }
}

WAŻNE : @PostConstructi @PreDestroy zostały całkowicie usunięte w Javie 11 .

Aby nadal ich używać, musisz dodać plik JAR javax.annotation-api do swoich zależności.

Maven

<!-- https://mvnrepository.com/artifact/javax.annotation/javax.annotation-api -->
<dependency>
    <groupId>javax.annotation</groupId>
    <artifactId>javax.annotation-api</artifactId>
    <version>1.3.2</version>
</dependency>

Gradle

// https://mvnrepository.com/artifact/javax.annotation/javax.annotation-api
compile group: 'javax.annotation', name: 'javax.annotation-api', version: '1.3.2'
Andrea Ligios
źródło
19
in a constructor, the injection of the dependencies has not yet occurred. prawda z zastrzykiem setera lub pola, ale nie prawda z zastrzykiem konstruktora.
Adam Siemion
Jak usunięto @PostConstruct w Javie 11, jak możemy teraz obsługiwać ten przykład z Java 11?
tet
@tet Jak wspomniano w odpowiedzi, musisz użyć biblioteki javax.annotation-api. Te adnotacje zostały usunięte w Javie 11, ale zostały już oznaczone jako przestarzałe od Java 9.
narendra-choudhary
63

Jeśli twoja klasa wykonuje całą inicjalizację w konstruktorze, @PostConstructto rzeczywiście jest zbędna.

Jeśli jednak w twojej klasie wstrzyknięto zależności za pomocą metod ustawiających, wówczas konstruktor klasy nie może w pełni zainicjować obiektu, a czasem po zainicjowaniu wszystkich metod ustawiających należy wykonać pewną inicjalizację, stąd przypadek użycia @PostConstruct.

skaffman
źródło
@staffman: plus jeden z mojej strony. Jeśli chcę zainicjować pole tekstowe z wartością pobraną z bazy danych, mogę to zrobić za pomocą PostConstruct, ale nie powiedzie się, gdy spróbuję zrobić to samo w konstruktorze. Mam ten wymóg, aby zainicjować bez użycia PostContruct. Jeśli masz czas, czy możesz odpowiedzieć na to również: stackoverflow.com/questions/27540573/…
Shirgill Farhan
10

Rozważ następujący scenariusz:

public class Car {
  @Inject
  private Engine engine;  

  public Car() {
    engine.initialize();  
  }
  ...
}

Ponieważ Car musi zostać utworzony przed iniekcją w terenie, silnik punktu iniekcji jest nadal zerowy podczas wykonywania konstruktora, co powoduje wyjątek NullPointerException.

Ten problem można rozwiązać za pomocą wstrzykiwania zależności JSR-330 dla wstrzykiwania konstruktora Java lub zwykłych adnotacji JSR 250 dla adnotacji metody @PostConstruct w języku Java.

@PostConstruct

JSR-250 definiuje wspólny zestaw adnotacji, który został zawarty w Java SE 6.

Adnotacja PostConstruct jest używana w metodzie, którą należy wykonać po wykonaniu wstrzyknięcia zależności w celu wykonania dowolnej inicjalizacji. Ta metoda MUSI zostać wywołana przed oddaniem klasy do użytku. Ta adnotacja MUSI być obsługiwana we wszystkich klasach obsługujących wstrzykiwanie zależności.

JSR-250 Rozdz. 2.5 javax.annotation.PostConstruct

Adnotacja @PostConstruct pozwala na zdefiniowanie metod, które będą wykonywane po utworzeniu instancji i wykonaniu wszystkich wstrzyknięć.

public class Car {
  @Inject
  private Engine engine;  

  @PostConstruct
  public void postConstruct() {
    engine.initialize();  
  }
  ...
} 

Zamiast inicjowania w konstruktorze kod jest przenoszony do metody opatrzonej adnotacją @PostConstruct.

Przetwarzanie metod postkonstrukcyjnych polega na znalezieniu wszystkich metod opatrzonych adnotacją @PostConstruct i wywołaniu ich z kolei.

private  void processPostConstruct(Class type, T targetInstance) {
  Method[] declaredMethods = type.getDeclaredMethods();

  Arrays.stream(declaredMethods)
      .filter(method -> method.getAnnotation(PostConstruct.class) != null) 
      .forEach(postConstructMethod -> {
         try {
           postConstructMethod.setAccessible(true);
           postConstructMethod.invoke(targetInstance, new Object[]{});
        } catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException ex) {      
          throw new RuntimeException(ex);
        }
      });
}

Przetwarzanie metod postkonstrukcyjnych należy wykonać po zakończeniu tworzenia instancji i wstrzyknięcia.

Humoyun Ahmad
źródło
1

Inicjalizacja oparta na konstruktorach nie będzie działać zgodnie z przeznaczeniem, ilekroć w grę wchodzi jakieś proxy lub zdalne zarządzanie.

CT będzie wywoływany za każdym razem, gdy EJB zostanie zdezrializowany i za każdym razem, gdy zostanie dla niego stworzony nowy serwer proxy ...

Struberg
źródło