SeamFramework.orgCommunity Documentation
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:
directly extend the bean class of the second bean, or
directly override the producer method, in the case that the second bean is a producer method, and then
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.