SeamFramework.orgCommunity Documentation

Chapter 13. Specialization, inheritance and alternatives

13.1. Using alternative stereotypes
13.2. A minor problem with alternatives
13.3. Using specialization

When you first start developing with CDI, you'll likely be dealing only with a single bean implementation for each bean type. In this case, it's easy to understand how beans get selected for injection. As the complexity of your application grows, multiple occurrences of the same bean type start appearing, either because you have multiple implementations or two beans share a common (Java) inheritance. That's when you have to begin studying the specialization, inheritance and alternative rules to work through unsatisfied or ambiguous dependencies or to avoid certain beans from being called.

The CDI specification recognizes two distinct scenarios in which one bean extends another:

  • The second bean specializes the first bean in certain deployment scenarios. In these deployments, the second bean completely replaces the first, fulfilling the same role in the system.

  • The second bean is simply reusing the Java implementation, and otherwise bears no relation to the first bean. The first bean may not even have been designed for use as a contextual object.

The second case is the default assumed by CDI. It's possible to have two beans in the system with the same part bean type (interface or parent class). As you've learned, you select between the two implementations using qualifiers.

The first case is the exception, and also requires more care. In any given deployment, only one bean can fulfill a given role at a time. That means one bean needs to be enabled and the other disabled. There are a two modifiers involved: @Alternative and @Specializes. We'll start by looking at alternatives and then show the guarantees that specialization adds.

CDI lets you override the implementation of a bean type at deployment time using an alternative. For example, the following bean provides a default implementation of the PaymentProcessor interface:

public class DefaultPaymentProcessor 

      implements PaymentProcessor {
   ...
}

But in our staging environment, we don't really want to submit payments to the external system, so we override that implementation of PaymentProcessor with a different bean:

public @Alternative

class StagingPaymentProcessor 
      implements PaymentProcessor {
   ...
}

or

public @Alternative

class StagingPaymentProcessor 
      extends DefaultPaymentProcessor {
   ...
}

We've already seen how we can enable this alternative by listing its class in the beans.xml descriptor.

But suppose we have many alternatives in the staging environment. It would be much more convenient to be able to enable them all at once. So let's make @Staging an @Alternative stereotype and annotate the staging beans with this stereotype instead. You'll see how this level of indirection pays off. First, we create the stereotype:

@Alternative

@Stereotype
@Retention(RUNTIME)
@Target(TYPE)
public @interface Staging {}

Then we replace the @Alternative annotation on our bean with @Staging:

@Staging

public class StagingPaymentProcessor 
      implements PaymentProcessor {
   ...
}

Finally, we activate the @Staging stereotype in the beans.xml descriptor:


<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">
   <alternatives>
         <stereotype>org.mycompany.myapp.Staging</stereotype>
   </alternatives>
</beans>

Now, no matter how many staging beans we have, they will all be enabled at once.

When we enable an alternative, does that mean the default implementation is disabled? Well, not exactly. If the default implementation has a qualifier, for instance @LargeTransaction, and the alternative does not, you could still inject the default implementation.

@Inject @LargeTransaction PaymentProcessor paymentProcessor;

So we haven't completely replaced the default implementation in this deployment of the system. The only way one bean can completely override a second bean at all injection points is if it implements all the bean types and declares all the qualifiers of the second bean. However, if the second bean declares a producer method or observer method, then even this is not enough to ensure that the second bean is never called! We need something extra.

CDI provides a special feature, called specialization, that helps the developer avoid these traps. Specialization is a way of informing the system of your intent to completely replace and disable an implementation of a bean.

When the goal is to replace one bean implementation with a second, to help prevent developer error, the first bean may:

explicitly declare that it specializes the second bean:

@Alternative @Specializes

public class MockCreditCardPaymentProcessor 
      extends CreditCardPaymentProcessor {
   ...
}

When an enabled bean specializes another bean, the other bean is never instantiated or called by the container. Even if the other bean defines a producer or observer method, the method will never be called.

So why does specialization work, and what does it have to do with inheritance?

Since we're informing the container that our alternative bean is meant to stand in as a replacement for the default implementation, the alternative implementation automatically inherits all qualifiers of the default implementation. Thus, in our example, MockCreditCardPaymentProcessor inherits the qualifiers @Default and @CreditCard.

Furthermore, if the default implementation declares a bean EL name using @Named, the name is inherited by the specialized alternative bean.