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!
Nice tutorial. do you have some more on how to get values from the method you annotated with the interceptor?
ReplyDelete