There are several issues here.
- What is the best way to change the desired implementation in the entire application? Look into
@Alternatives
.
- Do I need a qualifier for each implementation? No, see this answer for a lengthy and detailed explanation.
- Should I use a producer to decide which implementation is injected? Could be the solution you want, but I doubt it. Producers are generally used to perform some sort of initialization that can't be done in the constructor /
@PostConstruct
. You could also use it to inspect the injection point and make runtime decisions about what to inject. See the link 2. for some clues.
Is this solution correct? This will work, but you'll still have to mess with the code to change the implementation, so consider 1. first. Also @Calculator Calculator
seems highly redundant. Again, see the link at 2.
@ApplicationScoped
public class CalculatorFctory implements Serializable {
private Calculator calc;
@Produces @Calculator Calculator getCalculator() {
return new Calculator();
}
}
Update:
CDI uses qualifiers in addition to types for dependency resolution. In other words, as long as there is only one type that matches the type of the injection point, types alone are enough and qualifiers are not needed. Qualifiers are there for disambiguation when types alone are not enough.
For example:
public class ImplOne implements MyInterface {
...
}
public class ImplTwo implements MyInterface {
...
}
To be able to inject either implementation, you don't need any qualifiers:
@Inject ImplOne bean;
or
@Inject ImplTwo bean;
That's why I say @Calculator Calculator
is redundant. If you define a qualifier for each implementation, you're not gaining much, might as well just use the type. Say, two qualifiers @QualOne
and @QualTwo
:
@Inject @QualOne ImplOne bean;
and
@Inject @QualTwo ImplTwo bean;
The example directly above does not gain anything since in the previous example no dis-ambiguity existed already.
Sure, you can do this for cases where you don't have access to particular implementation types:
@Inject @QualOne MyInterface bean; // to inject TypeOne
and
@Inject @QualTwo MyInterface bean; // to inject TypeTwo
However OP shouldn't be using @Produces when he wants Calculator implementations to be CDI managed.
@Avinash Singh - CDI manages @Produces
as well as anything they return, as long as it is CDI that calls the method. See this section of the spec if you please. This includes returning `@...Scoped beans which will support dependency injection, life-cycle callbacks, etc.
I overlooked some details here, so consider the following two:
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;
}
}
}
and
public class SomeProducer {
@Produces
public MyInterface get() {
if (conditionOne()) {
return new ImplOne();
} else if (conditionTwo()) {
return new ImplTwo();
} else {
return new ImplThree;
}
}
}
Then, in the first example, CDI will manage the life cycle (i.e. @PostConstruct
and @Inject
support) of what's returned from the producer, but in the second one it will not.
Back to the original question - what's the best way to switch between implementations without having to modify the source? The assumption is that you want the change to be application wide.
@Default
public class ImplOne implements MyInterface {
...
}
@Alternative
public class ImplTwo implements MyInterface {
...
}
@Alternative
public class ImplThree implements MyInterface {
...
}
Then, any for any @Inject MyInterface instance
, ImplOne
will be injected, unless
<?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>
is specified, in which case ImplTwo
will be injected everywhere.
Further Update
There are indeed things in the Java EE environment that are not managed by CDI, such as EJBs and web services.
How would you inject a web service into a CDI managed bean? It's simple really:
@WebServiceRef(lookup="java:app/service/PaymentService")
PaymentService paymentService;
That's it, there you'll have a valid reference to the payment service which is managed outside CDI.
But, what if you didn't want to use the full @WebServiceRef(lookup="java:app/service/PaymentService")
everywhere you need it? What if you only want to inject it by type? Then you do this somewhere:
@Produces @WebServiceRef(lookup="java:app/service/PaymentService")
PaymentService paymentService;
and in any CDI bean that needs a reference to that payment service you can simply @Inject
it using CDI like this:
@Inject PaymentService paymentService;
Note that before defining the producer field, PaymentService
wouldn't be available for injection the CDI way. But it is always available the old way. Also, in either case the web service is not managed by CDI but defining the producer field simply makes that web service reference available for injection the CDI way.