How to Use the @Interceptor Annotation to Modify Extension Execution in DDDplus
Annotate a Spring bean with @Interceptor and implement IExtensionInterceptor to intercept extension point calls, enabling pre-processing, post-processing, or short-circuiting of extension execution without modifying the original extension code.
The @Interceptor annotation in the DDDplus framework (also known as cp-ddd-framework) provides a powerful mechanism for applying cross-cutting concerns to extension points. By implementing the IExtensionInterceptor interface and marking your Spring component with this annotation, you can modify extension execution behavior—such as adding logging, transactions, or validation—while keeping extension implementations clean and focused on business logic.
Understanding the @Interceptor Annotation and Interface Contract
The @Interceptor annotation is defined in io.github.dddplus.annotation.Interceptor and serves as a marker for Spring components that should participate in the extension invocation chain. To create a functional interceptor, you must implement the IExtensionInterceptor interface, which defines a single method: Object intercept(InvocationContext ctx) throws Throwable.
The InvocationContext object, defined in io.github.dddplus.runtime.interceptor.InvocationContext, provides access to the target extension, its arguments, and the critical proceed() method. Calling ctx.proceed() invokes the next interceptor in the chain or the actual extension implementation if no further interceptors exist.
How the Framework Registers and Indexes Interceptors
The DDDplus framework handles interceptor lifecycle through a coordinated registration and indexing process during application startup.
Registry Factory and Definition Registration
In RegistryFactory, the framework registers InterceptorDef as a valid registry entry type for the @Interceptor annotation. When Spring instantiates a bean annotated with @Interceptor, the registerBean method in InterceptorDef receives the bean instance.
Bean Extraction and Indexing
The InterceptorDef extracts the underlying interceptor implementation using InternalAopUtils.getTarget to unwrap any Spring proxies, storing the result in interceptorBean. Subsequently, InternalIndexer.index(this) adds the interceptor definition to the global interceptor chain maintained by the runtime. This index is consulted whenever an extension point is invoked to determine which interceptors apply.
Implementing a Custom Extension Interceptor
Creating a custom interceptor requires three steps: implementing the interface, annotating the class, and handling the invocation context.
Here is a complete example demonstrating transaction management around extension execution:
package com.example.interceptors;
import io.github.dddplus.runtime.interceptor.IExtensionInterceptor;
import io.github.dddplus.runtime.interceptor.InvocationContext;
import org.springframework.stereotype.Component;
import io.github.dddplus.annotation.Interceptor;
@Component
@Interceptor
public class TransactionInterceptor implements IExtensionInterceptor {
@Override
public Object intercept(InvocationContext ctx) throws Throwable {
// begin transaction
startTx();
try {
Object ret = ctx.proceed(); // call the original extension
// commit if successful
commitTx();
return ret;
} catch (Throwable ex) {
// rollback on error
rollbackTx();
throw ex;
}
}
private void startTx() { /* ... */ }
private void commitTx() { /* ... */ }
private void rollbackTx() { /* ... */ }
}
When the framework invokes any extension point, the TransactionInterceptor automatically wraps the execution, ensuring transactional consistency without modifying the extension implementation itself.
Controlling Interceptor Execution Order
When multiple interceptors exist, the DDDplus framework respects Spring's standard ordering mechanism. You can control interceptor precedence using Spring's @Order annotation or by implementing the Ordered interface.
The default order is defined by InterceptorDef if no explicit ordering is specified. Interceptors with lower order values execute earlier in the chain, allowing fine-grained control over which concerns (such as logging, security, or transactions) run first.
Key Source Files in the Interceptor Lifecycle
Understanding the source code structure helps when debugging or extending the framework's interception capabilities:
-
io.github.dddplus.annotation.Interceptor(Interceptor.java): Defines the annotation used to mark interceptor beans. -
io.github.dddplus.runtime.registry.InterceptorDef(InterceptorDef.java): Registry entry that processes@Interceptorbeans, unwraps proxies, and stores the interceptor instance. -
io.github.dddplus.runtime.registry.RegistryFactory(RegistryFactory.java): RegistersInterceptorDefas a valid handler for the@Interceptorannotation type. -
io.github.dddplus.runtime.registry.InternalIndexer(InternalIndexer.java): Maintains the global interceptor chain index consulted during extension invocation. -
io.github.dddplus.runtime.interceptor.IExtensionInterceptor(IExtensionInterceptor.java): Interface defining theintercept(InvocationContext)method that all interceptors must implement. -
io.github.dddplus.runtime.interceptor.InvocationContext(InvocationContext.java): Context object providing access to extension metadata and theproceed()method to continue the invocation chain.
Summary
- The
@Interceptorannotation marks Spring beans that implementIExtensionInterceptorto participate in extension point interception. - During startup,
RegistryFactoryandInterceptorDefregister and unwrap interceptor beans, whileInternalIndexerbuilds the global interceptor chain. - At runtime, the framework invokes interceptors in order (respecting Spring's
@Order) before calling the actual extension, allowing pre-processing, post-processing, or short-circuiting viaInvocationContext.proceed(). - This mechanism enables cross-cutting concerns like logging, transactions, and validation without modifying extension implementations.
Frequently Asked Questions
How does the @Interceptor annotation differ from Spring AOP?
While Spring AOP uses proxy-based aspect weaving, the @Interceptor annotation in DDDplus provides a framework-specific interception mechanism tailored for extension points. It integrates with the DDDplus registry system (InterceptorDef and InternalIndexer) to maintain a dedicated interceptor chain for extension invocations, offering more granular control over extension lifecycle events than generic AOP.
Can multiple interceptors be applied to a single extension point?
Yes, you can define multiple interceptors for the same extension point. The framework maintains a global interceptor chain in InternalIndexer that applies to all extension invocations. When multiple interceptors exist, their execution order is determined by Spring's @Order annotation or the Ordered interface, with lower values executing earlier in the chain.
What happens if an interceptor throws an exception?
If an interceptor throws an exception before calling ctx.proceed(), the exception propagates up the call stack and subsequent interceptors in the chain (as well as the target extension) are not executed. If the exception occurs after proceed() but during post-processing, it prevents later interceptors from processing the result and propagates to the caller. This behavior allows interceptors to short-circuit execution for validation or security failures.
Is it possible to modify the arguments passed to the extension?
Yes, interceptors can inspect and modify the arguments available through the InvocationContext before calling ctx.proceed(). While the provided code examples focus on pre/post logic and transaction management, the InvocationContext interface provides access to extension metadata and invocation details, enabling argument validation, transformation, or enrichment before the actual extension implementation receives them.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →