More about beans

A bean is usually an application class that contains business logic. It may be called directly from Java code, or it may be invoked via the Unified EL. A bean may access transactional resources. Dependencies between beans are managed automatically by the container. Most beans are stateful and contextual. The lifecycle of a bean is managed by the container.

Let’s back up a second. What does it really mean to be contextual? Since beans may be stateful, it matters which bean instance I have. Unlike a stateless component model (for example, stateless session beans) or a singleton component model (such as servlets, or singleton beans), different clients of a bean see the bean in different states. The client-visible state depends upon which instance of the bean the client has a reference to.

However, like a stateless or singleton model, but unlike stateful session beans, the client does not control the lifecycle of the instance by explicitly creating and destroying it. Instead, the scope of the bean determines:

  • the lifecycle of each instance of the bean and

  • which clients share a reference to a particular instance of the bean.

For a given thread in a CDI application, there may be an active context associated with the scope of the bean. This context may be unique to the thread (for example, if the bean is request scoped), or it may be shared with certain other threads (for example, if the bean is session scoped) or even all other threads (if it is application scoped).

Clients (for example, other beans) executing in the same context will see the same instance of the bean. But clients in a different context may see a different instance (depending on the relationship between the contexts).

One great advantage of the contextual model is that it allows stateful beans to be treated like services! The client need not concern itself with managing the lifecycle of the bean it’s using, nor does it even need to know what that lifecycle is. Beans interact by passing messages, and the bean implementations define the lifecycle of their own state. The beans are loosely coupled because:

  • they interact via well-defined public APIs

  • their lifecycles are completely decoupled

We can replace one bean with another different bean that implements the same interface and has a different lifecycle (a different scope) without affecting the other bean implementation. In fact, CDI defines a simple facility for overriding bean implementations at deployment time, as we will see in Alternatives .

Note that not all clients of a bean are beans themselves. Other objects such as servlets or message-driven beans—which are by nature not injectable, contextual objects—may also obtain references to beans by injection.

The anatomy of a bean

Enough hand-waving. More formally, the anatomy of a bean, according to the spec:

A bean comprises the following attributes:

  • A (nonempty) set of bean types

  • A (nonempty) set of qualifiers

  • A scope

  • Optionally, a bean EL name

  • A set of interceptor bindings

  • A bean implementation

Furthermore, a bean may or may not be an alternative.

Let’s see what all this new terminology means.

Bean types, qualifiers and dependency injection

Beans usually acquire references to other beans via dependency injection. Any injected attribute specifies a "contract" that must be satisfied by the bean to be injected. The contract is:

  • a bean type, together with

  • a set of qualifiers.

A bean type is a user-defined class or interface; a type that is client-visible. If the bean is an EJB session bean, the bean type is the @Local interface or bean-class local view. A bean may have multiple bean types. For example, the following bean has four bean types:

public class BookShop
      extends Business
      implements Shop<Book> {
   ...
}

The bean types are BookShop, Business and Shop<Book>, as well as the implicit type java.lang.Object. (Notice that a parameterized type is a legal bean type).

Meanwhile, this session bean has only the local interfaces BookShop, Auditable and java.lang.Object as bean types, since the bean class, BookShopBean is not a client-visible type.

@Stateful
public class BookShopBean
      extends Business
      implements BookShop, Auditable {
   ...
}
Note
The bean types of a session bean include local interfaces and the bean class local view (if any). EJB remote interfaces are not considered bean types of a session bean. You can’t inject an EJB using its remote interface unless you define a resource, which we’ll meet in Java EE component environment resources .

Bean types may be restricted to an explicit set by annotating the bean with the @Typed annotation and listing the classes that should be bean types. For instance, the bean types of this bean have been restricted to Shop<Book>, together with java.lang.Object:

@Typed(Shop.class)
public class BookShop
      extends Business
      implements Shop<Book> {
   ...
}

Sometimes, a bean type alone does not provide enough information for the container to know which bean to inject. For instance, suppose we have two implementations of the PaymentProcessor interface: CreditCardPaymentProcessor and DebitPaymentProcessor. Injecting a field of type PaymentProcessor introduces an ambiguous condition. In these cases, the client must specify some additional quality of the implementation it is interested in. We model this kind of "quality" using a qualifier.

A qualifier is a user-defined annotation that is itself annotated @Qualifier. A qualifier annotation is an extension of the type system. It lets us disambiguate a type without having to fall back to string-based names. Here’s an example of a qualifier annotation:

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

You may not be used to seeing the definition of an annotation. In fact, this might be the first time you’ve encountered one. With CDI, annotation definitions will become a familiar artifact as you’ll be creating them from time to time.

Note
Pay attention to the names of the built-in annotations in CDI and EJB. You’ll notice that they are often adjectives. We encourage you to follow this convention when creating your custom annotations, since they serve to describe the behaviors and roles of the class.

Now that we have defined a qualifier annotation, we can use it to disambiguate an injection point. The following injection point has the bean type PaymentProcessor and qualifier @CreditCard:

@Inject @CreditCard PaymentProcessor paymentProcessor

For each injection point, the container searches for a bean which satisfies the contract, one which has the bean type and all the qualifiers. If it finds exactly one matching bean, it injects an instance of that bean. If it doesn’t, it reports an error to the user.

How do we specify that qualifiers of a bean? By annotating the bean class, of course! The following bean has the qualifier @CreditCard and implements the bean type PaymentProcessor. Therefore, it satisfies our qualified injection point:

@CreditCard
public class CreditCardPaymentProcessor
    implements PaymentProcessor { ... }
Note
If a bean or an injection point does not explicitly specify a qualifier, it has the default qualifier, @Default.

That’s not quite the end of the story. CDI also defines a simple resolution rule that helps the container decide what to do if there is more than one bean that satisfies a particular contract. We’ll get into the details in Dependency injection and programmatic lookup .

Scope

The scope of a bean defines the lifecycle and visibility of its instances. The CDI context model is extensible, accommodating arbitrary scopes. However, certain important scopes are built into the specification, and provided by the container. Each scope is represented by an annotation type.

For example, any web application may have session scoped bean:

public @SessionScoped
class ShoppingCart implements Serializable { ... }

An instance of a session-scoped bean is bound to a user session and is shared by all requests that execute in the context of that session.

Note
Keep in mind that once a bean is bound to a context, it remains in that context until the context is destroyed. There is no way to manually remove a bean from a context. If you don’t want the bean to sit in the session indefinitely, consider using another scope with a shorted lifespan, such as the request or conversation scope.

If a scope is not explicitly specified, then the bean belongs to a special scope called the dependent pseudo-scope. Beans with this scope live to serve the object into which they were injected, which means their lifecycle is bound to the lifecycle of that object.

We’ll talk more about scopes in Scopes and contexts .

EL name

If you want to reference a bean in non-Java code that supports Unified EL expressions, for example, in a JSP or JSF page, you must assign the bean an EL name.

The EL name is specified using the @Named annotation, as shown here:

public @SessionScoped @Named("cart")
class ShoppingCart implements Serializable { ... }

Now we can easily use the bean in any JSF or JSP page:

<h:dataTable value="#{cart.lineItems}" var="item">
   ...
</h:dataTable>
Note
The @Named annotation is not what makes the class a bean. Most classes in a bean archive are already recognized as beans. The @Named annotation just makes it possible to reference the bean from the EL, most commonly from a JSF view.

We can let CDI choose a name for us by leaving off the value of the @Named annotation:

public @SessionScoped @Named
class ShoppingCart implements Serializable { ... }

The name defaults to the unqualified class name, decapitalized; in this case, shoppingCart.

Alternatives

We’ve already seen how qualifiers let us choose between multiple implementations of an interface at development time. But sometimes we have an interface (or other bean type) whose implementation varies depending upon the deployment environment. For example, we may want to use a mock implementation in a testing environment. An alternative may be declared by annotating the bean class with the @Alternative annotation.

public @Alternative
class MockPaymentProcessor extends PaymentProcessorImpl { ... }

We normally annotate a bean @Alternative only when there is some other implementation of an interface it implements (or of any of its bean types). We can choose between alternatives at deployment time by selecting an alternative in the CDI deployment descriptor META-INF/beans.xml of the jar or Java EE module that uses it. Different modules can specify that they use different alternatives. The other way to enable an alternative is to annotate the bean with @Priority annotation. This will enable it globally.

We cover alternatives in more detail in Alternatives .

Interceptor binding types

You might be familiar with the use of interceptors in EJB 3. Since Java EE 6, this functionality has been generalized to work with other managed beans. That’s right, you no longer have to make your bean an EJB just to intercept its methods. Holler. So what does CDI have to offer above and beyond that? Well, quite a lot actually. Let’s cover some background.

The way that interceptors were defined in Java EE 5 was counter-intuitive. You were required to specify the implementation of the interceptor directly on the implementation of the EJB, either in the @Interceptors annotation or in the XML descriptor. You might as well just put the interceptor code in the implementation! Furthermore, the order in which the interceptors are applied is taken from the order in which they are declared in the annotation or the XML descriptor. Perhaps this isn’t so bad if you’re applying the interceptors to a single bean. But, if you are applying them repeatedly, then there’s a good chance that you’ll inadvertently define a different order for different beans. Now that’s a problem.

CDI provides a new approach to binding interceptors to beans that introduces a level of indirection (and thus control). We must define an interceptor binding type to describe the behavior implemented by the interceptor.

An interceptor binding type is a user-defined annotation that is itself annotated @InterceptorBinding. It lets us bind interceptor classes to bean classes with no direct dependency between the two classes.

@InterceptorBinding
@Inherited
@Target( { TYPE, METHOD })
@Retention(RUNTIME)
public @interface Transactional {}

The interceptor that implements transaction management declares this annotation:

public @Transactional @Interceptor
class TransactionInterceptor { ... }

We can apply the interceptor to a bean by annotating the bean class with the same interceptor binding type:

public @SessionScoped @Transactional
class ShoppingCart implements Serializable { ... }

Notice that ShoppingCart and TransactionInterceptor don’t know anything about each other.

Interceptors are deployment-specific. (We don’t need a TransactionInterceptor in our unit tests!) By default, an interceptor is disabled. We can enable an interceptor using the CDI deployment descriptor META-INF/beans.xml of the jar or Java EE module. This is also where we specify the interceptor ordering. Better still, we can use @Priority annotation to enable the interceptor and define it’s ordering at the same time.

We’ll discuss interceptors, and their cousins, decorators, in Interceptors and Decorators .

What kinds of classes are beans?

We’ve already seen two types of beans: JavaBeans and EJB session beans. Is that the whole story? Actually, it’s just the beginning. Let’s explore the various kinds of beans that CDI implementations must support out-of-the-box.

Managed beans

A managed bean is a Java class. The basic lifecycle and semantics of a managed bean are defined by the Managed Beans specification. According to the specification, the CDI container treats any class that satisfies the following conditions as a managed bean:

  • It is either:

    • an abstract or non-abstract class annotated @Decorator, or

    • a non-abstract class.

  • It declares either:

    • a constructor with no parameters, or

    • a constructor annotated @Inject

  • It is not an inner class.

  • It does not implement jakarta.enterprise.inject.spi.Extension

  • It does not implement jakarta.enterprise.inject.build.compatible.spi.BuildCompatibleExtension.

  • It is not annotated @Vetoed

  • It is not in a package annotated @Vetoed

  • It is not annotated with an EJB component-defining annotation

  • It is not declared as an EJB bean class in ejb-jar.xml.

Note
According to this definition, JPA entities are technically managed beans. However, entities have their own special lifecycle, state and identity model and are usually instantiated by JPA or using new. Therefore we don’t recommend directly injecting an entity class. We especially recommend against assigning a scope other than @Dependent to an entity class, since JPA is not able to persist injected CDI proxies.

The unrestricted set of bean types for a managed bean contains the bean class, every superclass and all interfaces it implements directly or indirectly.

If a managed bean has a public field, it must have the default scope @Dependent.

Managed beans support the @PostConstruct and @PreDestroy lifecycle callbacks.

Session beans are also, technically, managed beans. However, since they have their own special lifecycle and take advantage of additional enterprise services, the CDI specification considers them to be a different kind of bean.

Session beans

Session beans belong to the EJB specification. They have a special lifecycle, state management and concurrency model that is different to other managed beans and non-managed Java objects. But session beans participate in CDI just like any other bean. You can inject one session bean into another session bean, a managed bean into a session bean, a session bean into a managed bean, have a managed bean observe an event raised by a session bean, and so on.

Note
Message-driven and entity beans are by nature non-contextual objects and may not be injected into other objects. However, message-driven beans can take advantage of some CDI functionality, such as dependency injection, interceptors and decorators. In fact, CDI will perform injection into any session or message-driven bean, even those which are not contextual instances.

The unrestricted set of bean types for a session bean contains all local interfaces of the bean and their superinterfaces. If the session bean has a bean class local view, the unrestricted set of bean types contains the bean class and all superclasses. In addition, java.lang.Object is a bean type of every session bean. But remote interfaces are not included in the set of bean types.

There’s no reason to explicitly declare the scope of a stateless session bean or singleton session bean. The EJB container controls the lifecycle of these beans, according to the semantics of the @Stateless or @Singleton declaration. On the other hand, a stateful session bean may have any scope.

Stateful session beans may define a remove method, annotated @Remove, that is used by the application to indicate that an instance should be destroyed. However, for a contextual instance of the bean—an instance under the control of CDI—this method may only be called by the application if the bean has scope @Dependent. For beans with other scopes, the application must let the container destroy the bean.

So, when should we use a session bean instead of a plain managed bean? Whenever we need the advanced enterprise services offered by EJB, such as:

  • method-level transaction management and security,

  • concurrency management,

  • instance-level passivation for stateful session beans and instance-pooling for stateless session beans,

  • remote or web service invocation, or

  • timers and asynchronous methods,

When we don’t need any of these things, an ordinary managed bean will serve just fine.

Many beans (including any @SessionScoped or @ApplicationScoped beans) are available for concurrent access. Therefore, the concurrency management provided by EJB 3.2 is especially useful. Most session and application scoped beans should be EJBs.

Beans which hold references to heavy-weight resources, or hold a lot of internal state benefit from the advanced container-managed lifecycle defined by the EJB stateless/stateful/singleton model, with its support for passivation and instance pooling.

Finally, it’s usually obvious when method-level transaction management, method-level security, timers, remote methods or asynchronous methods are needed.

The point we’re trying to make is: use a session bean when you need the services it provides, not just because you want to use dependency injection, lifecycle management, or interceptors. Java EE 7 provides a graduated programming model. It’s usually easy to start with an ordinary managed bean, and later turn it into an EJB just by adding one of the following annotations: @Stateless, @Stateful or @Singleton.

On the other hand, don’t be scared to use session beans just because you’ve heard your friends say they’re "heavyweight". It’s nothing more than superstition to think that something is "heavier" just because it’s hosted natively within the Java EE container, instead of by a proprietary bean container or dependency injection framework that runs as an additional layer of obfuscation. And as a general principle, you should be skeptical of folks who use vaguely defined terminology like "heavyweight".

Producer methods

Not everything that needs to be injected can be boiled down to a bean class instantiated by the container using new. There are plenty of cases where we need additional control. What if we need to decide at runtime which implementation of a type to instantiate and inject? What if we need to inject an object that is obtained by querying a service or transactional resource, for example by executing a JPA query?

A producer method is a method that acts as a source of bean instances. The method declaration itself describes the bean and the container invokes the method to obtain an instance of the bean when no instance exists in the specified context. A producer method lets the application take full control of the bean instantiation process.

A producer method is declared by annotating a method of a bean class with the @Produces annotation.

import jakarta.enterprise.inject.Produces;

@ApplicationScoped
public class RandomNumberGenerator {

   private java.util.Random random = new java.util.Random(System.currentTimeMillis());

   @Produces @Named @Random int getRandomNumber() {
      return random.nextInt(100);
   }

}

We can’t write a bean class that is itself a random number. But we can certainly write a method that returns a random number. By making the method a producer method, we allow the return value of the method—in this case an Integer—to be injected. We can even specify a qualifier—in this case @Random, a scope—which in this case defaults to @Dependent, and an EL name—which in this case defaults to randomNumber according to the JavaBeans property name convention. Now we can get a random number anywhere:

@Inject @Random int randomNumber;

Even in a Unified EL expression:

<p>Your raffle number is #{randomNumber}.</p>

A producer method must be a non-abstract method of a managed bean class or session bean class. A producer method may be either static or non-static. If the bean is a session bean, the producer method must be either a business method of the EJB or a static method of the bean class.

The bean types of a producer method depend upon the method return type:

  • If the return type is an interface, the unrestricted set of bean types contains the return type, all interfaces it extends directly or indirectly and java.lang.Object.

  • If a return type is primitive or is a Java array type, the unrestricted set of bean types contains exactly two types: the method return type and java.lang.Object.

  • If the return type is a class, the unrestricted set of bean types contains the return type, every superclass and all interfaces it implements directly or indirectly.

Note
Producer methods and fields may have a primitive bean type. For the purpose of resolving dependencies, primitive types are considered to be identical to their corresponding wrapper types in java.lang.

If the producer method has method parameters, the container will look for a bean that satisfies the type and qualifiers of each parameter and pass it to the method automatically—another form of dependency injection.

@Produces Set<Roles> getRoles(User user) {
   return user.getRoles();
}

We’ll talk much more about producer methods in Producer methods .

Producer fields

A producer field is a simpler alternative to a producer method. A producer field is declared by annotating a field of a bean class with the @Produces annotation—the same annotation used for producer methods.

import jakarta.enterprise.inject.Produces;

public class Shop {
   @Produces PaymentProcessor paymentProcessor = ....;
   @Produces @Catalog List<Product> products = ....;
}

The rules for determining the bean types of a producer field parallel the rules for producer methods.

A producer field is really just a shortcut that lets us avoid writing a useless getter method. However, in addition to convenience, producer fields serve a specific purpose as an adaptor for Java EE component environment injection, but to learn more about that, you’ll have to wait until Java EE component environment resources . Because we can’t wait to get to work on some examples.