(see Listing 11). Instead of using the
@AroundInvoke annotation,
@PostConstruct and @PreDestroy annotations are used to denote methods that
are going to be invoked after the intercepted object is created and destroyed.
In lifecycle callback methods, the
InvocationContext parameter is optional.
Nothing Is Impossible
So far, we have covered the usual way
of intercepting things. Java EE 6 comes
with a Service Provider Interface (SPI),
which allows you to intercept deployment time events and control the configuration of the system. During the
application’s boot time, CDI builds its
metamodel from the types, annotations,
and beans.xml file.
During the recognition, the container
throws events that carry the metainformation. But the events are not
read-only. You can enrich and even
replace the metainformation entirely.
You can add or remove annotations
(such as @InterceptorBinding) and react
to discovery or injection of beans. You
can easily react to your custom annotations or add interceptors dependent
on class names, the class locations, or
whatever trigger you want.
A CDI extension is registered with
the JDK’s JAR extension mechanism.
You have to put a file with the name of
the extension (
javax.enterprise.inject
.spi.Extension) and the name of the
implementation (your extension) as
content. The “extension” file resides
inside the WAR file in the WEB-INF/
classes/META-INF/services/ folder. The
Extension interface is just a marker—you
are not forced to implement any methods. An extension listens to events and
is able to change their state. Listing 12
shows adding the HiPrefixer dynamically.
The BootObserver annotation in Listing 12
listens to all ProcessAnnotated Type
events. The ProcessAnnotated Type event
wraps an Annotated Type instance that
represents a Java class (usually a managed bean or EJB bean).
Unfortunately, Annotated Type is an
immutable, read-only class. To add
additional annotations, you will have to
build your own mutable Annotated Type
implementation, as shown in Listing 13.
To add a custom interceptor without
changing the code at startup, an additional InterceptorBinding annotation,
@Hi, has to be added at boot time. The
list of already existing annotations at the
UniversalGreeter bean has to be extended
with @Hi. The field Set<Annotation>
annotations in Annotated Type Wrapper
(Listing 13) has exactly this purpose. The
method getAnnotations() merges the
custom annotations with annotations
discovered by the CDI runtime.
An annotation instance cannot be
just created. You have to use a helper
class instead:
public class BootObserver implements Extension{
void processUniversalGreeter(@Observes ProcessAnnotated Type event) {
Class javaClass = annotated Type.getJavaClass();
if( javaClass.getName().ends With("UniversalGreeter")){
Annotated Type Wrapper atw = new Annotated Type Wrapper(annotated Type);
atw.addAnnotation(new HiInstance());
event.setAnnotatedType(atw);
}
}
}
JAVA IN ACTION
Download all listings in this issue as text
ABOUT US
In the BootObserver class, we pass
the HiInstance helper instead of the
@Hi annotation.
public class HiInstance implements
Hi{
@Override
public Class<? extends
}
}
Cleaner Code with Decorators
Our interceptor sample has a fundamental problem. Interceptors work
well as generic decorators, but our
sample relies on a specific method
signature. A different method return
type (for example, int) would result in
a ClassCastException at runtime. Also,
decoration of specific methods with custom cross-cutting functionality usually
results in a lot of plumbing. Listing 14
is an (ugly) implementation of method-specific behavior.
With the code in Listing 14, the interceptor would have to examine the
method name or parameters to apply
method-specific behavior. You could
also apply method-specific interceptors
on each method, but a type-safe implementation of the decorator pattern is far
easier to maintain.
Method-dependent cross-cutting
concerns can be cleanly implemented
with @Decorator. The @Decorator annotation in CDI can be considered to be a
type-safe interceptor. Listing 15 shows