baeldung - Coding and Testing Stuff: CDI Interceptor vs Spring AspectJ

Datetime:2016-08-22 23:34:05          Topic: Spring  Test Engineer           Share

I just released the Master Class of " Learn Spring Security ":

>> CHECK OUT THE COURSE

1. Introduction

The Interceptor pattern is generally used to add new, cross-cutting functionality or logic in an application, and has solid support in a large number of libraries.

In this article we’ll cover and contrast two of these major libraries: CDI interceptors and Spring AspectJ.

2. CDI Interceptor Project Setup

CDI is officially supported for Java EE but some implementations provide support to use CDI in Java SE environment. Weld can be considered as one example of CDI implementation which is supported in Java SE.

In order to use CDI we need to import the Weld library in our POM:

<dependency>
    <groupId>org.jboss.weld.se</groupId>
    <artifactId>weld-se-core</artifactId>
    <version>2.3.5.Final</version>
</dependency>

The most recent Weld library can be found in Maven repository.

Let’s now create a simple interceptor.

3. Introducing the CDI Interceptor

In order to designate classes we needed to intercept, let’s create the interceptor binding:

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

After we’ve defined the interceptor binding we need to define the actual interceptor implementation:

@Audited
@Interceptor
public class AuditedInterceptor {
    public static boolean calledBefore = false;
    public static boolean calledAfter = false;

    @AroundInvoke
    public Object auditMethod(InvocationContext ctx) throws Exception {
        calledBefore = true;
        Object result = ctx.proceed();
        calledAfter = true;
        return result;
    }
}

Every @AroundInvoke method takes a javax.interceptor.InvocationContext argument, returns a java.lang.Object , and can throw an Exception .

And so, when we annotate a method with the new @Audit interface, auditMethod will be invoked first, and only then the target method be proceed as well.

4. Apply the CDI Interceptor

Let’s apply the created interceptor on some business logic:

public class SuperService {
    @Audited
    public String deliverService(String uid) {
        return uid;
    }
}

We’ve created this simple service and annotated the method we wanted to intercept with the @Audited annotation.

To enable the CDI interceptor one need to specify the full class name in the beans.xml file, located in the META-INF directory:

<beans xmlns="http://java.sun.com/xml/ns/javaee"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
      http://java.sun.com/xml/ns/javaee/beans_1_2.xsd">
    <interceptors>
        <class>com.baeldung.interceptor.AuditedInterceptor</class>
    </interceptors>
</beans>

To validate that interceptor is indeed works let’s now run the following test :

public class TestInterceptor {
    Weld weld;
    WeldContainer container;

    @Before
    public void init() {
        weld = new Weld();
        container = weld.initialize();
    }

    @After
    public void shutdown() {
        weld.shutdown();
    }

    @Test
    public void givenTheService_whenMethodAndInterceptorExecuted_thenOK() {
        SuperService superService = container.select(SuperService.class).get();
        String code = "123456";
        superService.deliverService(code);
        
        Assert.assertTrue(AuditedInterceptor.calledBefore);
        Assert.assertTrue(AuditedInterceptor.calledAfter);
    }
}

In this quick test, we first get the bean SuperService from the container, then invoke business method deliverService on it and check that interceptor AuditedInterceptor was actually called by validating it’s state variables.

Also we have @Before and @After annotated methods in which we initialize and shutdown Weld container respectively.

5. CDI Considerations

We can point out the following advantages of CDI interceptors:

  • It is a standard feature of Java EE specification
  • Some CDI implementations libraries can used in Java SE
  • Can be used when we project has severe limitations on third party libraries

The disadvantages of the CDI interceptors are the following:

  • Tight coupling between class with business logic and interceptor
  • Hard to see which classes are intercepted in the project
  • Lack of flexible mechanism to apply interceptors to a group of methods

6. Spring AspectJ

Spring supports a similar implementation of interceptor functionality using AspectJ syntax as well.

First we need to add the following Spring and AspectJ dependencies to POM:

<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-context</artifactId>
    <version>4.3.1.RELEASE</version>
</dependency>
<dependency>
    <groupId>org.aspectj</groupId>
    <artifactId>aspectjweaver</artifactId>
    <version>1.8.9</version>
</dependency>

The most recent versions of Spring contextaspectjweaver can be found in the Maven repository.

We can now create an simple aspect using AspectJ annotation syntax:

@Aspect
public class SpringTestAspect {
    @Autowired
    private List accumulator;

    @Around("execution(* com.baeldung.spring.service.SpringSuperService.*(..))")
    public Object auditMethod(ProceedingJoinPoint jp) throws Throwable {
        String methodName = jp.getSignature().getName();
        accumulator.add("Call to " + methodName);
        Object obj = jp.proceed();
        accumulator.add("Method called successfully: " + methodName);
        return obj;
    }
}

We created an aspect which applies to all the methods of SpringSuperService class – which, for simplicity, looks like this:

public class SpringSuperService {
    public String getInfoFromService(String code) {
        return code;
    }
}

7. Spring AspectJ Aspect Apply

In order to validate that aspect really applies to the service, let’s write the following unit test:

@RunWith(SpringRunner.class)
@ContextConfiguration(classes = { AppConfig.class })
public class TestSpringInterceptor {
    @Autowired
    SpringSuperService springSuperService;

    @Autowired
    private List accumulator;

    @Test
    public void givenService_whenServiceAndAspectExecuted_thenOk() {
        String code = "123456";
        String result = springSuperService.getInfoFromService(code);
        
        Assert.assertThat(accumulator.size(), is(2));
        Assert.assertThat(accumulator.get(0), is("Call to getInfoFromService"));
        Assert.assertThat(accumulator.get(1), is("Method called successfully: getInfoFromService"));
    }
}

In this test we inject our service, call the method and check the result.

Here’s what the configuration looks like:

@Configuration
@EnableAspectJAutoProxy
public class AppConfig {
    @Bean
    public SpringSuperService springSuperService() {
        return new SpringSuperService();
    }

    @Bean
    public SpringTestAspect springTestAspect() {
        return new SpringTestAspect();
    }

    @Bean
    public List getAccumulator() {
        return new ArrayList();
    }
}

One important aspect here in the @EnableAspectJAutoProxy annotation – which enables support for handling components marked with AspectJ’s @Aspect annotation, similar to functionality found in Spring’s XML element.

8. Spring AspectJ Considerations

We can point out the following advantages of Spring AspectJ:

  • Interceptors are decoupled from the business logic
  • Interceptors can benefit from dependency injection
  • Interceptor has all the configuration information in itself
  • Adding new interceptors wouldn’t require augmenting existing code
  • Interceptor has flexible mechanism to choose which methods to intercept
  • Can be used in Java EE and Java SE

The disadvantages of the CDI interceptors are the following:

  • Need to know AspectJ syntax in order to develop interceptors
  • Learning curve for the AspectJ interceptors is higher than for the CDI interceptors

9. Conclusion

In this article we have covered two implementations of interceptor pattern: CDI interceptor and Spring AspectJ. We have covered advantages and disadvantages each of them.

Source code for examples of this article can be found in our repository on GitHub .





About List