Can Java Interceptors Avoid Dynamic Proxies? Feat’s AOT Solution Explained
Feat 2.3.0 introduces compile‑time generated interceptors that replace Spring‑style runtime proxies, showing how AOT weaving eliminates reflection overhead, reduces startup cost, and offers a clean stack trace, while outlining the trade‑offs, limitations, and practical usage guidelines for Java developers.
Version updates in Feat 2.3.0
Fixes a large‑body
PostInputStream StackOverflowError, upgrades the underlying smart-socket library to version 2.1.3, changes the default loading order of @Bean from 0 to Byte.MAX_VALUE, and adds a Nacos configuration example.
Interceptor syntax
Define an interceptor class:
@Interceptor
public class LoggingInterceptor {
@AroundInvoke
public Object intercept(InvocationContext context) throws Exception {
String methodName = context.getMethod().getDeclaringClass().getSimpleName() + "." + context.getMethod().getName();
long start = System.nanoTime();
System.out.println("进入 " + methodName + ", 参数=" + java.util.Arrays.toString(context.getParameters()));
try {
Object result = context.proceed();
System.out.println("完成 " + methodName + ", 返回值=" + result);
return result;
} finally {
long elapsed = System.nanoTime() - start;
System.out.println("耗时 " + elapsed + " ns");
}
}
}Bind the interceptor to a method:
@Controller("orders")
public class OrderController {
@RequestMapping("/42")
@Interceptors(LoggingInterceptor.class)
public String findOrder() {
return "order-42";
}
}After running mvn compile and invoking the endpoint, the console prints:
进入 OrderController.findOrder, 参数=[]
完成 OrderController.findOrder, 返回值=order-42
耗时 325100 nsCompile‑time generation vs runtime proxy
During mvn compile, Feat’s AOT processor scans for @Interceptor, @AroundInvoke and @Interceptors, then generates new Java source files. For each intercepted class a subclass is created; the overridden methods contain the ordered interceptor chain. No reflection or proxy objects are used at runtime; the call becomes a plain Java method invocation.
Comparison of dimensions:
Weaving timing : Spring AOP – runtime during bean initialization; Feat – compile time via mvn compile.
Call overhead : Spring AOP – proxy + reflection/method handle; Feat – ordinary Java method call.
Startup burden : Spring AOP – creates proxies and resolves pointcuts; Feat – call chain already determined at compile time.
Debug stack : Spring AOP – includes proxy classes; Feat – generated plain class, clean stack.
Usage limits : Spring AOP – cannot proxy final methods/classes; Feat – cannot generate subclass for final classes or override final methods.
Flexibility : Spring AOP – supports runtime expression matching and dynamic adjustments; Feat – binding is fixed at compile time, immutable at runtime.
Why Feat retains only core capabilities
Feat keeps three essential actions:
Declare an interceptor with @Interceptor.
Bind it to a class or method using @Interceptors.
Advance the chain with context.proceed().
Two reasons drive this reduction:
In most real‑world scenarios interceptors are used for logging, permission checks, transaction handling, or timing; advanced features are rarely needed.
Dynamic features (e.g., runtime parameter replacement, context propagation) would require reflection, contradicting the goal of compile‑time plain Java code.
The resulting API requires a public interceptor class with a single @AroundInvoke method, a no‑arg accessible constructor, and a single binding annotation per interceptor.
Constraints and usage boundaries
The intercepted class cannot be final because Feat needs to generate a subclass.
The intercepted method must be a public instance method; static, final, or private methods are not woven.
The interceptor class must have an accessible no‑argument constructor.
Each interceptor class may contain only one @AroundInvoke method.
After changing interceptor annotations, re‑run mvn compile to regenerate code.
If an interceptor does not call proceed() and returns directly, the return type must be compatible with the target method, otherwise type‑conversion errors occur.
When code heavily relies on final classes or methods, a dynamic‑proxy solution may be more appropriate.
Project reference
Repository: https://gitee.com/smartboot/feat
Documentation: https://smartboot.tech/feat/
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
Three Knives
Every line of code you contribute to open source could help make the future better.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
