Stereotypes
The CDI specification defines a stereotype as follows:
In many systems, use of architectural patterns produces a set of recurring bean roles. A stereotype allows a framework developer to identify such a role and declare some common metadata for beans with that role in a central place.
A stereotype encapsulates any combination of:
a default scope, and
a set of interceptor bindings.
A stereotype may also specify that:
all beans with the stereotype have defaulted bean names, or that
all beans with the stereotype are alternatives.
A bean may declare zero, one or multiple stereotypes. Stereotype annotations may be applied to a bean class or producer method or field.
A stereotype is an annotation, annotated @Stereotype
, that packages
several other annotations. For instance, the following stereotype
identifies action classes in some MVC framework:
@Stereotype
@Retention(RUNTIME)
@Target(TYPE)
...
public @interface Action {}
We use the stereotype by applying the annotation to a bean.
@Action
public class LoginAction { ... }
Of course, we need to apply some other annotations to our stereotype or else it wouldn’t be adding much value.
Default scope for a stereotype
A stereotype may specify a default scope for beans annotated with the stereotype. For example:
@RequestScoped
@Stereotype
@Retention(RUNTIME)
@Target(TYPE)
public @interface Action {}
A particular action may still override this default if necessary:
@Dependent @Action
public class DependentScopedLoginAction { ... }
Naturally, overriding a single default isn’t much use. But remember, stereotypes can define more than just the default scope.
Interceptor bindings for stereotypes
A stereotype may specify a set of interceptor bindings to be inherited by all beans with that stereotype.
@RequestScoped
@Transactional(requiresNew=true)
@Secure
@Stereotype
@Retention(RUNTIME)
@Target(TYPE)
public @interface Action {}
This helps us get technical concerns, like transactions and security, even further away from the business code!
Name defaulting with stereotypes
We can specify that all beans with a certain stereotype have a defaulted
EL name when a name is not explicitly defined for that bean. All we need
to do is add an empty @Named
annotation:
@RequestScoped
@Transactional(requiresNew=true)
@Secure
@Named
@Stereotype
@Retention(RUNTIME)
@Target(TYPE)
public @interface Action {}
Now, the LoginAction
bean will have the defaulted name loginAction
.
Alternative stereotypes
A stereotype can indicate that all beans to which it is applied are `@Alternative`s. An alternative stereotype lets us classify beans by deployment scenario.
@Alternative
@Stereotype
@Retention(RUNTIME)
@Target(TYPE)
public @interface Mock {}
We can apply an alternative stereotype to a whole set of beans, and
activate them all with one line of code in beans.xml
.
@Mock
public class MockLoginAction extends LoginAction { ... }
<beans>
<alternatives>
<stereotype>org.mycompany.testing.Mock</stereotype>
</alternatives>
</beans>
Stereotypes with @Priority
A stereotype can declare a @Priority
annotation which then affects enablement and ordering of beans.
This is typically useful in combination with @Alternative
to immediately mark a bean as a globally enabled alternative:
@Alternative
@Priority(Interceptor.Priority.APPLICATION + 5)
@Stereotype
@Target(TYPE)
@Retention(RUNTIME)
public @interface EnabledAlternativeStereotype {}
A @Priority
annotation declared directly on the bean always precedes any @Priority
annotation inherited via stereotypes.
Stereotype stacking
This may blow your mind a bit, but stereotypes may declare other stereotypes, which we’ll call stereotype stacking. You may want to do this if you have two distinct stereotypes which are meaningful on their own, but in other situation may be meaningful when combined.
Here’s an example that combines the @Action
and @Auditable
stereotypes:
@Auditable
@Action
@Stereotype
@Target(TYPE)
@Retention(RUNTIME)
public @interface AuditableAction {}
Built-in stereotypes
CDI defines one standard stereotype, @Model
, which is expected to be
used frequently in web applications:
@Named
@RequestScoped
@Stereotype
@Target({TYPE, METHOD, FIELD})
@Retention(RUNTIME)
public @interface Model {}
Instead of using JSF managed beans, just annotate a bean @Model
, and
use it directly in your JSF view!