Java EE CDI dependency disambiguation example
Introduction
In Java EE CDI it is possible to provide multiple implementations of a given interface - or service - to the service clients. The client should then be responsible for selecting the desired service implementation to be injected into a given injection point. In this tutorial we will see how to achieve this behaviour in detail.
This tutorial considers the following environment:
- JDK 1.7.0.21
- Weld 1.1.10
Weld is the CDI reference implementation
Example exception:
org.jboss.weld.exceptions.DeploymentException: WELD-001409 Ambiguous dependencies for type [...] with qualifiers [...] at injection point [...]. Possible dependencies [...] with qualifiers [...], Managed Bean [...] with qualifiers [...]
The service
We start by defining a simple interface and a couple of interface implementations to serve as an example in this tutorial:
package com.byteslounge.service; public interface NotificationService { void sendNotification(); }
package com.byteslounge.service.impl; import com.byteslounge.service.NotificationService; public class EmailNotificationService implements NotificationService { @Override public void sendNotification() { System.out.println("Sending email notification"); } }
package com.byteslounge.service.impl; import com.byteslounge.service.NotificationService; public class SmsNotificationService implements NotificationService { @Override public void sendNotification() { System.out.println("Sending SMS notification"); } }
What happens now when we inject the NotificationService bean?
@Inject private NotificationService notificationService;
The container will not know which of the available implementations should be injected and will complain about it. We will see how to do dependency disambiguation in the next section.
Dependency disambiguation
One of the mechanisms provided by CDI to do dependency disambiguation is Qualifiers. Qualifiers are defined as Java annotations and are used by CDI to specify which dependency should be injected at a given injection point.
Let's define a Qualifier to be used in our NotificationService:
package com.byteslounge.service; import static java.lang.annotation.ElementType.FIELD; import static java.lang.annotation.ElementType.TYPE; import static java.lang.annotation.ElementType.METHOD; import static java.lang.annotation.RetentionPolicy.RUNTIME; import java.lang.annotation.Retention; import java.lang.annotation.Target; import javax.inject.Qualifier; @Qualifier @Retention(RUNTIME) @Target({FIELD, TYPE, METHOD}) public @interface NotificationServiceType { ServiceType value(); public enum ServiceType{ EMAIL, SMS; } }
We defined a Java annotation that will be used to disambiguate between EmailNotificationService and SmsNotificationService service implementations. Also note that for convenience we defined an Enum which will be used as the value of our qualifier: EMAIL or SMS.
Now we go back to the service implementations and use the Qualifier annotation we just defined:
package com.byteslounge.service.impl; import com.byteslounge.service.NotificationService; import com.byteslounge.service.NotificationServiceType; import com.byteslounge.service.NotificationServiceType.ServiceType; @NotificationServiceType(ServiceType.EMAIL) public class EmailNotificationService implements NotificationService { @Override public void sendNotification() { System.out.println("Sending email notification"); } }
package com.byteslounge.service.impl; import com.byteslounge.service.NotificationService; import com.byteslounge.service.NotificationServiceType; import com.byteslounge.service.NotificationServiceType.ServiceType; @NotificationServiceType(ServiceType.SMS) public class SmsNotificationService implements NotificationService { @Override public void sendNotification() { System.out.println("Sending SMS notification"); } }
As you can see we used the Qualifier in each service implementation - @NotificationServiceType - where the value is EMAIL or SMS respectively.
Now when we inject the service we also use the Qualifier in order to specify which service implementation should be used:
@Inject @NotificationServiceType(ServiceType.EMAIL) private NotificationService notificationService;
By annotating the injection point with @NotificationServiceType(ServiceType.EMAIL) we will make possible for the container to know that it should inject the EmailNotificationService service implementation.
Dependency disambiguation in CDI Producer methods
It is also possible to do dependency disambiguation in CDI Producer methods. Please see Java EE CDI Producer methods tutorial.
Downloadable sample
You can find a downloadable sample at the end of this page showing CDI dependency disambiguation in action. The sample is configured to be run on Tomcat and the service is injected into a testing servlet.