EJB 3 In Action - Using EJB with JPA and CDI - CDI

359 阅读9分钟

Introducing CDI

CDI was originally developed via the Java Community Process (JCP) as JSR-299. Origi- nally JSR-299 was called WebBeans, but it quickly became evident that the features encompassed within JSR-299 went far beyond replacing the oft-maligned JSF-managed beans.

A managed bean is simply a POJO that’s managed by a container, with the container providing a basic set of services including resource injec- tion, lifecycle callbacks, and interceptors. A managed bean must have a no-argument constructor, not be serializable, and possess a unique name. Managed beans are defined using the @ManagedBean annotation with annotations for lifecycle callbacks: @PostConstruct and @Destroy.

image.png

CDI Services

  • EJB 3 dependency injection is limited to only EJBs; you can’t perform injection into any POJO or inject a non-EJB into an EJB.
  • CDI’s dependency injection doesn’t have these limitations
  • CDI is an object container that can be used as a standalone or within an existing Java EE container.
  • The CDI container provides a lifecycle for stateful objects, binding of objects to well-defined contexts, type-safe dependency injection, an event notification facility, and robust interceptors.

Lifecycle for stateful objects

CDI provides a well-defined lifecycle for its beans. CDI is an extension of the managed beans specification and thus provides a number of hooks for controlling the creation of new objects, as well as notification of object destruction.

Contexts

Most web applications have at least two contexts or scopes: application and session.

  • The data stored in the application scope is shared—it isn’t tied to one particular user.
  • Session scope, on the other hand, is asso- ciated with a specific user—typically a browser window.

In the case of a travel website, a user might have two tabs open and be comparing flights to Rome on two different days or comparing two different hotels. The user thus has two different contexts that must be tracked separately.

In addition, the user may have created a nested context by launching a wizard in one window to not only pick a hotel but also book a car at the same time. As you can see, application scope and session scope are suddenly not enough; you need additional contexts with which to build your applications.

This is where CDI comes into play. It introduces additional contexts and provides a simple mechanism by which you can define additional contexts.

CDI comes with four built-in contexts or scopes:

  • Application
  • Conversation
  • Request
  • Session

Besides these four scopes, there are also two pseudo-scopes:

  • singleton: handles the special case of singleton beans.
  • dependent:
    • The dependent scope is the scope that a bean is assigned to by default if the bean isn’t assigned explicitly to a scope.
    • An object that belongs to the dependent scope is created when the object it belongs to is created and is destroyed when its owning object is destroyed.

CDI has a pluggable architecture and with a little code, you can develop your own contexts.

Type-safe dependency injection

  • Unlike other DI frameworks, CDI doesn’t use string-based identifiers to determine what object should be injected. Instead, CDI uses type information provided by the Java object model to determine what object should be injected.
  • In situations where the determination is ambiguous because multiple Java objects match the type, quali- fier annotations are used to select the correct candidate.

Event notification

ActionListenerEvent and having the listener registration performed automatically.

Interceptors

Interceptor support in both EJB and CDI is specified in JSR-381 Interceptors 1.1.

  • With CDI, interceptors aren’t limited to EJBs and can be used on any bean managed by CDI.
  • With CDI, interceptors can be used with business methods on a bean, as well as lifecycle and timeout callbacks.
  • Related to interceptors, CDI also introduces a new construct called decorators. A decorator is an interceptor that’s tied to a specific interface.

Relationship between CDI and EJB 3

  • EJB beans are still managed by the EJB container. Thus, the EJB container handles transactions and con- currency and provides all of the supporting functionality.
  • CDI manages its own beans but also provides services to EJBs.
  • You can think of CDI beans as object containers that provide injection, events, interceptors, and scoping support to objects that don’t need the full set of services provided by the EJB container.
  • EJB—where you need transaction support, security, and so on.
  • CDI is the container for your POJOs
  • CDI and EJB are fully integrated. This means that EJBs can use all of the services we just discussed including dependency injection, event notification, interceptors, and decorators.
  • To use the CDI variant because they’re more powerful, generic, and not limited to just EJBs. As you’ll see, you can safely use the @Inject instead of the @EJB.
  • CDI’s DI will support the injection of EJBs into CDI beans. This eliminates the need to write code that will retrieve beans from JNDI. Just add the @Inject annotation and CDI will take care of the rest. Thus, using EJBs has never been easier—you can treat an EJB bean just like any other POJO.
  • Furthermore, if a CDI bean takes on additional functionality, you can simply add a @Stateless, @Singleton, and @Stateful annotation and make your bean an EJB.

CDI isn’t a replacement technology for EJBs but a powerful enhancement. CDI not only empowers EJB, it also greatly augments JSF .

Relationship between CDI and JSF 2

CDI beans

Unlike EJB, CDI doesn’t have its own component model. A bean within CDI can be a managed bean (JSF), Enterprise Java Bean, or POJO. All of these types of objects can use the full range of services provided by CDI.

A CDI bean is associated with a context, has a type, and may be qualified. The context determines the lifecycle of the bean, whether it lives for only one request or it’s a part of a conversation or workflow.

How to use CDI beans

  • The creators of CDI took the approach of convention over configuration.

If your application container supports CDI, which it does if it’s at least compliant with Java EE 6, then all you have to do is place a beans.xml file in your application. The beans.xml file serves two purposes: it’s a configuration file for CDI and it’s a marker file so that CDI knows whether it needs to scan the JAR file for beans.

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://java.sun.com/xml/ns/javaee"
       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/beans_1_0.xsd">
</beans>

Component naming and EL resolution

  • @Named: CDI provides an annotation, @Named, which must be placed on classes or pro- ducer methods
package javax.inject;
@Qualifier
@Documented
@Retention(RUNTIME)
public @interface Named {
  String value() default "";
}

image.png

  • Referencing employee instance from editEmployee.xhtml

image.png

Bean scoping

 Scopes/contexts in CDI
ScopeDescription
@ApplicationScopedAn instance is created only once for the duration of the application and is destroyed once the application is terminated.
@DependentAn instance is created each time an injection is performed. This is the default scope of a bean and is used the vast majority of the time.
@ConversationScopedThis is a new scope type that was introduced with CDI but existed in JBoss’s Seam. It’s a scope that’s programmatically controlled by the application. It spans multiple requests but is shorter than the session scope. In a web application, conversations are used for a task that spans multiple page requests. For example, this is how you’d handle the situations where a user is booking two different vacations in two different browser tabs.
@RequestScopedThis scope corresponds to the standard HTTP request. It begins when a request arrives and is discarded when the response is rendered.
@SessionScopedThis scope corresponds to the HTTP session. References live for the duration of the session.

If no annotation is placed on a bean, the bean is automatically assigned to the dependent scope.

Conversation scope

  • A bean is marked as belonging to a conversation by adding the @ConversationScoped annotation to the bean definition.
  • By default, a conversation is associated with the current request scope and is terminated when the current request is destroyed.

Next generation of DI

Injection with @Inject

image.png

  • Using constructor injection.

image.png

In the first example you could have easily used the @EJB annotations. The real power of the @Inject annotation is apparent when you annotate the constructor. This is a more natural approach—you don’t need to use the @PostConstruct annotation to do setup operations after the bean is created and injection is performed. Using injection with constructors enables the beans to behave like regular POJOs—parameters are passed in via the constructor and the object initializes itself like any other object. You don’t have to do injection and then use a callback to initialize the bean.

Producer methods

  • producer method: A producer is a method or instance variable that’s consulted by CDI to create a bean instance. Marked with the @Producer annotation.

image.png

  • Invoking the producer from index.xhtml

image.png

CDI will scan all of the classes in a JAR file—the JAR file containing a beans.xml file. It’ll keep track of all producer methods it discovers while analyzing the classes in the JAR file. When an instance of the bean is requested—a user as in the case of this example—CDI will invoke the producer method if an instance of the bean doesn’t already exist in the current scope.

Producer methods and JPA

  • Most of the time, the fact that the injected bean is a proxy object isn’t an issue.
  • But JPA implementations often do care.
    • If you try to pass a bean instance created by CDI to JPA, JPA will claim that the bean can’t be persisted and that it isn’t a known type.
    • To solve this problem, you’ll need to use a producer method and keep a reference to the original POJO.

Anyone who gets a reference via injection will be working with a proxy.

Using qualifiers

  • qualifier: A qualifier enables you to qualify certain injection points as using different instances of a bean.

The following code snippet defines the @AuthenticatedUser and @Seller qualifiers:

@Qualifier
@Retention(RUNTIME)
@Target({METHOD, FIELD, PARAMETER, TYPE})
public @interface AuthenticatedUser {}

@Qualifier
@Retention(RUNTIME)
@Target({METHOD, FIELD, PARAMETER, TYPE})
public @interface Seller {}

After defining the qualifier, you need to add the qualifier to the producer method:

@Produces @SessionScoped @AuthenticatedUser @Named("currentUser")
public User getCurrentUser() {
  ...
}

Once the qualifier has been defined, you can use it to tell CDI which instance you want injected, as shown in the next listing.

image.png

Disposer methods

  • A disposer method is responsible for handling the destruction of a bean.

Implementing a disposer method involves adding the @Disposes annotation to a method parameter. Qualifiers may also be used. Additional parameters can be specified; CDI will attempt to resolve the additional parameters as beans using qualifiers if provided. When the disposer method is invoked, the code can close database connections and so on.

image.png

Specifying alternatives

With different deployment scenarios, you need a mechanism at runtime to configure an alternative for an injection point. Obviously this mechanism must use a configuration file and not require code changes.

To mark a bean as being an alternative, add the @Alternative annotation to the class. To use the alternative, enable it in the beans.xml configuration file. Depending on the target environment for the build, you’d optionally use a different beans.xml file or dynamically generate one.

image.png

image.png

image.png

To enable DevelopmentStartup, you’d add the following configuration entry to the beans.xml file:

<alternatives>
  <class>com.actionbazaar.setup.DevelopmentStartup</class>
</alternatives>

Interceptor and decorators

  • A decorator is used to implement specific business functionality for an interface that’s abstracting into a crosscutting concern.
  • A traditional interceptor, on the other hand, provides generic crosscutting logic.

Interceptor bindings

Creating an interceptor with CDI involves four different coding tasks:

  • Create an interceptor annotation.
  • Create an interceptor implementation annotated with the interceptor annotation.
  • Annotate targeted instances with the interceptor annotation.
  • Enable the interceptor with the beans.xml file.
 Interceptor types
Interceptor typeAnnotationDescription
Business method@AroundInvokeIntercepts business method invocations
Lifecycle callback@PostConstruct/@PreDestroyIntercepts lifecycle callbacks
EJB timeout@AroundTimeoutIntercepts EJB timeout methods
  • Example
@InterceptorBinding
@Retention(RUNTIME)
@Target({TYPE})
public @interface PerformanceMonitor {}

image.png

 Methods on InvocationContext
MethodDescription
Object getTarget();Returns the object instance you’re performing interception on
Object getTimer();Returns the timer object associated with the timeout
Method getMethod();Returns the method on which you’re performing interception
Object[] getParameters();Returns the parameters that are to be passed to the method
void setParameters(Object[] os);A setter enabling you to change the parameters used to invoke the target method
Map<String, Object> getContextData();Returns context data associated with this invocation—information from the annotation
Object proceed() throws Exception;Proceeds with invoking the method
  • To use it on the LandingController , you’d add it as follows:
@Named
@ApplicationScoped
@PerformanceMonitor
public class LandingController {
  ...
}

Adding the annotation to the LandingController doesn’t enable it by default. Interceptors must be enabled in the beans.xml file. The following snippet enables this interceptor:

<interceptors>
  <class>com.actionbazaar.util.PerformanceInterceptor</class>
</interceptors>

Decorators

But unlike an interceptor, you’re overriding the methods you want to intercept. A decorator extends or implements the interface of the bean it’s intercepting. As a result, a decorator is tightly coupled to the bean and can implement business logic.

image.png

Once again, decorators are disabled by default. To enable the preceding decorator, you’ll need to add the following snippet to the beans.xml file:

<decorators>
  <class>com.actionbazaar.buslogic.BidManagerFraudDetector</class>
</decorators>

Component stereotypes

@Named @SessionScoped
public class CurrentUserBean implements Serializable {
  @Produces @SessionScoped @AuthenticatedUser @Named
  public User getCurrentUser() {
    ...
  }
}
@SessionScoped
@AuthenticatedUser
@Named
@Stereotype
@Target( { TYPE, METHOD, FIELD })
@Retention(RUNTIME)
public @interface CurrentUser {}

With this new annotation, you’ve consolidated @SessionScoped, @AuthenticatedUser , and @Named down to @CurrentUser . You can see this annotation in action in the simplified code for the CurrentUserBean:

@Named @SessionScoped
public class CurrentUserBean implements Serializable {
  @Produces @CurrentUser
  public User getCurrentUser() {
    ...
  }
}

There are some ground rules for defining stereotypes. Stereotypes can encapsulate the following CDI annotations:

  • Scope
  • Interceptor bindings
  • Named annotation for JSF integration
  • Alternative annotation

The @Model annotation is defined as follows

@Named
@RequestScoped
@Documented
@Stereotype
@Target(value = {ElementType.TYPE, ElementType.METHOD, ElementType.FIELD})
@Retention(value = RetentionPolicy.RUNTIME)
public @interface Model {}

@Model
public class ItemController implements Serializable {
  ...
}

Injecting events

The @Observes annotation is placed on one or more parameters of the method that’s to receive an event.

image.png

image.png

image.png

As you can see from this listener, CDI handled event registration for you. You didn’t need to implement an interface or acquire a reference to the ItemController to register this class as a listener. The onNewItemmethod is a listener because the item parameter is annotated with @Observes. The instances of item that you’ll receive are restricted to only new items with the @NewItem qualifier.

Using conversations

Conversations are central to how most users interact with their web browsers.

A conversation-scoped bean will exist for the duration of a conversation, which is less than that of a session but greater than that of a request. Thus, a conversation spans multiple page requests and represents a subunit of work.

 Methods on conversation
MethodDescription
void begin()Converts the current transient conversation into a long-running conversation
void begin(String id)Converts the current transient conversation into a long-running conversation with a custom ID
void end()Terminates the conversation and converts it to a transient conversation
String getId()Returns the ID of the current conversation
long getTimeout()Returns the timeout of the current conversation in milliseconds
boolean isTransient()Returns true if the current conversation is transient and will be lost at the end of the request
void setTimeout(long timeout)Sets a custom timeout for the current conversation; only applicable if the conversation isn’t transient

Example

image.png

image.png

image.png

  • Item page -view and bid

image.png

  • placeBid.xhtml-confirms the bid

image.png

  • BidController-concludes placing a bid

image.png

Using CDI effectively with EJB 3

  • CDI was added in Java EE 6 and its integration has contin- ued to grow with Java EE 7.
  • EJB and CDI are complementary technologies that enable one another and enable robust Java EE apps to be built relatively easily.
  • CDI greatly simplifies the amount of work necessary to use EJBs—there’s no need to access JNDI or write a custom JNDI abstraction layer.
  • CDI introduces dependency injection and context features as well as some extensions to POJOs.
  • CDI isn’t a replacement for EJBs—EJBs are necessary when security, transactions, remoting, scheduling, asynchro- nous support, and locking are required.
  • EJBs are beans within CDI, so you can approach the problem as if everything is a CDI bean, and you upgrade a CDI bean to an EJB when you require the Enterprise services provided by the EJB container

Because EJBs are also CDI beans, one very important practice is to use @Inject instead of @EJB. The configurability provided by CDI makes testing an application much easier.

@Inject makes it irrelevant whether a bean is an EJB or simply a POJO—at any point in the future, you can upgrade the bean from a POJO to an EJB as the application evolves.