Suppose we have an external resource, a database, let's say, that we want to be able to change depending upon the deployment environment. In CDI, we declare resources using a producer field declaration.

class CustomerDatabase {
   static 
   @Resource(lookup="java:global/env/jdbc/CustomerDatasource")
   @Produces @CustomerDatabase
   Datasource customerDatabase;
}

The purpose of this field declaration is to associate the bean type Datasource and binding type @CustomerDatabase with the Java EE resource with the global JNDI name java:global/env/jdbc/CustomerDatasource.

Now we can easily inject our Datasource into any bean that needs it:

public class Bean {
   private Datasource customerDatabase;

   @Inject public Bean(@CustomerDatabase Datasource ds) {
       customerDatabase = ds;
   }
   ...
}

Now, this injected resource reference is already extremely configurable. The JNDI name is just a logical name. The actual datasource it points to can easily change depending upon the deployment. We certainly haven't hardcoded a JDBC URL, username and password! But let's say that's not enough. CDI lets you define alternative implementations of a bean.

class TestCustomerDatabase {
   static 
   @Resource(lookup="java:global/env/jdbc/TestDatasource") 
   @Produces @Alternative @CustomerDatabase 
   Datasource testCustomerDatabase;
}
@Alternative @CustomerDatabase
class MockCustomerDatabase implements Datasource {
   //operations of Datasource go here!
   ...
}

These classes both provide an alternative implementation of @CustomerDatabase Datasource. By default, CDI will ignore these beans, and keep using the original implementation, since it is the only implementation not declared @Alternative.

However, if we declare one of the alternatives in beans.xml, CDI will use that implementation instead:

<beans>
   <alternatives>
      <class>org.mycompany.resources.TestCustomerDatabase</class>
   </alternatives>
</beans>

That's great, and straightforward, when we only have one or two beans that vary depending upon the deployment scenario. Well, I usually consider that to be the case in most applications. However, developers whose brains have been turned to mush by overexposure to the Spring framework think that they have hundreds of beans which might vary depending upon the deployment. That's how they justify writing great gobs of XML to explicitly list out all the classes in their application. We certainly don't want to follow them down that path!

Therefore, CDI provides the ability to define an annotation that represents a deployment scenario. These annotations are called alternative stereotypes. For example:

@Alternative @Stereotype
@Target(TYPE) @Retention(RUNTIME)
public @interface Mock {}
@Alternative @Stereotype
@Target({TYPE, METHOD, FIELD}) @Retention(RUNTIME)
public @interface Test {}

We can now use these annotations to indicate which of our beans belong to which deployment scenario:

class TestCustomerDatabase {
   static 
   @Resource(lookup="java:global/env/jdbc/TestDatasource") 
   @Produces @Test @CustomerDatabase 
   Datasource testCustomerDatabase;
}
@Mock @CustomerDatabase
class MockCustomerDatabase implements Datasource {
   //operations of Datasource go here!
   ...
}

Now, with just a single line of XML, we can collectively enable all @Test beans, or all @Mock beans.

<beans>
   <alternatives>
      <stereotype>org.mycompany.deployment.Test</stereotype>
   </alternatives>
</beans>

Don't you think that's a better way?


Back to top