Perché usare @PostConstruct?


294

In un bean gestito, @PostConstructviene chiamato dopo il normale costruttore di oggetti Java.

Perché dovrei usare @PostConstructper inizializzare per bean, invece del normale costruttore stesso?


4
Ho avuto l'impressione che l'iniezione del costruttore fosse generalmente preferita per consentire le dipendenze final. Dato questo schema, perché viene @PostConstructaggiunto a J2EE - devono aver visto sicuramente un altro caso d'uso?
Mjaggard,

Risposte:


409
  • perché quando viene chiamato il costruttore, il bean non è ancora inizializzato, ovvero non vengono iniettate dipendenze. Nel @PostConstructmetodo il bean è completamente inizializzato ed è possibile utilizzare le dipendenze.

  • perché questo è il contratto che garantisce che questo metodo verrà invocato una sola volta nel ciclo di vita del bean. Può accadere (sebbene improbabile) che un bean sia istanziato più volte dal contenitore nel suo funzionamento interno, ma garantisce che @PostConstructverrà invocato una sola volta.


17
nel caso in cui il costruttore stesso autorizzi tutte le dipendenze - il bean può anche essere completamente inizializzato nel costruttore (dopo aver impostato manualmente tutti i campi autowired).
yair,

7
qual è il caso in cui il costruttore di un bean può essere chiamato più di una volta?
yair,

1
Probabilmente qualcosa come "passivazione". Se il contenitore decide di archiviare il bean nell'archivio disco e ripristinarlo da lì.
Bozho,

13
Non è così improbabile vedere il costruttore chiamato più volte. Quando il contenitore crea un'istanza di un proxy, vedrai che il costruttore viene chiamato almeno una volta per il proxy e una volta per il bean reale.
marcus

Si noti che i metodi @PostConstruct non vengono chiamati quando la pagina viene caricata immediatamente dopo il riavvio del server. (Potrebbe trattarsi di un bug di JBoss.)
Dave Jarvis,

96

Il problema principale è che:

in un costruttore, l'iniezione delle dipendenze non è ancora avvenuta *

* ovviamente esclusa l'iniezione del costruttore


Esempio nel mondo reale:

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

IMPORTANTE : @PostConstructe @PreDestroy sono stati completamente rimossi in Java 11 .

Per continuare a usarli, devi aggiungere il JAR javax.annotation-api alle tue dipendenze.

Esperto di

<!-- 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'

19
in a constructor, the injection of the dependencies has not yet occurred. vero con l'iniezione del setter o del campo, ma non vero con l'iniezione del costruttore.
Adam Siemion,

Con @PostConstruct rimosso in Java 11, come possiamo ora gestire questo esempio del mondo reale con Java 11?
tet

@tet Come indicato nella risposta, è necessario utilizzare la libreria javax.annotation-api. Queste annotazioni sono state rimosse in Java 11, ma erano già state contrassegnate come obsolete da Java 9.
narendra-choudhary

63

Se la tua classe esegue tutta la sua inizializzazione nel costruttore, allora @PostConstructè davvero ridondante.

Tuttavia, se la tua classe ha le sue dipendenze iniettate usando i metodi setter, allora il costruttore della classe non può inizializzare completamente l'oggetto, e qualche volta l'inizializzazione deve essere eseguita dopo che tutti i metodi setter sono stati chiamati, quindi il caso d'uso di @PostConstruct.


@staffman: più uno dalla mia parte. Se desidero inizializzare un campo inputtext con un valore recuperato dal database, sono in grado di farlo con l'aiuto di PostConstruct, ma fallisco quando provo a fare lo stesso all'interno del costruttore. Ho questo requisito per inizializzare senza l'uso di PostContruct. Se avete tempo, potete per favore rispondere a questa domanda anche: stackoverflow.com/questions/27540573/...
Shirgill Farhan

10

Considera il seguente scenario:

public class Car {
  @Inject
  private Engine engine;  

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

Poiché Car deve essere istanziato prima dell'iniezione sul campo, il motore del punto di iniezione è ancora nullo durante l'esecuzione del costruttore, con conseguente NullPointerException.

Questo problema può essere risolto tramite JSR-330 Dependency Injection per l' iniezione del costruttore Java o JSR 250 Common Annotations per l'annotazione del metodo Java @PostConstruct.

@PostConstruct

JSR-250 definisce un insieme comune di annotazioni che è stato incluso in Java SE 6.

L'annotazione PostConstruct viene utilizzata su un metodo che deve essere eseguito dopo l'iniezione di dipendenza per eseguire qualsiasi inizializzazione. Questo metodo DEVE essere invocato prima che la classe venga messa in servizio. Questa annotazione DEVE essere supportata su tutte le classi che supportano l'iniezione delle dipendenze.

JSR-250 Cap. 2.5 javax.annotation.PostConstruct

L'annotazione @PostConstruct consente di definire i metodi da eseguire dopo che l'istanza è stata istanziata e tutti gli iniezioni sono stati eseguiti.

public class Car {
  @Inject
  private Engine engine;  

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

Invece di eseguire l'inizializzazione nel costruttore, il codice viene spostato in un metodo annotato con @PostConstruct.

L'elaborazione dei metodi post-costruzione è una semplice questione di trovare tutti i metodi annotati con @PostConstruct e invocarli a loro volta.

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

L'elaborazione dei metodi post-costruzione deve essere eseguita dopo che l'istanza e l'iniezione sono state completate.


1

Anche l'inizializzazione basata sul costruttore non funzionerà come previsto ogni volta che è coinvolto un qualche tipo di proxy o remoting.

Il ct verrà chiamato ogni volta che un bean viene deserializzato e ogni volta che viene creato un nuovo proxy per esso ...

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.