Зачем мне нужен конструктор без аргументов, чтобы использовать бины ApplicationScoped с инжектором Constructor в CDI?

Я пытаюсь применить шаблон инжектора конструктора к bean-компонентам в моем приложении CDI и получаю следующее сообщение об ошибке:

15:18:11,852 ERROR [izone.adams.webapp.error.IzoneExceptionHandler] (default task-40) org.jboss.weld.exceptions.UnproxyableResolutionException: WELD-001435: Normal scoped bean class webapp.util.LoginManagerAction is not proxyable because it has no no-args constructor - <unknown javax.enterprise.inject.spi.Bean instance>.
        at org.jboss.weld.bean.proxy.DefaultProxyInstantiator.validateNoargConstructor(DefaultProxyInstantiator.java:50)

Действительно, чтобы использовать шаблон внедрения конструктора, я специально разработал свой класс с одним конструктором, требующим аргументов:

@ApplicationScoped
@Typed(LoginManagerAction.class)
public class LoginManagerAction extends UtilBasicDispatchAction {

  @Inject
   public LoginManagerAction( SessionManager sessionManager, JMSHealthCheckService jmsHealthCheckService) {
       super();
       this.sessionManager = sessionManager;
       this.jmsHealthCheckService = jmsHealthCheckService;
   }

    ...
    ...

}

Просматривая спецификации CDI типов компонентов Unproxyable, я вижу, что:

3,15. Непроксируемые типы бинов

Контейнер использует прокси для обеспечения определенной функциональности. Некоторые допустимые типы bean-компонентов не могут быть переданы контейнером:

  • классы, которые не имеют не приватного конструктора без параметров,
  • классы, которые объявлены окончательными,
  • классы, которые имеют нестатические, финальные методы с публичной, защищенной или видимостью по умолчанию,
  • примитивные типы,
  • и типы массивов.

Тип bean-компонента должен быть проксируемым, если точка внедрения переходит в bean-компонент:

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

В противном случае контейнер автоматически обнаруживает проблему и рассматривает ее как проблему развертывания.

А в дальнейшем в разделе Нормальные области видимости и псевдоскопы говорится:

Все обычные области должны быть явно объявлены @NormalScope, чтобы указать контейнеру, что требуется клиентский прокси.

Дано @ApplicationScoped бобы по определению @NormalScope Мне нужен не приватный конструктор без аргументов. Итак, мне нужен защищенный конструктор без аргументов, чтобы соответствовать спецификации CDI? Я пробовал использовать защищенный конструктор без аргументов, и, похоже, он работает, но я не понимаю, как работает WELD в этом случае; в каких условиях он использует конструктор без аргументов? Почему это требование в CDI вообще?

Использует ли Weld только no-arg для создания прокси, но при фактическом вызове базовой реализации он использует конструктор на основе inject с аргументами?

2 ответа

Я постараюсь ответить на него немного шире, если я что-то упустил, дайте мне знать ниже.

Что нужно сделать Weld?

Вэлду нужно создать экземпляр вашего прокси @NormalScoped боб. Такой прокси не несет большой информации, это более или менее просто делегат, который он передает вместо контекстного экземпляра. Прокси будет классом, расширяющим ваш бин - это нигде не заявлено, но именно так это делает Weld (и OWB). Это имеет смысл, если вы подумаете об этом... тип безопасности, перехват / декорация и так далее. Пробег от того, как это происходит, варьируется. (Тот факт, что он расширяет бобы, также почему protected конструктора без аргументов будет достаточно. Кроме того, это причина, по которой он должен вызывать некоторый конструктор суперкласса)

Почему ограничение?

Ограничение иметь конструктор без аргументов исходит из самой Java, где единственный законный способ программной реализации объекта - вызвать конструктор. Обратите внимание, что речь не идет об инстанцировании прокси, а не бинов! Вызов параметризованного конструктора для создания прокси на самом деле не вариант, потому что у вас нет контекста относительно того, какое значение должны иметь параметры. У ваших бобов может быть конструктор с инъекцией (@Inject) но опять же, прокси не должны вызывать такие конструкторы, поскольку они являются просто делегатами, и это также может предотвратить некоторые сценарии с циклическим внедрением. Кроме того, он также может вызвать нежелательную инициализацию других объектов, связанных с ним. Вы просто не можете знать, что может происходить внутри конструктора с параметрами.

Поэтому спецификация CDI требует, чтобы у вас был конструктор без аргументов, чтобы Weld мог быть уверен, что он всегда есть, и его можно использовать для безопасного создания экземпляра своего прокси без каких-либо побочных эффектов.

Спасатель жизни, когда вы действительно не можете иметь конструктор без аргументов

На самом деле, есть способ обойти это ограничение. Непостоянная опция конфигурации сварки, которую вместо конструктора можно использовать Unsafe, Посмотрите документы, если вы хотите знать, как включить его.

Мне нужен защищенный конструктор без аргументов, чтобы удовлетворить спецификации CDI? в каких условиях он использует конструктор без аргументов? Почему это требование в CDI вообще?

Как вы цитировали, в спецификации CDI бины станут недоступными для прокси, если у них нет конструктора без аргументов, но есть конструкторы с аргументами. Это не "просто для спецификации", хотя в том смысле, что серверы требований не имеют смысла: в этом нуждаются механизмы создания прокси, используемые CDI. Сначала они создают прокси, а затем реализацию.

Использует ли Weld только no-arg для создания прокси, но при фактическом вызове базовой реализации он использует конструктор на основе inject с аргументами?

Короче да.

Одна из альтернатив, которую я использовал в аналогичном сценарии вместо @ApplicationScoped, - это псевдоскоп @Singleton. Это работает без конструктора без параметров, так как он не использует обычную область видимости. Это означает, что бин не будет проксироваться. Для моих случаев использования это было хорошо. Вот пример класса:

import javax.inject.Inject;
import javax.inject.Singleton;
import javax.validation.Valid;
import javax.ws.rs.Consumes;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.container.AsyncResponse;
import javax.ws.rs.container.Suspended;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.MediaType;

@Produces(MediaType.APPLICATION_JSON)
@Consumes(MediaType.APPLICATION_JSON)
@Path("/add")
@Singleton
public class CounterController {

    private CounterService counterService;

    @Inject
    public CounterController(@Context CounterService counterService) {
        this.counterService = counterService;
    }

    @POST
    public void add(@Suspended final AsyncResponse asyncResponse, @Valid
            CounterRequest counterRequest) {
        asyncResponse.resume(counterService.count(counterRequest));
    }
}

(Обратите внимание, что если вы используете их для ресурсов jax-rs, как у меня, спецификация jax-rs говорит об этом:

Поддержка внедрения в конструктор ресурсов JAX-RS НЕОБЯЗАТЕЛЬНА. Портативные приложения ДОЛЖНЫ вместо этого использовать поля или свойства компонента в сочетании с аннотированным методом @PostConstruct. Реализации ДОЛЖНЫ предупреждать пользователей об использовании непереносимых инъекций конструктора.

Так что это может или не может работать, в зависимости от реализации. Я использовал Weld для моего класса, где он работает.)

Другие вопросы по тегам