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?
This looks like the ideal solution to where you have e.g. the same application deployed to multiple customers with different business logic. You write stereotypes @IBM, @Oracle, @Nokia and stick them on the implementations and then just have a build script that toggles the correct stereotype alternative.
Why the hate? Are you trying to win us over by being the tough guy? Show me that your approach is better by design instead of telling me that my brain has been turned to mush. Or is this just the JBoss way of doing things?
Sakuraba, it's not an either/or thing. In this post I showed how our approach is better and mocked you. Two for the price of one.
One switch to flick in order to swap in a whole group beans, as opposed to rewriting your whole dependency injection configuration structure for each different deployment configuration? I'd have to say YES!
Alternatively this feat could be accomplished using Seam 2's (I know, we're supposed to be talking CDI here) @Install annotation, where all 'Test' beans might be installed depending on the presence of some bean that may be configured as a component in components.xml?
I see the CDI approach is typesafe (no string references on dependencies) and there's no need for a possibly pointless bean whose sole purpose is to be the dependency that activates all other 'Test' beans (for example). It also seems a little more straightforward.
You are right. Clearly the damage was done long before (birth?) the developer was introduced to Spring - Spring just cannibalized what was left."
Seriously I think that there have always been technically better approaches [to Spring] and Gavin is demonstrating just a new and standard based one that is certainly on face value a much better approach. But will that be enough to sway the masses of fanboys to move from a proprietary solution to a standard one affording much more competition and innovation? I am afraid it might not be judging by the reactions of the Spring community to the current crap of new [commercial] offerings that fail to even match the basic features in products 5 years ago. The community refers to such things as "cool" and "amazing". The only thing amazing is how gullible the Spring community is at least those within the community that express such opinions. Clearly vmWare thinks the same of the community because no bean factory class is worth millions. Its the flock of sheep that is priceless.
Oh, thats professional. Did writing your post require you to run some MicroContainer as well?
Oh c'mon man lighten up! What is it about Spring fans and not taking criticism well - not even a little light teasing.