2010-08-04 7 views
196

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

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

+0

У меня создалось впечатление, что инъекция конструктора обычно предпочтительнее, чтобы зависимости были «окончательными». Учитывая этот шаблон, почему «@ PostConstruct» добавляется в J2EE - они наверняка видели другой вариант использования? – mjaggard

ответ

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

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

+7

в том случае, если сам конструктор автоувеличивает все зависимости - тогда компонент также может быть полностью инициализирован в конструкторе (после установки вручную всех полей с автоподстановкой). – yair

+3

В каком случае конструктор bean может быть вызван более одного раза? – yair

+0

Наверное, что-то вроде «пассивации». Если контейнер решает сохранить компонент в хранилище дисков, а затем восстановить его. – Bozho

45

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

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

+0

@staffman: плюс один с моей стороны. Если я хочу инициализировать поле входного текста со значением, полученным из базы данных, я могу сделать это с помощью PostConstruct, но не удается, когда пытаюсь сделать то же самое внутри конструктора. У меня есть это требование для инициализации без использования PostContruct. Если у вас есть время, можете ли вы ответить на этот вопрос также: http://stackoverflow.com/questions/27540573/how-to-initialize-inputtextfield-with-a-value-from-database-on-runtime-without-t –

1

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

ТТЫ будут вызваны каждый раз, когда EJB получает десериализацию, и всякий раз, когда новый прокси-сервер получает создан для этого ...

50

Других ответов, особенно @ Bozho один, уже объяснили основную проблемы (среди другие):

в конструктор, инъекция зависимостей еще не произошло.

В случае, если кто еще есть сомнения по поводу того, что это означает, что это реальный пример только что случилось со мной:

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

Надежда, что помогает.

+4

'в конструкторе инъекции зависимостей еще не произошло. 'true с установщиком или инъекцией поля, но не верно при инъекции конструктора. –

+0

Конечно! Я почти не считаю эту * инъекцию *, хотя: несмотря на название, в конце концов, это просто передача параметров ... но спасибо, что указали его –

+0

, что насчет Ejbs? например ejbUs = (UsEjbBeanRemote) sl.findServiceRemote («Пользователь»); ? , Я думаю, что они инициализировались в конструкторе –

2

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

public class Car { 
    @Inject 
    private Engine engine; 

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

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

Эта проблема может быть решена либо путем встраивания конструктора JSR-330 Dependency Injection for Java, либо в JSR 250 Общие аннотации для аннотации метода Java @PostConstruct.

@PostConstruct

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

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

JSR-250 Chap. 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); 
     } 
     }); 
} 

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

Смежные вопросы