web-dev-qa-db-fra.com

Pourquoi utiliser @PostConstruct?

Dans un bean géré, @PostConstruct est appelé après le constructeur d'objet Java standard.

Pourquoi devrais-je utiliser @PostConstruct pour initialiser par bean au lieu du constructeur standard lui-même?

261
Jan
  • parce que lorsque le constructeur est appelé, le bean n’est pas encore initialisé - c’est-à-dire qu’aucune dépendance n’est injectée. Dans la méthode @PostConstruct, le bean est complètement initialisé et vous pouvez utiliser les dépendances.

  • car c’est le contrat qui garantit que cette méthode ne sera invoquée qu’une fois dans le cycle de vie du bean. Il peut arriver (bien que cela soit peu probable) qu'un bean soit instancié plusieurs fois par le conteneur dans son fonctionnement interne, mais cela garantit que @PostConstruct ne sera invoqué qu'une seule fois.

373
Bozho

Le problème principal est le suivant:

dans un constructeur, l'injection des dépendances n'a pas encore eu lieu *

* évidemment à l'exclusion de l'injection de constructeur


Exemple du monde réel:

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

IMPORTANT: @PostConstruct et @PreDestroy ont été complètement supprimé dans Java 11.

Pour continuer à les utiliser, vous devez ajouter le fichier JAR javax.annotation-api à vos dépendances.

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'
76
Andrea Ligios

Si votre classe effectue toutes ses initialisations dans le constructeur, alors @PostConstruct est en effet redondant.

Cependant, si les dépendances de votre classe sont injectées à l'aide de méthodes setter, alors le constructeur de la classe ne peut pas initialiser complètement l'objet, et parfois une certaine initialisation doit être effectuée après l'appel de toutes les méthodes setter, d'où le cas d'utilisation de @PostConstruct. .

52
skaffman

Considérez le scénario suivant:

public class Car {
  @Inject
  private Engine engine;  

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

Etant donné que Car doit être instancié avant l'injection sur le terrain, le moteur du point d'injection est toujours nul pendant l'exécution du constructeur, ce qui entraîne une exception NullPointerException.

Ce problème peut être résolu soit par , soit par injection de constructeur JSR-330 Dependency Injection pour Java , soit par annotation commune JSR 250 pour l'annotation de méthode Java @PostConstruct.

@ PostConstruct

JSR-250 définit un ensemble commun d'annotations qui a été inclus dans Java SE 6.

L'annotation PostConstruct est utilisée sur une méthode qui doit être exécutée après l'injection de dépendance pour effectuer toute initialisation. Cette méthode DOIT être invoquée avant la mise en service de la classe. Cette annotation DOIT être prise en charge sur toutes les classes prenant en charge l'injection de dépendances.

JSR-250 Chap. 2.5 javax.annotation.PostConstruct

L'annotation @PostConstruct permet de définir les méthodes à exécuter après l'instanciation de l'instance et l'exécution de toutes les injections.

public class Car {
  @Inject
  private Engine engine;  

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

Au lieu d'effectuer l'initialisation dans le constructeur, le code est déplacé vers une méthode annotée avec @PostConstruct.

Le traitement des méthodes post-construction consiste à trouver toutes les méthodes annotées avec @PostConstruct et à les invoquer à leur tour.

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

Le traitement des méthodes post-construction doit être effectué une fois l’instanciation et l’injection terminées.

6
Humoyun Ahmad

En outre, l'initialisation basée sur le constructeur ne fonctionnera pas comme prévu lorsqu'un type de proxy ou d'accès distant est impliqué.

Le ct sera appelé chaque fois qu'un EJB sera désérialisé, et chaque fois qu'un nouveau proxy sera créé pour celui-ci ...

1
struberg