Для Хелидон МП..
Я наблюдаю некоторую проблему при внедрении поля при доступе в конструкторе.
В приведенном ниже сценарии получение нулевого значения в конструкторе с @Inject или без него над конструктором
Класс GreetingProvider
снабжен аннотацией ApplicationScoped
@Inject
@ConfigProperty(name = "app.greeting")
private String message;
@Inject //Getting the field as null with or without @Inject annotation
public GreetingProvider() {
LOG.debug("Message {}, message);
}
Может получить значение в прослушивателе событий для ApplicationScoped
.
Он отлично работает, если я использовал инъекцию на основе конструктора.
@Inject
public GreetingProvider(@ConfigProperty(name = "app.greeting") String message) {
this.message.set(message);
}
Разве конструктор не должен получать инициализированное значение из инъекции поля?
Ожидается, что инициализированное поле, введенное с помощью @ConfigProperty
, будет доступно в конструкторе.
Он доступен в методе, аннотированном @PostConstruct
, или в методе, который наблюдает за событием activated(@Observes @Initialized(ApplicationScoped.class) final Object event)
.
Значения полей можно вводить только после создания экземпляра объекта. Создание экземпляра объекта требует вызова конструктора. Итак, если вы думаете об этом, ваш конструктор по умолчанию вызывается для создания экземпляра объекта в первую очередь, и только после построения поля вводятся в том порядке, в котором они объявлены. Вот почему все поля @Inject будут нулевыми при доступе к ним через конструктор по умолчанию.
Правильный способ справиться с этой ситуацией - через внедрение конструктора, используя @Inject в конструкторе, как во втором примере.
@Inject
public GreetingProvider(@ConfigProperty(name = "app.greeting") String message) {
this.message.set(message);
}
Здесь вы определяете зависимости, необходимые вашему классу, а диспетчер CDI гарантирует, что зависимости будут переданы в конструктор с аннотацией @Inject. Теперь вы можете обращаться к зависимостям как к формальным аргументам конструктора и самостоятельно инициализировать поля.
Метод с аннотацией @PostConstruct OTOH вызывается только один раз для каждого экземпляра и после завершения внедрения конструктора, поля и метода. Если у вас есть какая-то логика, которая должна выполняться один раз для каждого экземпляра, @PostConstruct — идеальный способ.
@PostConstruct
public void executeOncePerInstance(){
// You can safely access all the field & constructor-injected properties here
}
Поведение будет похоже на внедрение конструктора, но некоторые реализации контейнера CDI могут вызывать конструктор более одного раза в процессе настройки прокси-сервера Java для компонента.
Спасибо за подробное разъяснение. Кроме того, еще одна вещь, которую следует добавить после просмотра документов CDI, это то, что по умолчанию его ленивая инициализация, поэтому для класса контроллера (те, которые предоставляют API REST, но никогда не внедряются) лучше использовать наблюдение за событиями
@Observes @Initialized(ApplicationScoped.class...)
, так как@PostConstruct
может вызываться только после того, как bean-компонент введен в первый раз.