Здесь есть несколько проблем.
- Как лучше всего изменить желаемую реализацию во всем приложении? Посмотрите
@Alternatives
.
- Нужен ли мне квалификатор для каждой реализации? Нет, см. этот ответ для длинного и подробное объяснение.
- Должен ли я использовать производителя, чтобы решить, какая реализация внедряется? Может быть решение, которое вы хотите, но я сомневаюсь в этом. Производители обычно используются для выполнения некоторой инициализации, которую нельзя выполнить в конструкторе /
@PostConstruct
. Вы также можете использовать его для проверки точки внедрения и принятия решений во время выполнения о том, что вводить. См. ссылку 2. для некоторых подсказок.
Правильно ли это решение? Это будет работать, но вам все равно придется возиться с кодом, чтобы изменить реализацию, поэтому сначала рассмотрим 1. Также @Calculator Calculator
кажется очень избыточным. Опять же, см. ссылку на 2.
@ApplicationScoped
public class CalculatorFctory implements Serializable {
private Calculator calc;
@Produces @Calculator Calculator getCalculator() {
return new Calculator();
}
}
Обновление:
CDI использует квалификаторы в дополнение к типам для разрешения зависимостей. Другими словами, пока существует только один тип, соответствующий типу точки внедрения, одних типов достаточно и квалификаторы не нужны. Квалификаторы нужны для устранения неоднозначности, когда одних типов недостаточно.
Например:
public class ImplOne implements MyInterface {
...
}
public class ImplTwo implements MyInterface {
...
}
Чтобы иметь возможность внедрить любую реализацию, вам не нужны никакие квалификаторы:
@Inject ImplOne bean;
or
@Inject ImplTwo bean;
Вот почему я говорю, что @Calculator Calculator
избыточно. Если вы определите квалификатор для каждой реализации, вы не получите многого, с тем же успехом можно было бы просто использовать тип. Скажем, два квалификатора @QualOne
и @QualTwo
:
@Inject @QualOne ImplOne bean;
и
@Inject @QualTwo ImplTwo bean;
Приведенный выше пример ничего не дает, поскольку в предыдущем примере уже не существовало двусмысленности.
Конечно, вы можете сделать это для случаев, когда у вас нет доступа к определенным типам реализации:
@Inject @QualOne MyInterface bean; // to inject TypeOne
и
@Inject @QualTwo MyInterface bean; // to inject TypeTwo
Однако OP не должен использовать @Produces, когда он хочет, чтобы реализации калькулятора управлялись CDI.
@Avinash Singh - CDI управляет @Produces
, а также всем, что они возвращают, если именно CDI вызывает метод. Если хотите, см. этот раздел спецификации. Это включает в себя возврат компонентов `@...Scoped, которые будут поддерживать внедрение зависимостей, обратные вызовы жизненного цикла и т. д.
Здесь я упустил некоторые детали, поэтому рассмотрим следующие два:
public class SomeProducer {
@Inject ImplOne implOne;
@Inject ImplTwo implTwo;
@Inject ImplThree implThree;
@Produces
public MyInterface get() {
if (conditionOne()) {
return implOne;
} else if (conditionTwo()) {
return implTwo;
} else {
return implThree;
}
}
}
и
public class SomeProducer {
@Produces
public MyInterface get() {
if (conditionOne()) {
return new ImplOne();
} else if (conditionTwo()) {
return new ImplTwo();
} else {
return new ImplThree;
}
}
}
Тогда в первом примере CDI будет управлять жизненным циклом (т. е. поддержкой @PostConstruct
и @Inject
) того, что возвращается от производителя, а во втором — нет.
Вернемся к исходному вопросу — как лучше всего переключаться между реализациями без изменения исходного кода? Предполагается, что вы хотите, чтобы изменение касалось всего приложения.
@Default
public class ImplOne implements MyInterface {
...
}
@Alternative
public class ImplTwo implements MyInterface {
...
}
@Alternative
public class ImplThree implements MyInterface {
...
}
Затем будут введены любые для любых @Inject MyInterface instance
, ImplOne
, если только
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/beans_1_0.xsd">
<alternatives>
<class>ImplTwo</class>
</alternatives>
</beans>
указано, и в этом случае ImplTwo
будет введено везде.
Дальнейшее обновление
В среде Java EE действительно есть вещи, которые не управляются CDI, например EJB и веб-службы.
Как бы вы внедрили веб-службу в управляемый компонент CDI? Это действительно просто:
@WebServiceRef(lookup="java:app/service/PaymentService")
PaymentService paymentService;
Вот и все, у вас будет действующая ссылка на платежный сервис, который управляется вне CDI.
Но что, если вы не хотите использовать полный @WebServiceRef(lookup="java:app/service/PaymentService")
везде, где он вам нужен? Что, если вы хотите ввести его только по типу? Затем вы делаете это где-то:
@Produces @WebServiceRef(lookup="java:app/service/PaymentService")
PaymentService paymentService;
и в любом компоненте CDI, которому нужна ссылка на эту платежную службу, вы можете просто @Inject
использовать CDI следующим образом:
@Inject PaymentService paymentService;
Обратите внимание, что до определения поля производителя PaymentService
не будет доступно для внедрения способом CDI. Но он всегда доступен старым способом. Кроме того, в любом случае веб-служба не управляется CDI, но определение поля производителя просто делает ссылку на эту веб-службу доступной для внедрения способом CDI.
16.03.2013
@Produces
, если вы собираетесь вызывать его самостоятельно. 16.03.2013@Produces @myservice @WebServiceRef(lookup="java:app/service/PaymentService") {} @Inject @myservice MyWebService;
. В этом случае контейнер не управляет жизненным циклом MyWebServiceImpl, он просто внедряет его 17.03.2013@Produces
по-прежнему управляются CDI, если только они не создаются в методе какnew()
. Я исправил свое определение того, когда использовать подход 3. 17.03.2013