multiple bean injection in WebBeans
In my last post, I selected a webbeans implementation out of several drivers, but only one active ATMTransport bean at a time. If I need multiple beans, I need a way to choose the instances I want. In WebBeans, the @BindingType annotations select an active implementation. Like the previous posts, this example is based on Rick Hightower’s DI introduction.
WebBeans encourages semantically-meaningful annotations to organize and document code, and the @BindingType follows this philosophy. If I have two ATMTransport beans, there must be a reason, and I really ought to document that reason in my code. In this case, I’ll use one transport as a backup. If the standard implementation doesn’t connect to the bank, I’ll try again with the backup. So, I’ll create a new @Backup annotation for the backup ATMTransport, and continue to use @Current for the primary ATMTransport.
@Current is just a predefined @BindingType with the implicit meaning of the primary bean of a given type. So I can use @Current instead of creating a new binding type with the same meaning. @Current is only special because its is the default binding type when no others are defined. Otherwise, it’s treated exactly as any other binding.
class ATMImpl {
@Current ATMTransport _transport;
@Backup ATMTransport _backupTransport;
...
}
It’s important that I’ve created meaningful role annotations for my transport, not just names. My ATMImpl doesn’t care about the ATMTransport name; it only cares how that transport can help. In the XML, I’ll bind my selected implementation driver to the binding annotation. In this case, I’ll select a SoapATMTransport for the backup. I could also create a second StandardATMTransport with different configuration.
<WebBeans xmlns="urn:java:javax.webbeans"
xmlns:example="urn:java:example">
<example:SimpleATMTransport>
<Production/>
</example:SimpleATMTransport>
<example:SoapATMTransport>
<example:Backup/>
<example:url>https://mybank.com/atm</example:url>
<Production/>
</example:SoapATMTransport>
</WebBeans>
In the XML configuration, the example:Backup is my @example.Backup annotation, and example:url is a bean property. The <example:Backup/> annotates the SoapATMTransport just as if I had a @Backup annotation on the class itself. Notice, by the way, that none of the beans so far have any names at all. Since the matching is done by the bean types and the set of binding annotations, it’s impossible to match the wrong name with the wrong type, i.e. WebBeans is type safe.
When Resin injects the _backupTransport field, it looks for all the ATMTransport definitions with a @Backup annotation. If it can’t find one, it will report an error.
The @Backup definition itself is an annotation which has the @BindingType meta-annotation. Now, it is a small bit of extra work to create the annotation, but I can use it to document my intention by the annotation name and by any extra discussion in the JavaDoc. Since the @Binding annotation is also used for the XML configuration, I also get automatic validation in case I misspell it as <example:Backrup/>. Plus, my XML gets documented automatically by the JavaDoc.
package example;
import static java.lang.annotation.ElementType.*;
import static java.lang.annotation.RetentionPolicy.*;
import java.lang.annotation.*;
import javax.webbeans.BindingType;
@BindingType
@Documented
@Retention(RUNTIME)
@Target({TYPE, METHOD, FIELD, PARAMETER})
public @interface Backup {
}
Revisiting @Disabled as a @BindingType
In the previous post, I used a @Disabled and @Mock as a @DeploymentType to disable the ATMTransports unless I configured them in the XML. I can rewrite @Disabled as a @BindingType to accomplish the same thing. If the drivers are marked with the @Disabled binding type, they will never match any injection (unless the injection uses @Disabled itself, which would be silly.)
package example;
import static java.lang.annotation.ElementType.*;
import static java.lang.annotation.RetentionPolicy.*;
import java.lang.annotation.*;
import javax.webbeans.BindingType;
@BindingType
@Documented
@Retention(RUNTIME)
@Target({TYPE, METHOD, FIELD, PARAMETER})
public @interface Disabled {
}
Rewriting @Disabled as a @BindingType would slightly simplify my XML, since I could skip the <Production> tag.
<WebBeans xmlns="urn:java:javax.webbeans"
xmlns:example="urn:java:example">
<example:SimpleATMTransport>
<Current/>
</example:SimpleATMTransport>
<example:SoapATMTransport>
<example:Backup/>
<example:url>https://mybank.com/atm</example:url>
</example:SoapATMTransport>
</WebBeans>
Conclusion
Now that I’ve introduced the basic injection, @BindingType, @DeploymentType, and the XML configuration, there should be enough WebBeans background to make some initial comparisons with Hightower’s Spring-based examples in a following post.
