specialization.xml 8.44 KB
<!DOCTYPE chapter PUBLIC "-//OASIS//DTD DocBook XML V4.5//EN"
   "http://www.oasis-open.org/docbook/xml/4.5/docbookx.dtd"  [ ]>
<chapter id="specialization">

   <title>Specialization, inheritance and alternatives</title>

   <para>
      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.
   </para>

   <para>
      The CDI specification recognizes two distinct scenarios in which one bean extends another:
   </para>

   <blockquote>
   <itemizedlist>
      <listitem>
         <para>
            The second bean <emphasis>specializes</emphasis> the first bean in certain deployment scenarios. In these 
            deployments, the second bean completely replaces the first, fulfilling the same role in the system.
         </para>
      </listitem>
      <listitem>
         <para>
            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.
         </para>
      </listitem>
   </itemizedlist>
   </blockquote>

   <para>
      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.
   </para>

   <para>
      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: <literal>@Alternative</literal> and <literal>@Specializes</literal>. We'll start by looking at
      alternatives and then show the guarantees that specialization adds.
   </para>

   <section id="alternativestereotypes">
      <title>Using alternative stereotypes</title>

      <para>
         CDI lets you <emphasis>override</emphasis> the implementation of a bean type at deployment time using an
         alternative. For example, the following bean provides a default implementation of the 
         <literal>PaymentProcessor</literal> interface:
      </para>
  
      <programlisting role="JAVA"><![CDATA[public class DefaultPaymentProcessor 
      implements PaymentProcessor {
   ...
}]]></programlisting>

      <para>
         But in our staging environment, we don't really want to submit payments to the external system, so we override 
         that implementation of <literal>PaymentProcessor</literal> with a different bean:
      </para>
  
      <programlisting role="JAVA"><![CDATA[public @Alternative
class StagingPaymentProcessor 
      implements PaymentProcessor {
   ...
}]]></programlisting>

      <para>or</para>

      <programlisting role="JAVA"><![CDATA[public @Alternative
class StagingPaymentProcessor 
      extends DefaultPaymentProcessor {
   ...
}]]></programlisting>

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

      <para>
         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 <literal>@Staging</literal> an <literal>@Alternative</literal> 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:
      </para>

      <programlisting role="JAVA"><![CDATA[@Alternative
@Stereotype
@Retention(RUNTIME)
@Target(TYPE)
public @interface Staging {}]]></programlisting>

      <para>
         Then we replace the <literal>@Alternative</literal> annotation on our bean with <literal>@Staging</literal>:
      </para>

      <programlisting role="JAVA"><![CDATA[@Staging
public class StagingPaymentProcessor 
      implements PaymentProcessor {
   ...
}]]></programlisting>

      <para>
         Finally, we activate the <literal>@Staging</literal> stereotype in the <literal>beans.xml</literal> descriptor:
      </para>

      <programlisting role="XML"><![CDATA[<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>]]></programlisting>

      <para>
         Now, no matter how many staging beans we have, they will all be enabled at once. 
      </para>
      
   </section>
    
   <section>
      <title>A minor problem with alternatives</title>
    
      <para>
         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 <literal>@LargeTransaction</literal>, and the alternative 
         does not, you could still inject the default implementation.
      </para>

      <programlisting role="JAVA"><![CDATA[@Inject @LargeTransaction PaymentProcessor paymentProcessor;]]></programlisting>

      <para>
         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.
      </para>
  
      <para>
         CDI provides a special feature, called <emphasis>specialization</emphasis>, 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.
      </para>
      
   </section>
  
   <section>
      <title>Using specialization</title>

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

      <itemizedlist>
         <listitem>
            <para>
               directly extend the bean class of the second bean, or
            </para>
         </listitem>
         <listitem>
            <para>
               directly override the producer method, in the case that the second bean is a producer method, and then
            </para>
         </listitem>
      </itemizedlist>

      <para>explicitly declare that it <emphasis>specializes</emphasis> the second bean:</para>

      <programlisting role="JAVA"><![CDATA[@Alternative @Specializes
public class MockCreditCardPaymentProcessor 
      extends CreditCardPaymentProcessor {
   ...
}]]></programlisting>
      
      <para>
         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.
      </para>
      
      <para>
         So why does specialization work, and what does it have to do with inheritance?
      </para>
      
      <para>
         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, <literal>MockCreditCardPaymentProcessor</literal> inherits the qualifiers 
         <literal>@Default</literal> and <literal>@CreditCard</literal>.
      </para>

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

   </section>
  
<!--
vim:et:ts=3:sw=3:tw=120
-->
</chapter>