Injecting Different Implementations Into An EJB 3 - Killing Factories 📎
In most cases the EJB 3 container relies on conventions and injects the one and only existing EJB 3 implementation. It is convenient because in vast majority of all cases you only have one single implementation of an interface. Because it is the only possible choice, additional configuration doesn't provide any added value. It wouldn't be DRY.
But if you get an additional implementation of the interface - this approach will break.
You will have to specify, what implementation has to be injected. You have two choices:
@Local
public interface Service {
public String getMessage();
}
@Stateless
public class DefaultService implements Service {
public String getMessage() {
return "Hello from: " + this.getClass().getName();
}
}
//additional implementation - breaks the DI conventions
@Stateless
public class SpecificService implements Service {
public String getMessage() {
return "Hello from: " + this.getClass().getName();
}
}
1. The use of annotations:
@Stateless
@WebService
public class ClientAnnotationBean implements Client {
@EJB(beanName="DefaultService")
private Service service;
public String getHello() {
return this.service.getMessage();
}
}
The "DefaultService" in the @EJB annotation is the name of the EJB (DefaultService.class.getSimpleName()). You will have to recompile the code the client on every change...
2. The use of XML-configuration. In this case you can skip the beanName attribute in the annotation and specify it in the ejb-jar.xml. You could even entirely omit the @EJB annotation - but it doesn't provide additional benefits. You don't have to recompile your code, but provide and maintain an ejb-jar.xml file:
The particular implementation has to be specified in the ejb-jar.xml configuration then:
@Stateless
@WebService
public class ClientXMLBean implements Client {
@EJB
private Service service;
public String getHello() {
return this.service.getMessage();
}
}
<ejb-jar xmlns = "http://java.sun.com/xml/ns/javaee"
version = "3.0"
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/ejb-jar_3_0.xsd">
<enterprise-beans>
<session>
<ejb-name>ClientXMLBean</ejb-name>
<ejb-local-ref>
<ejb-ref-name>com.abien.samples.di.client.ClientXMLBean/service</ejb-ref-name>
<local>com.abien.samples.di.service.Service</local>
<ejb-link>DefaultService</ejb-link>
</ejb-local-ref>
</session>
</enterprise-beans>
</ejb-jar>
In EJB 3.1 interfaces are actually superfluous for common cases. Interfaces are only needed for the encapsulation of multiple implementations. With DI and EJB 3 you don't need factories any more. The whole sample was pushed into: http://kenai.com/projects/javaee-patterns/ and tested with Glassfish v2 and Netbeans 6.5/6.7.1
[See also: "Real World Java EE Patterns - Rethinking Best Practices", chapter 1, page 37 for more details about DI]