Since Java EE 6 you can invoke Interceptors around method calls. With Interceptors you will add Aspect-oriented Programming (AoP) patterns to your Java EE project. Given you have a method bound to an Interceptor a method call will look like this:
To actually implement this behavior there are some simple steps to do. First implement an Interceptor:
@javax.interceptor.Interceptor
public class OurInterceptor {
@javax.interceptor.AroundInvoke
public Object someMethodName(InvocationContext context) throws Exception {
// do something before method
System.out.println(">before");
//executing source method
Object result = context.proceed();
// do something after method
System.out.println("<after");
return result;
}
}
And register our Interceptor in the beans.xml
:
de.beanbelt.bbinterceptorsandreapeatableannotations.interceptors.OurInterceptor
Now you can simply invoke this Interceptor by annotating a class or method in e.g. an Stateless EJB.
@Stateless
// interceptor will be invoked on every method in OurService
@javax.interceptor.Interceptors(OurInterceptor.class)
public class OurService {
// You can invoke an Interceptor on one method only
// Note: this will overwrite the class-level Interceptor
//@Interceptors(OurInterceptor.class)
public String getName(long id) {
System.out.println("method");
return database.get(id).getName();
}
}
If you execute the code the following will be printed to the console:
>before
method
<after
Now we extends the example by our own annotations which will invoke the interceptor. Create an annotation that looks like the following:
@Inherited
@InterceptorBinding
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface OurAnnotation {
@Nonbinding
boolean someValue() default false;
}
And change the Interceptor like this:
@OurAnnotation
@javax.interceptor.Interceptor
public class OurInterceptor{...}
Now we can use the interceptor on method-level in OurService like this:
@Stateless
public class OurService {
@OurAnnotation(someValue=true)
public String getName(long id) {
System.out.println("method");
return database.get(id).getName();
}
}
As you can see, we can pass parameters to the Annotation which we can read in our interceptor like this:
@OurAnnotation
@javax.interceptor.Interceptor
public class OurInterceptor{
@javax.interceptor.AroundInvoke
public Object someMethodName(InvocationContext context) throws Exception {
Method method = context.getMethod();
if (method.isAnnotationPresent(OurAnnotation.class)) {
boolean someValue = method.getAnnotation(OurAnnotation.class).someValue();
System.out.println("someValue is: "+someValue);
}
// do something before method
System.out.println(">before");
//executing source method
Object result = context.proceed();
// do something after method
System.out.println("<after");
return result;
}
Since we annotated our method with someValue=true the output on the console will look like this:
someValue is: true
>before
method
<after
So far so good but what happens if we want to annotate our method multiple times like this?
@OurAnnotation(someValue=false)
@OurAnnotation(someValue=true)
public String getName(long id) {...}
We have to create a grouping Annotation (Note the S in OurAnnotationS).
@Inherited
@InterceptorBinding
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface OurAnnotations {
@Nonbinding
OurAnnotation[] value();
}
Now we can annotate our method like this:
@OurAnnotations({
@OurAnnotation(someValue=false),
@OurAnnotation(someValue=true)
}) public String getName(long id) {...}
In Java 8 we can use the Repeatable Annotations feature. To do so we just need to add an Annotation to @OurAnnotation
@Inherited
@Repeatable(OurAnnotations.class)
@InterceptorBinding
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface OurAnnotation {...}
Now we can annotate our method as we stated before:
@OurAnnotation(someValue=false)
@OurAnnotation(someValue=true)
public String getName(long id) {...}
But what happened to the Interceptor, why is it not working anymore? Well, we have to implement a new interceptor for our grouping Annotation like this:
@OurAnnotations
@javax.interceptor.Interceptor
public class OurGroupingInterceptor{
@javax.interceptor.AroundInvoke
public Object someMethodName(InvocationContext context) throws Exception {
Method method = context.getMethod();
if (method.isAnnotationPresent(OurAnnotations.class)) {
OurAnnotation[] ourAnnotations = method.getAnnotation(OurAnnotations.class).value();
for (OurAnnotation annotation : ourAnnotations) {
System.out.println("someValue is: "+ annotation.someValue());
}
}
// do something before method
System.out.println(">before");
//executing source method
Object result = context.proceed();
// do something after method
System.out.println("<after");
return result;
}
}
Now the output on the console will look like this:
someValue is: false
someValue is: true
>before
method
<after
Now we successfully implemented an Interceptor with our own Annotation which we can repeat like we want.
Happy Coding!