Originaly By: Adam Bien
Learn how and when to use the contexts & dependency injection model in Java EE 6.
Introduction
Java Platform, Enterprise Edition (Java EE) 5 brought dependency injection (DI) with Convention over Configuration to Enterprise JavaBeans (EJB) 3.0. Java EE 6 introduces the flexible and powerful @Inject dependency injection model (JSR-330 and JSR-299) in addition to the already existing @EJB annotation. So when should you use what?
Configuring EJB 3 Dependency Injection
EJB 3 dependency injection is extremely simple to use. A single annotation, @EJB, causes the injection of a declared bean. The injection of the MessageSession bean into a Servlet 3.0 looks like this:
@WebServlet(name="Messenger", urlPatterns={"/Messenger"})
public class Messenger extends HttpServlet {
@EJB
MessageSession session;
//
}
@WebServlet(name="Messenger", urlPatterns={"/Messenger"})
public class Messenger extends HttpServlet {
@EJB
MessageSession session;
//
}
The injected MessageSession bean could be an interface or a no-interface view bean. As long as there is only one implementation of the interface, it will be injected without any ceremony. Configuration is required only to disambiguate the choice.
@Stateless
@Local(MessageSession.class)
public class PersistentMessageSession implements MessageSession{
@Local(MessageSession.class)
public class PersistentMessageSession implements MessageSession{
@Override
public String getReceivedMessage() {
return "From persistent. Received at: " + new Date();
} }
@Stateless
@Local(MessageSession.class)
public class TransientMessageSession implements MessageSession {
//...implementation
}
Listing 1: Breaking the Convention with Two Implementations of a MessageSession Interface
The existence of two implementations of the MessageSession @Local interface breaks the convention and throws an exception during deployment:
Exception while deploying the app: java.lang.RuntimeException: Cannot resolve reference Remote ejb-ref name=[...] because there are 2 ejbs in the application with interface com.taman.di.messenger.MessageSession
Enhancement of the @EJB annotation with the beanName attribute fixes the problem. The value of the beanName attribute is the simple name (getSimpleName) of the desired bean.
@EJB(beanName="TransientMessageSession")
MessageSession session;
The dependency injection can be also configured in the deployment descriptor (XML configuration) instead of annotations, but it is still string based. Here, there is no other implementation option than string matching.
public String getReceivedMessage() {
return "From persistent. Received at: " + new Date();
} }
@Stateless
@Local(MessageSession.class)
public class TransientMessageSession implements MessageSession {
//...implementation
}
Listing 1: Breaking the Convention with Two Implementations of a MessageSession Interface
The existence of two implementations of the MessageSession @Local interface breaks the convention and throws an exception during deployment:
Exception while deploying the app: java.lang.RuntimeException: Cannot resolve reference Remote ejb-ref name=[...] because there are 2 ejbs in the application with interface com.taman.di.messenger.MessageSession
Enhancement of the @EJB annotation with the beanName attribute fixes the problem. The value of the beanName attribute is the simple name (getSimpleName) of the desired bean.
@EJB(beanName="TransientMessageSession")
MessageSession session;
The dependency injection can be also configured in the deployment descriptor (XML configuration) instead of annotations, but it is still string based. Here, there is no other implementation option than string matching.
What Is the EJB Reference
The injected instance is neither the implementation of the @Local interface nor the bean itself in the case of a no-interface view. The Java EE specification does not prescribe the nature of the injected class, but it does imply the usage of indirection.
The EJB container handles transactions, concurrency, security, and custom features, such as interceptors, management, and monitoring, for you transparently. These features, however, can be implemented only with an indirection in place.
In case of a @Local interface injection, GlassFish Server Open Source Edition will generate a class at runtime with the name $Proxy202. It is a dynamic proxy, and the number after the "@Proxy" is constantly increased.
@EJB(beanName="TransientMessageSession")
MessageSession session;
Listing 2: @Local Interface Injection
Dynamic proxies work only with interfaces. The direct injection of the @LocalBean: @EJB PersistentMessageSession, causes GlassFish to generate another kind of proxy:
The EJB container handles transactions, concurrency, security, and custom features, such as interceptors, management, and monitoring, for you transparently. These features, however, can be implemented only with an indirection in place.
In case of a @Local interface injection, GlassFish Server Open Source Edition will generate a class at runtime with the name $Proxy202. It is a dynamic proxy, and the number after the "@Proxy" is constantly increased.
@EJB(beanName="TransientMessageSession")
MessageSession session;
Listing 2: @Local Interface Injection
Dynamic proxies work only with interfaces. The direct injection of the @LocalBean: @EJB PersistentMessageSession, causes GlassFish to generate another kind of proxy:
com.taman.di.messenger.ejb.__EJB31_Generated__PersistentMessageSession__Intf____Bean__
In either case, the proxy can be considered as a decorator, because it enhances the business interface with reusable cross-cutting concerns.
EJB 3 Is Not Contextual
The EJB 3 dependency injection is simple. The name of the annotation @Stateless and @Statefulbeans describe the beans’ behavior from the client’s perspective. In the case of @Stateless, there is no defined relation between the proxy (and so the client) and the actual bean-instance. Several clients can "share" a single EJB instance between method invocations. An eager client, on the other hand, could overload the server and communicate with several instances through the same proxy at a time. There is an N:M relation between a client and the bean instances.
In either case, the proxy can be considered as a decorator, because it enhances the business interface with reusable cross-cutting concerns.
EJB 3 Is Not Contextual
The EJB 3 dependency injection is simple. The name of the annotation @Stateless and @Statefulbeans describe the beans’ behavior from the client’s perspective. In the case of @Stateless, there is no defined relation between the proxy (and so the client) and the actual bean-instance. Several clients can "share" a single EJB instance between method invocations. An eager client, on the other hand, could overload the server and communicate with several instances through the same proxy at a time. There is an N:M relation between a client and the bean instances.
The size of M is dependent on server configuration and request/transaction load. A request will always be entirely handled by a single bean instance. Several requests will never share a single instance concurrently. It is also impossible for a client to free an instance or "disconnect" or "disassociate" from it.
@Stateful beans are even simpler. The stateful nature is defined by an exact 1:1 relation between the client and the associated bean instance. A client communicates with exactly one bean instance. There is also no way (hacks excluded) for several clients to share an instance. @Stateless as well as @Stateful session beans can be developed in a single-threaded way: There will be never more than one thread accessing the bean instance at a time.
A client has absolutely no control over the @Stateless session bean lifecycle. It is entirely managed by the container. The opposite is true for @Stateful beans. Here, the client has to manage the entire lifecycle. A @Stateful bean is created by the container during the injection. The destruction is initiated by the client through the invocation of a business method annotated with the @Remove annotation or a container-specific timeout.
A @Stateful bean could be injected into a @Stateless bean, but the outcome would be unpredictable. The injected @Stateful instance would be directly bound to a given @Stateless bean instance. The amount of stateful session bean instances would be dependent on the amount of pooled stateless beans.
@Stateful beans are even simpler. The stateful nature is defined by an exact 1:1 relation between the client and the associated bean instance. A client communicates with exactly one bean instance. There is also no way (hacks excluded) for several clients to share an instance. @Stateless as well as @Stateful session beans can be developed in a single-threaded way: There will be never more than one thread accessing the bean instance at a time.
A client has absolutely no control over the @Stateless session bean lifecycle. It is entirely managed by the container. The opposite is true for @Stateful beans. Here, the client has to manage the entire lifecycle. A @Stateful bean is created by the container during the injection. The destruction is initiated by the client through the invocation of a business method annotated with the @Remove annotation or a container-specific timeout.
A @Stateful bean could be injected into a @Stateless bean, but the outcome would be unpredictable. The injected @Stateful instance would be directly bound to a given @Stateless bean instance. The amount of stateful session bean instances would be dependent on the amount of pooled stateless beans.
An automatic association between the HttpSession and a @Stateful instance cannot be achieved transparently with EJB. The developer would need to associate a bean with the HttpSession manually. Beans know nothing about the HttpSession and other concepts from the Servlet specification.
The "C" in CDI
Contexts and Dependency Injection (CDI) is a new Java EE 6 specification, which not only defines a powerful and type-safe Dependency Injection, but also introduces the concept of "contextual" references or scopes.
The "C" in CDI is the main difference between EJB beans and managed CDI beans. CDI-managed beans are contextual and EJB beans are not. Managed beans in CDI live in well-defined scope. They are created and destroyed on demand by the container. CDI comes already with pre-defined scopes and annotations:
The "C" in CDI is the main difference between EJB beans and managed CDI beans. CDI-managed beans are contextual and EJB beans are not. Managed beans in CDI live in well-defined scope. They are created and destroyed on demand by the container. CDI comes already with pre-defined scopes and annotations:
@RequestScoped, @SessionScoped, @ApplicationScoped, and @ConversationScoped.
The CDI container manages all beans inside the scope automatically for you. At the end of an HttpSession or HttpRequest, all instances associated with this scope are automatically destroyed and, thus, garbage collected.
This behavior is very different from that of Stateful session beans. A Stateful session bean instance needs to be explicitly removed by the client with the invocation of a method annotated with @Remove. It will not be automatically destroyed by the container; it is not bound to any context. If you associate a Stateful session bean with the HttpSession, you also have to care about its reliable destruction at the end or timeout of the HttpSession.
The contextual nature of CDI makes the use of beans from different scopes more natural and convenient. You can even mix and match scopes and inject beans from different scopes. The container will still care about proper lifecycle management.
The CDI container manages all beans inside the scope automatically for you. At the end of an HttpSession or HttpRequest, all instances associated with this scope are automatically destroyed and, thus, garbage collected.
This behavior is very different from that of Stateful session beans. A Stateful session bean instance needs to be explicitly removed by the client with the invocation of a method annotated with @Remove. It will not be automatically destroyed by the container; it is not bound to any context. If you associate a Stateful session bean with the HttpSession, you also have to care about its reliable destruction at the end or timeout of the HttpSession.
The contextual nature of CDI makes the use of beans from different scopes more natural and convenient. You can even mix and match scopes and inject beans from different scopes. The container will still care about proper lifecycle management.
Mixing Scope Without Losing the Context
Scopes can be arbitrarily mixed. A @SessionScoped bean can be injected into a @RequestScoped or @ApplicationScoped bean and vice versa. Although the @SessionScoped context is "wider" than that of @RequestScoped, the association between the user and its session will not get lost during the invocation. The container will select the proper session inside the @RequestScoped bean for you. This happens behind the scenes inside the injected proxy.
In Listing 3, the value of the inputText JavaServer Faces (JSF) 2 component needs to be passed between multiple views. This is usually achieved by storing it in the HttpSession.
In Listing 3, the value of the inputText JavaServer Faces (JSF) 2 component needs to be passed between multiple views. This is usually achieved by storing it in the HttpSession.
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:h="http://java.sun.com/jsf/html">
<h:body>
<h:form>
Name:<h:inputText value="#{index.sessionInfo.name}"/>
<h:commandButton value="Next" action="display"/>
</h:form>
</h:body>
</Html>
xmlns:h="http://java.sun.com/jsf/html">
<h:body>
<h:form>
Name:<h:inputText value="#{index.sessionInfo.name}"/>
<h:commandButton value="Next" action="display"/>
</h:form>
</h:body>
</Html>
Listing 3: Index.xhtml: Storing a String Name in HttpSession
The Index backing bean was annotated with the @Model annotation. @Model is a macro (@Stereotype), which gets expanded to @RequestScoped, @Named annotations.
import com.taman.di.messenger.SessionInfo;
import javax.enterprise.inject.Model;
import javax.inject.Inject;
import javax.enterprise.inject.Model;
import javax.inject.Inject;
@Model
public class Index {
@Inject
SessionInfo sessionInfo;
public SessionInfo getSessionInfo(){
return sessionInfo;
}
}
public class Index {
@Inject
SessionInfo sessionInfo;
public SessionInfo getSessionInfo(){
return sessionInfo;
}
}
Listing 4: A @RequestScoped Backing Bean
The SessionInfo bean is the actual state holder (see Listing 5). The index.xhtml JSF 2 component uses the EL expression #{index.sessionInfo.name} to indirectly access the SessionInfo backing bean. The Index backing bean is accessed first, and then the SessionInfo is accessed (see Listing 5). The last segment in the EL expression is responsible for the access of the name property.
import java.io.Serializable;
import javax.enterprise.context.SessionScoped;
import javax.inject.Named;
@Named
@SessionScoped
public class SessionInfo implements Serializable{
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
Listing 5: The State Holder: a @SessionScoped Backing Bean
Because SessionInfo is not bound to the Index backing bean, rather than the current HttpSession, the user will access the state represented by the SessionInfo instance.
Proper resolution of scopes even works with @ApplicationScoped backing beans and thus, singletons. Although there is only one instance of the Display backing bean (see Listing 6) per application and Java Virtual Machine (JVM), injected SessionInfo instances are still properly dispatched between HttpSession instances by the container. Different users will always see their SessionInfo instance.
import com.taman.di.messenger.SessionInfo;
import javax.enterprise.context.ApplicationScoped;
import javax.inject.Inject;
import javax.inject.Named;
@Named
@ApplicationScoped
public class Display {
@Inject
SessionInfo sessionInfo;
public SessionInfo getSessionInfo() {
return sessionInfo;
}
}
import java.io.Serializable;
import javax.enterprise.context.SessionScoped;
import javax.inject.Named;
@Named
@SessionScoped
public class SessionInfo implements Serializable{
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
Listing 5: The State Holder: a @SessionScoped Backing Bean
Because SessionInfo is not bound to the Index backing bean, rather than the current HttpSession, the user will access the state represented by the SessionInfo instance.
Proper resolution of scopes even works with @ApplicationScoped backing beans and thus, singletons. Although there is only one instance of the Display backing bean (see Listing 6) per application and Java Virtual Machine (JVM), injected SessionInfo instances are still properly dispatched between HttpSession instances by the container. Different users will always see their SessionInfo instance.
import com.taman.di.messenger.SessionInfo;
import javax.enterprise.context.ApplicationScoped;
import javax.inject.Inject;
import javax.inject.Named;
@Named
@ApplicationScoped
public class Display {
@Inject
SessionInfo sessionInfo;
public SessionInfo getSessionInfo() {
return sessionInfo;
}
}
Listing 6: Injection of @SessionScoped Bean into a "Singleton"
The SessionInfo instance can also be directly accessed from the view without any injection (see Listing 7). In this case, the SessionInfo instance is directly used as the actual backing bean. Note that only a SessionInfo instance from the currently active HttpSession is value-bound to the view.
The SessionInfo instance can also be directly accessed from the view without any injection (see Listing 7). In this case, the SessionInfo instance is directly used as the actual backing bean. Note that only a SessionInfo instance from the currently active HttpSession is value-bound to the view.
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:h="http://java.sun.com/jsf/html">
<h:body>
<h:form>
Name:<h:inputText value="#{sessionInfo.name}"/>
<h:commandLink action="display" value="back"/>
</h:form>
</h:body>
</Html>
<h:body>
<h:form>
Name:<h:inputText value="#{sessionInfo.name}"/>
<h:commandLink action="display" value="back"/>
</h:form>
</h:body>
</Html>
Listing 7: Accessing the SessionInfo Instance Directly
String Qualifiers Are Legacy
Ambiguous EJB dependencies are resolved with the simple name of the interface implementation in the beanName attribute
@EJB(beanName="TransientMessageSession") at the injection time.
CDI also supports such a resolution of ambiguity with plain String. It even comes with a built-in qualifier for that purpose. @Named can be used to annotate the implementation, as well as the injection point, with plain String.
If the string value matches at the injection point as well as at the implementation, it gets injected. The name can even be derived from the attribute name. Here is an example of the injection of the persistent attribute:
@Inject @Named
MessageSession persistent;
MessageSession persistent;
The code above resolves the injection of the CdiPersistentMessageSession implementation annotated with the @Named("persistent") annotation (see Listing 8):
@Named("persistent")
public class CdiPersistentMessageSession implements MessageSession{
@Override
public String getReceivedMessage() {
return "From CDI persistent. Received at: " + new Date();
}
}
public class CdiPersistentMessageSession implements MessageSession{
@Override
public String getReceivedMessage() {
return "From CDI persistent. Received at: " + new Date();
}
}
Listing 8: Resolution of Ambiguity with @Named
An explicit use of the implementation name is supported as well:
@Inject @Named("persistent")
MessageSession something;
An explicit use of the implementation name is supported as well:
@Inject @Named("persistent")
MessageSession something;
The combination of @Inject with @Named is very similar to the @EJB(beanName="...") injection style. In contrast to EJB, the use of @Named as qualifier was deprecated with the release of the specification. Qualifying a dependency with a string is not type-safe. A simple misspelling of the name leads to hard-to-find errors. The CDI specification (JSR-299) discourages the use of string-based dependency resolution:
"...The use of @Named as an injection point qualifier is not recommended, except in the case of integration with legacy code that uses string-based names to identify beans..."
The Right Way
The recommended way to qualify and to configure the dependencies is to use typed annotations with optional attributes instead of plain strings. These annotations are called qualifiers. It’s a standard annotation marked with a @Qualifier annotation. For injection purposes, a qualifier annotation uses FIELD and TYPE as a target (see Listing 9).
@Qualifier
@Retention(RUNTIME)
@Target({FIELD, TYPE})
public @interface Session {
Type value();
enum Type{
TRANSIENT, PERSISTENT
}
}
Listing 9: A Custom Qualifier for Dependency Injection Disambiguation
The configuration is fairly easy. The injection point gets annotated with the custom qualifier, which has to match with the desired implementation (see Listing 10). Not only are the types of the qualifiers compared, but all of the contained attributes.
@WebServlet(name="CDIMessenger", urlPatterns={"/CDIMessenger"})
public class CDIMessenger extends HttpServlet {
@Inject @Session(Session.Type.TRANSIENT)
MessageSession persistent;
//...
}
Listing 10: Configuration of the Injection Point with a Qualifier
Using the annotation attributes to configure the injection eliminates the need to introduce additional annotations. You can use a single annotation and choose the implementation by varying its attributes.
@Session(Session.Type.TRANSIENT)
public class CdiTransientMessageSession implements MessageSession {
//...
}
Listing 11: Marking an Implementation with a Qualifier
Without the ability to compare annotation attributes, even the @Named annotation would not work. It comprises a single attribute of type String, which is used to compare annotated elements. Besides its poor semantic expressiveness, @Named is just a usual qualifier (see Listing 12).
@Qualifier
@Documented
@Retention(value = RetentionPolicy.RUNTIME)
public @interface Named {
public String value() default "";
}
Listing 12: The Dissection of the @Named Qualifier
@Named is not recommended for configuring injection, because it is too generic. @Named does not provide any additional information and the String attribute can be misspelled easily.
Conclusion
In most cases, you will end up having a one-to-one relation between the injection point and the implementation. Because of Convention over Configuration, you don’t have to configure or annotate anything in this case. Both EJB 3.1 and CDI injection models will work and behave similarly.
The more complex the system you have to implement, the more interesting CDI gets. The configuration of the EJB beans’ DI is similar to the use of the @Named qualifier. CDI goes far beyond that and is not limited to plain String matching. Custom qualifiers can be introduced easily and used to configure the dependency injection in a type-safe manner.
The lack of contextual capabilities in EJB beans is the biggest difference in CDI. The injected EJB proxy just points to either a pool of instances or a single bean instance for a bean annotated with the @Stateless or @Stateful annotation, respectively. There is no automatic association between the HttpRequest or HttpSession and a given EJB instance.
The opposite is true for CDI. The managed beans are managed inside their scopes. The container is aware of the current scope and manages the bean instances at the same time. Having both in place, the container is able to pick the right instance and inject it consistently into your scoped beans. This is not possible with EJB beans because they are scope- and HTTP-infrastructure agnostic.
The lack of contextual capabilities in EJB beans is the biggest difference in CDI. The injected EJB proxy just points to either a pool of instances or a single bean instance for a bean annotated with the @Stateless or @Stateful annotation, respectively. There is no automatic association between the HttpRequest or HttpSession and a given EJB instance.
The opposite is true for CDI. The managed beans are managed inside their scopes. The container is aware of the current scope and manages the bean instances at the same time. Having both in place, the container is able to pick the right instance and inject it consistently into your scoped beans. This is not possible with EJB beans because they are scope- and HTTP-infrastructure agnostic.
See Also
- Dynamic proxies in JDK 1.3+:
- Class proxy: http://download.oracle.com/javase/6/docs/api/java/lang/reflect/Proxy.html
- Interface InvocationHandler: http://download.oracle.com/javase/6/docs/api/java/lang/reflect/InvocationHandler.html
- Enterprise JavaBeans 3.1 with Contexts and Dependency Injection: The Perfect Synergy: http://www.oracle.com/technetwork/articles/java/ejb-3-1-175064.html
- JSR 299: Contexts and Dependency Injection for The Java EE Platform: http://www.jcp.org/en/jsr/summary?id=299
- JSR 330: Dependency Injection for Java: http://www.jcp.org/en/jsr/summary?id=330
was this article written by Adam Bien?
ReplyDeleteCan read on http://www.oracle.com/technetwork/articles/java/cdi-javaee-bien-225152.html
Yes it was.
Delete