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.