In this tutorial we will see how to use producer methods for injecting CDI beans by
using @Produces
annotation. We will use Weld CDI implementation.
Using producer methods provides a programmatic way to solve disambiguation by using injection
points. We have already seen how to solve disambiguation by using qualifiers in this
tutorial.
As a first step, we will create a disambiguation.
Here we have one interface (Bank), one implementation (BankOfAmerica) ,and a simple producer method.
public interface Bank {
public void withdrawal();
public void deposit();
}
public class BankOfAmerica implements Bank {
@Override
public void withdrawal() {
System.out.println("Withdrawal from Bank of America");
}
@Override
public void deposit() {
System.out.println("Deposit to Bank of America");
}
}
public class BankFactory {
@Produces
public Bank createBank() {
return new BankOfAmerica();
}
}
Now, if we try to inject a bank bean like
@Inject
private Bank bankOfAmerica
Our container will not be able to decide to return correct implementation of Bank bean and will throw a Ambiguous dependencies error.
org.jboss.weld.exceptions.DeploymentException: Exception List with 2 exceptions:
Exception 0 :
org.jboss.weld.exceptions.DeploymentException: WELD-001409 Ambiguous dependencies for type [Bank] with qualifiers [@Default] at injection point [[field] @Inject private produces_example.ProducesExample.bankOfAmerica]. Possible dependencies [[Managed Bean [class produces_example.BankOfAmerica] with qualifiers [@Default @Any], Producer Method [Bank] with qualifiers [@Default @Any] declared as [[method] @Produces public produces_example.BankFactory.createBank()]]]
at org.jboss.weld.bootstrap.Validator.validateInjectionPoint(Validator.java:277)
at org.jboss.weld.bootstrap.Validator.validateInjectionPoint(Validator.java:243)
at org.jboss.weld.bootstrap.Validator.validateBean(Validator.java:106)
at org.jboss.weld.bootstrap.Validator.validateRIBean(Validator.java:126)
at org.jboss.weld.bootstrap.Validator.validateBeans(Validator.java:345)
at org.jboss.weld.bootstrap.Validator.validateDeployment(Validator.java:330)
at org.jboss.weld.bootstrap.WeldBootstrap.validateBeans(WeldBootstrap.java:366)
at org.jboss.arquillian.container.weld.ee.embedded_1_1.mock.TestContainer.startContainer(TestContainer.java:257)
at org.jboss.arquillian.container.weld.ee.embedded_1_1.WeldEEMockContainer.deploy(WeldEEMockContainer.java:98)
Now it is time solve our simple ambiguous dependency problem. We are going to create a qualifier called @BankProducer
.
@Qualifier
@Retention(RetentionPolicy.RUNTIME)
@Target({FIELD, METHOD, TYPE})
public @interface BankProducer {
}
If we annotate our producer method with BankProducer
qualifier, container will know that when to call producer method when needed. There are two conditions now,
- If a Bank bean has qualifier BankProducer then container will call the producer method.
- If a Bank bean has not a qualifier then container will inject directly the BankOfAmerica bean.
public class ProducesExample {
@Inject
private Bank beanFromBankImplementation;
@Inject
@BankProducer
private Bank beanFromBankProducer;
public void callBanksWithdrawal() {
beanFromBankImplementation.withdrawal();
beanFromBankProducer.withdrawal();
}
}
Now what if we had more than one Bank implementation and we want to resolve disambiguation in producer method. Assume we have HSBC and Chase.
public class Chase implements Bank {
@Override
public void withdrawal() {
System.out.println("Withdrawal from Chase");
}
@Override
public void deposit() {
System.out.println("Deposit to Chase");
}
}
public class HSBC implements Bank {
@Override
public void withdrawal() {
System.out.println("Withdrawal from HSBC");
}
@Override
public void deposit() {
System.out.println("Deposit to HSBC");
}
}
Additionally, lets define a java annotation and an Enum to separate our Bank implementations in their injection points.
public enum BankName {
HSBC (HSBC.class),
Chase (Chase.class),
BankOfAmerica (BankOfAmerica.class);
private Class<? extends Bank> bankType;
private BankName(Class<? extends Bank> bankType) {
this.bankType = bankType;
}
public Class<? extends Bank> getBankType() {
return bankType;
}
}
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.FIELD, ElementType.METHOD, ElementType.TYPE})
public @interface BankType {
BankName value();
}
Producer method should have two parameters which are; Instance<Bank>
and InjectionPoint
.
public class BankFactory {
@Produces
@BankProducer
public Bank createBank(@Any Instance<Bank> instance, InjectionPoint injectionPoint) {
Annotated annotated = injectionPoint.getAnnotated();
BankType bankTypeAnnotation = annotated.getAnnotation(BankType.class);
Class<? extends Bank> bankType = bankTypeAnnotation.value().getBankType();
return instance.select(bankType).get();
}
}
Instance
parameter is parameterized by BankType
and uses @Any
annotation which refers to any Bank implementation. So, we are able to return any type of Bank implementation. A CDI bean has @Any
qualifier as a default. Moreover, InjectionPoint
refers to the place where the bean is injected (Simply where @Inject is used for requested bean type). Thus, producer method knows where to inject the requested bean due to InjectionPoint
. On the other hand, we can return the new instances of Bank implementations by checking their class types. For example;
if (bankType == BankOfAmerica.class) {
// init new BankOfAmerica object and return it
return new BankOfAmerica();
} else if (bankType == Chase.class) {
// init new Chase object and return it
return new Chase();
} else {
// init new HSBC object and return it
return new HSBC();
}
Finally, our ProducesExample will look like;
public class ProducesExample {
@Inject
@BankType(BankName.BankOfAmerica)
@BankProducer
private Bank bankOfAmerica;
@Inject
@BankType(BankName.Chase)
@BankProducer
private Bank chase;
@Inject
@BankType(BankName.HSBC)
@BankProducer
private Bank hsbc;
public void callBanksWithdrawal() {
bankOfAmerica.withdrawal();
chase.withdrawal();
hsbc.withdrawal();
}
}
When we call callBanksWithdrawal() method the output will be;
Withdrawal from Bank of America Withdrawal from Chase Withdrawal from HSBC
Additional Notes
- Producer method can be either static or non-static method.
- Producer methods let us to do custom initialization on beans.
- The injection point should have the same type and qualifier with producer method so that injection point will resolved to the proper producer method.
Comments powered by Disqus.