Навіщо використовувати @PostConstruct?


294

У керованому бобі @PostConstructвикликається після звичайного конструктора об'єктів Java.

Чому я @PostConstructб ініціалізував бин, а не звичайний конструктор?


4
У мене склалося враження, що введення конструктора, як правило, вважається кращим, щоб дозволити залежність final. Зважаючи на цю схему, чому @PostConstructдодається до J2EE - вони, напевно, вже бачили інший випадок використання?
маджард

Відповіді:


409
  • тому що, коли викликається конструктор, боб ще не ініціалізований - тобто не вводяться залежності. У @PostConstructметоді боб повністю ініціалізується, і ви можете використовувати залежності.

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


17
у випадку, якщо сам конструктор автоматично з'єднає всі залежності - тоді боб також може бути повністю ініціалізований у конструкторі (після встановлення вручну всіх автопровідних полів).
yair

7
який випадок, коли конструктор квасолі може називатися не один раз?
yair

1
Напевно, щось на кшталт "пасивації". Якщо контейнер вирішить зберігати квасоля на сховищі дисків, а потім відновити її звідти.
Божо

13
Це не так вже ймовірно, що конструктор викличе кілька разів. Коли контейнер інстанціює проксі, ви побачите, що конструктор викликається принаймні один раз для проксі і один раз для справжнього біна.
Маркус

Зауважте, що методи @PostConstruct не викликаються при завантаженні сторінки відразу після перезавантаження сервера. (Це може бути помилка JBoss.)
Дейв Джарвіс

96

Основна проблема полягає в тому , що:

у конструкторі введення залежностей ще не відбулося *

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


Приклад у реальному світі:

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 до ваших залежностей.

Мейвен

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

Градле

// 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. вірно з сетером або полевим впорскуванням, але не відповідає дійсності для конструктора.
Адам Сіміон

Якщо в Java 11 видалено @PostConstruct, як тепер ми можемо обробляти цей реальний приклад із Java 11?
тет

@tet Як зазначено у відповіді, вам потрібно використовувати бібліотеку javax.annotation-api. Ці анотації були видалені в Java 11, але вони були позначені застарілими після Java 9.
narendra-choudhary

63

Якщо ваш клас виконує всю свою ініціалізацію в конструкторі, то @PostConstructце справді зайве.

Однак, якщо у вашого класу введені залежності за допомогою методів встановлення, конструктор класу не може повністю ініціалізувати об'єкт, і іноді потрібно виконати деяку ініціалізацію після виклику всіх методів встановлення, отже, і випадку використання @PostConstruct.


@staffman: плюс один з мого боку. Якщо я хочу ініціалізувати поле inputtext зі значенням, отриманим з бази даних, я можу це зробити за допомогою PostConstruct, але не вдається, коли спробую зробити те ж саме у конструкторі. У мене є ця вимога ініціалізуватися без використання PostContruct. Якщо у вас є час, будь ласка , ви можете відповісти на це питання також: stackoverflow.com/questions/27540573 / ...
Shirgill Фархан

10

Розглянемо наступний сценарій:

public class Car {
  @Inject
  private Engine engine;  

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

Оскільки автомобіль доводиться екземплярувати перед польовим впорскуванням, двигун точки вприскування все ще є нульовим під час виконання конструктора, що призводить до 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

Також ініціалізація, заснована на конструкторі, не працюватиме за призначенням, коли бере участь якийсь проксі-сервер чи видалення.

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

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.