Showing posts with label Java EE. Show all posts
Showing posts with label Java EE. Show all posts

22 April 2014

Working with Java EE Interceptors and Repeatable Annotations

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:

Overview and Behavior of Interceptors

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!