Зачем использовать @PostConstruct? - PullRequest
256 голосов
/ 04 августа 2010

В управляемом компоненте @PostConstruct вызывается после обычного конструктора Java-объекта.

Почему я должен использовать @PostConstruct для инициализации с помощью компонента, а не самого обычного конструктора?

Ответы [ 5 ]

365 голосов
/ 04 августа 2010
  • , потому что, когда вызывается конструктор, бин еще не инициализирован, т.е. не вводятся зависимости.В методе @PostConstruct компонент полностью инициализирован, и вы можете использовать зависимости.

  • , поскольку этот контракт гарантирует, что этот метод будет вызываться только один раз в жизненном цикле компонента.Может случиться (хотя и маловероятно), что контейнер несколько раз создается экземпляром контейнера во время его внутренней работы, но это гарантирует, что @PostConstruct будет вызван только один раз.

73 голосов
/ 27 ноября 2014

Проблема main заключается в том, что:

в конструкторе внедрение зависимостей еще не произошло *

*, очевидно, за исключением инжектора конструктора


Пример из реального мира:

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

ВАЖНО : @PostConstruct и @PreDestroy были полностью удалены в Java 11 .

Чтобы продолжать использовать их, вам нужно добавить javax.annotation-api JAR в ваши зависимости.

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'
52 голосов
/ 04 августа 2010

Если ваш класс выполняет всю свою инициализацию в конструкторе, то @PostConstruct действительно избыточен.

Однако, если ваш класс имеет свои зависимости, введенные с помощью методов установки, тогда конструктор класса не сможет полностью инициализироватьобъект, и иногда некоторая инициализация должна быть выполнена после того, как все методы установщика были вызваны, следовательно, случай использования @PostConstruct.

5 голосов
/ 18 октября 2017

Рассмотрим следующий сценарий:

public class Car {
  @Inject
  private Engine engine;  

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

Так как экземпляр Car должен быть создан до внедрения поля, во время выполнения конструктора механизм точки инжекции все еще равен нулю, что приводит к исключению NullPointerException.

Эту проблему можно решить либо с помощью Инъекции зависимостей JSR-330 для Java , либо с помощью общих аннотаций JSR 250 для аннотации метода Java @PostConstruct.

@ PostConstruct

JSR-250 определяет общий набор аннотаций, который был включен в Java SE 6.

Аннотация PostConstruct используется для метода, который должен быть выполняется после внедрения зависимости для выполнения любого инициализация. Этот метод ДОЛЖЕН быть вызван до помещения класса в сервис. Эта аннотация ДОЛЖНА поддерживаться всеми классами, которые поддержка внедрения зависимости.

JSR-250 гл. 2.5 javax.annotation.PostConstruct

Аннотация @PostConstruct позволяет определять методы, которые будут выполняться после создания экземпляра и выполнения всех инъекций.

public class Car {
  @Inject
  private Engine engine;  

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

Вместо выполнения инициализации в конструкторе код перемещается в метод, аннотированный @ PostConstruct.

Обработка методов после конструирования - это простой поиск всех методов, аннотированных @PostConstruct, и их последовательный вызов.

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

Обработка методов после конструирования должна выполняться после завершения создания экземпляра и внедрения.

1 голос
/ 16 октября 2011

Инициализация на основе конструктора также не будет работать так, как задумано, когда задействованы какие-либо прокси или удаленные взаимодействия.

Ct будет вызываться всякий раз, когда EJB десериализуется, и всякий раз, когда для него создается новый прокси ...

...