Let's consider the example that we wrote earlier, where we created a person identity. Let's say we want to invoke some functionality around this identity creation function and that we don't necessarily want to mix this logic with the existing business function.
For instance, let's say we want to add audit functionality, which is a common cross-cutting concern. We can use interceptors to write a function to add audit messages, which will be invoked around the execution of the business function. We use the @Interceptors annotation to do this and then we can provide one or more interceptors for common application concerns:
@Stateless
@Interceptors(AuditInterceptor.class)
class App {
@Inject
private lateinit var identityCreator: IdentityCreator
@Inject
private lateinit var identityRepository: IdentityRepository
fun createIdentity(inputData: InputData): Identity {
val person = identityCreator.createPerson(inputData)
identityRepository.store(person)
return person
}
}
We will create an AuditInterceptor class in the interceptor package that is annotated with the @Interceptor annotation.
This interceptor will specify a handle function that is annotated with @AroundInvoke. The @AroundInvoke annotation declares the handle() function to be invoked around our business functionality.
By injecting the InvocationContext, we can simply use the context.proceed() function to proceed with our business functionality, as follows:
@Interceptor
public class AuditInterceptor {
@AroundInvoke
fun handle(context: InvocationContext){
context.proceed()
}
}
When the createIdentity() function is called, the handler function will be invoked by intercepting the invocation of the createIdentity() function call.
We can now include a CDI-managed bean, namely the Auditor. This class has a single audit() function that simply prints a message to the console once the function has been invoked:
class Auditor {
fun audit(message: String) {
println(message)
}
}
Let's inject the Auditor bean in the AuditInterceptor class and call the audit() function from handle():
@Interceptor
class AuditInterceptor {
@Inject
private lateinit var auditor: Auditor
fun handle(context: InvocationContext) {
auditor.audit("message")
context.proceed()
}
}
Now, the interceptor's handle() function has been invoked around the business logic of creating a person.
@Interceptor
class AuditInterceptor {
@Inject
private lateinit var auditor: Auditor
fun handle(context: InvocationContext) {
auditor.audit("message")
context.parameters.forEach { param -> println(param) }
context.proceed()
}
}
This section has shown how we can intercept some logic in our application without binding too much of the intercepting logic to the business code. The @Interceptors annotation controls whether the interceptor is invoked. This is not tightly bound to the business logic.