CDI Interceptors Decorators and Alternatives tutorial

In this tutorial we will learn three features of CDI programming: Interceptors, Decorators and Alternatives. All these features allow you to perform a separation of concerns in your applications in a type safe manner through annotations.

Using CDI Interceptors

An Interceptor is a class that is used to interpose in method invocations or lifecycle events that occur in an associated target class. The interceptor performs a separation of concern activity by doing tasks, such as logging or auditing, that are not related with the business logic of the application and that are repeated often within an application.

CDI Interceptors are pretty much the same as EJB interceptors. However, to apply them you need a qualifier for it. For example, here is an Auditing Qualifier:

@InterceptorBinding
@Retention(RUNTIME)
@Target({METHOD,TYPE})
public @interface Auditing {
}

Notice the @InterceptorBinding annotation which differentiates this specifies that this qualifier will be used as an Interceptor.

And now will code the Interceptor definition, which bears the qualifier annotation (@Auditing) at the top of it:

@Interceptor
@Auditing
public class AuditInterceptor implements Serializable {
    @AroundInvoke
     public Object auditMethodEntry(InvocationContext ctx) throws Exception {
           System.out.println("Before entering method:" + ctx.getMethod().getName());
           return ctx.proceed();
     }
}

Now we’re ready to enable our Interceptor for our application. You can do it by adding the qualifier that is bound to the Interceptor. So, for example, if you want to activate auditing of the following Player class, then you can add the @Auditing annotation either at class level:

@SessionScoped
@Named
@Auditing
public class Player implements Serializable {

    @Inject
    @Anagram
    Word word;

    int attempt = 1;
    int maxAttempts = 5;
    private String guess;
    private boolean endgame;

    public void check() {
        if (guess.equals(word.getSolution())) {
            endgame = true;
            printMessage("You guessed! Click Restart");
        } else {
            printMessage("Wrong guess!");
            attempt++;
        }
        if (attempt == maxAttempts) {
            printMessage("You lost! Click Restart");
            endgame = true;
        }

    }

    private void printMessage(String string) {
        FacesContext.getCurrentInstance().addMessage(null,
                new FacesMessage(FacesMessage.SEVERITY_INFO, string, string));

    }

    public void restart() {
        ExternalContext ec = FacesContext.getCurrentInstance()
                .getExternalContext();
        ec.invalidateSession();
        try {
            ec.redirect(ec.getRequestContextPath() + "/index.xhtml");
        } catch (IOException e) {
            e.printStackTrace();
        }

    }
   // getters/ setters
}

As an alternative, you can also apply the Interceptor annotation just to a single method so that auditing will span just during the method invocation:

@PostConstruct
@Auditing
public void retrieveAllItems() {         . . . . . }

Finally, since Interceptors are disabled by default, we need to enable our Interceptor within the beans.xml file of a bean archive:

<interceptors>
    <class>com.itbuzzpress.cdi.interceptors.AuditInterceptor</class>
</interceptors>

Two things to note:

  1. Activation of an interceptor within beans.xml will activate it for all beans within the same archive only.
  2. The interceptor’s ordering is determined by the order of the <class> elements within beans.xml.

Source code for this application: https://github.com/fmarchioni/practical-enterprise-development/tree/master/code/cdi/ee-cdi-interceptor

Multiple Interceptors

A single class or method can have multiple Interceptors attached. In such a case, you can define the order in which they are triggered using the @Priority annotation along with a priority value. For example:

@Priority(Interceptor.Priority.APPLICATION+1)
@Auditing
public class AuditInterceptor implements Serializable { . . . }

The priority values should be based on a list of constants contained in the jakarta.interceptor.Interceptor.Priority class as displayed by the following table:

Priority Constant

Interceptor.Priority.PLATFORM_BEFORE

0

Interceptor.Priority.LIBRARY_BEFORE

1000

Interceptor.Priority.APPLICATION

2000

Interceptor.Priority.LIBRARY_AFTER

3000

Interceptor.Priority.PLATFORM_AFTER

4000

As you can see, the priority that you choose places the Interceptor in a different point of the Interceptor chain. The rule is that Interceptors with smaller priority values are called first

Inheriting Interceptors

If you have several Interceptors in your chain, you can allow your Interceptor inherit from another one; hence, with a single annotation you can describe a complex chain of Interceptors.

This can be done by adding the @Inherited annotation and including the qualifiers we are inheriting from inside our qualifier. Here, the @ComplexLog qualifier inherits from our @Auditing qualifier:

@Inherited
@InterceptorBinding
@Target({ TYPE, METHOD })
@Retention(RUNTIME)
@Auditing
public @interface ComplexLog {
}

Now, once you have defined your ComplexLog Interceptor class, you can trigger both Interceptors by simply adding to your Bean the @ComplexLog annotation:

@ComplexLog
public class Player implements Serializable  { . . .}

Using CDI Decorators

So you know that Interceptors are not aware of the semantics of what they intercept, hence they are not the right component for separating business concerns; that’s where decorators come into play.

Decorators intercept methods on a specific interface, so they know everything about the context in which that interface is used. For this reason, decorators are a good option for enhancing the capabilities of classes of the same type. In most cases, it would be possible to use the standard abstract class pattern instead of a decorator, but then we lose the ability to disable and enable that functionality as required.

In order to use a decorator we need at first to define a common interface to be used by both our target bean class and our decorator. For example, suppose we want to decorate the Player Bean class. Here are some methods that we will expose through the PlayerItf interface:

public interface PlayerItf {
        public String check();
        public String getGuess();
        public void setGuess(String guess);
}

Now we will add the Decorator class implementation, which contains a @Decorator annotation, meaning that it will be able to intercept calls to the top interface implemented:

@Decorator
public class PlayerDecorator implements PlayerItf,Serializable {
        @Inject @Delegate PlayerItf player;
        @Override
        public String check()  {
            System.out.println("[Decorator] User check with "+player.getGuess());
            return player.check();
        }
        public String getGuess() {
                return player.getGuess();
        }
        public void setGuess(String guess) {
                player.setGuess(guess.toUpperCase());
        }
}

Then, as we said, the PlayerItf interface needs to be implemented as well in the target class:

public class Player implements Serializable, PlayerItf { . . . }

The main difference between a decorator and an Interceptor is that the Decorator must have a delegate injection point (annotated with @Delegate) with the same type as the beans that they decorate. This allows the decorator to invoke business methods on the delegate object.

Decorators, just like Interceptors, are not active by default; you need to enable them in your beans.xml file as follows:

<decorators>
        <class> com.itbuzzpress.cdi.decorator.PlayerDecorator</class>
</decorators>

Pro Tip

If a method has both interceptors and decorators, interceptors are called first

Source code for this example: https://github.com/fmarchioni/practical-enterprise-development/tree/master/code/cdi/ee-cdi-decorator

Using Alternative Beans

If you need to vary which implementation is chosen depending on the deployment environment or other factors, then you can create an Alternative bean that can be activated when needed.

A concrete example will make things look simpler. In our Player Bean implementation, we account for the maximum number of attempts with a class field:

int maxAttempts = 5;

Let’s rewrite this implementation, using a top level interface, which wraps this field in a method:

public interface Rules extends Serializable{
        public int getMaxAttempts();
}

Now let’s write two Alternative implementation of this interface: the first one will be used to make the game easier, the second one harder. Here is the simple class named Simple:

@Alternative
public class Simple implements Rules {
        private int maxAttempts = 6;
        public int getMaxAttempts() {
                return maxAttempts;
        }
}

And here’s the other class named Hard:

@Alternative
public class Hard implements Rules {
        private int maxAttempts = 3;
        public int getMaxAttempts() {
                return maxAttempts;
        }
}

In order to use these implementations, you need an injection point in your Player class:

public class Player implements Serializable, PlayerItf  {
        @Inject Rules rules;
       //. . . . .
}

If you try to compile and deploy your project as it is, an exception will be raised as the CDI container finds two possible implementations for the Rules injection point. Hence, you need to set the actual Alternative implementation in your beans.xml as follows:

<alternatives>
        <class>com.itbuzzpress.cdi.bean.Hard</class>
</alternatives>

Source code for this application: https://github.com/fmarchioni/practical-enterprise-development/tree/master/code/cdi/ee-cdi-alternative

Decorators vs Interceptors vs Alternatives

Decorators bear some similarities with Interceptors and with  Alternatives as well.

Decorators and Interceptors are similar because they can be both used to “enrich” or “decorate” one method call with additional tasks. However Interceptors are used for general tasks, which are not related with your class in particular (e.g. auditing). On the other hand, Decorators, do bear with them the Bean attributes/operations so they can actually specialize or override a specific business functionality

Decorators and Alternatives are similar as well because they can be used to provide different implementations for your use cases. However Alternative beans can be used to provide a whole different bean implementation, while decorators can be used to further specialize a single method/attribute and they allow as well interacting with the method/attribute we are going to specialize.

Found the article helpful? if so please follow us on Socials