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.


@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();
}
}

You will have to specify, what implementation has to be injected. You have two choices: 

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:


@Stateless
@WebService
public class ClientXMLBean implements Client {

@EJB
private Service service;

public String getHello() {
return this.service.getMessage();
}
}


The particular implementation has to be specified in the ejb-jar.xml configuration then:

<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]

Comments:

Killing factories...really? This is fine and good but factories are still an important part of your toolbox. The method described above only addresses the case when you know at compile time what bean you will need. A factory is still required when you can only determine a bean at runtime.

Posted by todd on August 04, 2009 at 01:21 AM CEST #

@Todd,

exactly - this approach is static. With factories and reflection you could swap the implementation dynamically. BUT: in the context of a typical business app it is very rare.
You will have to use a ServiceLocator then....

Factories inside the container are rarely needed,

thanks for your comment!,

adam

Posted by Adam Bien on August 04, 2009 at 10:31 AM CEST #

@Todd,

see also: http://www.adam-bien.com/roller/abien/entry/ejb_3_1_beanlocator_when

regards,

adam

Posted by Adam Bien on August 04, 2009 at 11:10 AM CEST #

@EJB(beanName="DefaultService")
private Service service;

leads to the same tight coupling as

private Service service = new DefaultService();

In fact this is a lookup even it is powered by DI mechanisms.

Posted by Christian on December 09, 2009 at 06:01 PM CET #

@Christian,

"@EJB(beanName="DefaultService")
private Service service;

leads to the same tight coupling as..."

with that name - yes. But you can name it however you want. You could even override the annotations with XML.

thanks for your feedback!,

adam

Posted by Adam Bien on December 09, 2009 at 10:03 PM CET #

Hi.

What if I need to find all the instances of a remote interfaces deployed in the same application server?

In other words, find all the deployed providers of a service.

I would like to have something like:

@Inject @Any private Instance<ServiceRemote> providers;

Where 'ServiceRemote' is a @Remote interface, and there are multiple implementions deployed in differents EARs.

Thank you.

Posted by Xavier on February 05, 2013 at 09:11 AM CET #

It's quite an old post, but I am searching for the same solution Xavier mentioned:

"I would like to have something like:
@Inject @Any private Instance<ServiceRemote> providers;
Where 'ServiceRemote' is a @Remote interface, and there are multiple implementions deployed in differents EARs."

Is there any solution?

Posted by Chris on January 06, 2015 at 12:31 PM CET #

@Chris,

a good question, I'm going to cover it at the next: http://airhacks.tv,

thanks for asking and commenting!

cheers,

adam

Posted by Adam Bien on January 11, 2015 at 08:04 AM CET #

This post helped me a lot :)
Thanks for sharing your knowledge.

Posted by Lucas Reis on August 11, 2016 at 02:00 PM CEST #

Post a Comment:
  • HTML Syntax: NOT allowed
...the last 150 posts
...the last 10 comments
License