Warum @PostConstruct verwenden?

294

Wird in einer verwalteten Bean @PostConstructnach dem regulären Java-Objektkonstruktor aufgerufen.

Warum sollte ich @PostConstructanstelle des regulären Konstruktors selbst die Initialisierung nach Bean verwenden?

Jan.
quelle
4
Ich hatte den Eindruck, dass die Konstruktorinjektion im Allgemeinen bevorzugt wurde, um Abhängigkeiten zuzulassen final. Warum wird @PostConstructJ2EE angesichts dieses Musters hinzugefügt - sie müssen sicherlich einen anderen Anwendungsfall gesehen haben?
mjaggard

Antworten:

409
  • Denn wenn der Konstruktor aufgerufen wird, ist die Bean noch nicht initialisiert - dh es werden keine Abhängigkeiten eingefügt. In der @PostConstructMethode ist die Bean vollständig initialisiert und Sie können die Abhängigkeiten verwenden.

  • weil dies der Vertrag ist, der garantiert, dass diese Methode nur einmal im Bean-Lebenszyklus aufgerufen wird. Es kann vorkommen (obwohl es unwahrscheinlich ist), dass eine Bean in ihrer internen Arbeitsweise mehrmals vom Container instanziiert wird, dies garantiert jedoch, dass @PostConstructsie nur einmal aufgerufen wird.

Bozho
quelle
17
Falls der Konstruktor selbst alle Abhängigkeiten automatisch verdrahtet, kann die Bean auch im Konstruktor vollständig initialisiert werden (nachdem alle automatisch verdrahteten Felder manuell festgelegt wurden).
yair
7
In welchem ​​Fall kann der Konstruktor einer Bohne mehrmals aufgerufen werden?
yair
1
Wahrscheinlich so etwas wie "Passivierung". Wenn der Container beschließt, die Bean auf dem Plattenspeicher zu speichern und von dort aus wiederherzustellen.
Bozho
13
Es ist nicht unwahrscheinlich, dass der Konstruktor mehrmals aufgerufen wird. Wenn der Container einen Proxy instanziiert, wird der Konstruktor mindestens einmal für den Proxy und einmal für die echte Bean aufgerufen.
Marcus
Beachten Sie, dass @ PostConstruct-Methoden nicht aufgerufen werden, wenn die Seite unmittelbar nach einem Neustart des Servers geladen wird. (Dies könnte ein JBoss-Fehler sein.)
Dave Jarvis
96

Das Hauptproblem ist:

In einem Konstruktor ist die Injektion der Abhängigkeiten noch nicht erfolgt *

* offensichtlich ohne Konstruktorinjektion


Beispiel aus der Praxis:

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
    }
}

WICHTIG : @PostConstructund @PreDestroy wurden in Java 11 vollständig entfernt .

Um sie weiterhin verwenden zu können, müssen Sie Ihren Abhängigkeiten die JAR javax.annotation-api hinzufügen .

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
quelle
19
in a constructor, the injection of the dependencies has not yet occurred. wahr mit Setter- oder Feldinjektion, aber nicht wahr mit Konstruktorinjektion.
Adam Siemion
Wie können wir jetzt mit Java 11 umgehen, wenn @PostConstruct in Java 11 entfernt wird?
Tet
@tet Wie in der Antwort erwähnt, müssen Sie die Bibliothek javax.annotation-api verwenden. Diese Anmerkungen wurden in Java 11 entfernt, aber bereits seit Java 9 als veraltet markiert.
narendra-choudhary
63

Wenn Ihre Klasse die gesamte Initialisierung im Konstruktor durchführt, @PostConstructist sie tatsächlich redundant.

Wenn jedoch die Abhängigkeiten Ihrer Klasse mithilfe von Setter-Methoden eingefügt wurden, kann der Konstruktor der Klasse das Objekt nicht vollständig initialisieren, und manchmal muss eine Initialisierung durchgeführt werden, nachdem alle Setter-Methoden aufgerufen wurden, daher der Anwendungsfall von @PostConstruct.

Skaffman
quelle
@ Personal: plus eins von meiner Seite. Wenn ich ein Eingabetextfeld mit einem aus der Datenbank abgerufenen Wert initialisieren möchte, kann ich dies mit Hilfe von PostConstruct tun, aber es schlägt fehl, wenn ich versuche, dasselbe im Konstruktor zu tun. Ich habe diese Anforderung, ohne die Verwendung von PostContruct zu initialisieren. Wenn Sie Zeit haben, können Sie diese bitte auch beantworten: stackoverflow.com/questions/27540573/…
Shirgill Farhan
10

Stellen Sie sich das folgende Szenario vor:

public class Car {
  @Inject
  private Engine engine;  

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

Da Car vor der Feldeinspritzung instanziiert werden muss, ist die Einspritzpunkt-Engine während der Ausführung des Konstruktors immer noch null, was zu einer NullPointerException führt.

Dieses Problem kann entweder durch JSR-330 Dependency Injection für die Java- Konstruktorinjektion oder durch JSR 250 Common Annotations für die Annotation der Java @ PostConstruct-Methode gelöst werden.

@ PostConstruct

JSR-250 definiert einen allgemeinen Satz von Anmerkungen, die in Java SE 6 enthalten sind.

Die PostConstruct-Annotation wird für eine Methode verwendet, die ausgeführt werden muss, nachdem die Abhängigkeitsinjektion durchgeführt wurde, um eine Initialisierung durchzuführen. Diese Methode MUSS aufgerufen werden, bevor die Klasse in Betrieb genommen wird. Diese Anmerkung MUSS für alle Klassen unterstützt werden, die die Abhängigkeitsinjektion unterstützen.

JSR-250 Kap. 2.5 javax.annotation.PostConstruct

Mit der Annotation @PostConstruct kann die Definition von Methoden ausgeführt werden, nachdem die Instanz instanziiert und alle Injects ausgeführt wurden.

public class Car {
  @Inject
  private Engine engine;  

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

Anstatt die Initialisierung im Konstruktor durchzuführen, wird der Code in eine mit @PostConstruct kommentierte Methode verschoben.

Bei der Verarbeitung von Post-Konstrukt-Methoden müssen einfach alle mit @PostConstruct kommentierten Methoden gefunden und nacheinander aufgerufen werden.

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);
        }
      });
}

Die Verarbeitung der Nachkonstruktionsmethoden muss nach Abschluss der Instanziierung und Injektion durchgeführt werden.

Humoyun Ahmad
quelle
1

Auch die konstruktorbasierte Initialisierung funktioniert nicht wie beabsichtigt, wenn eine Art Proxy oder Remoting erforderlich ist.

Der ct wird aufgerufen, wenn ein EJB deserialisiert wird und wenn ein neuer Proxy dafür erstellt wird ...

struberg
quelle