如何用AspectJ拦截构造器调用并替换对象?拦截后对象仍创建的疑问
问题原因分析
你遇到的问题核心在于AspectJ对构造器的execution和call切入点处理逻辑不同,以及构造器本身的JVM执行机制:
- 构造器的JVM执行逻辑:当执行
new ExampleClass()时,JVM会先完成对象的内存分配(创建出一个未初始化的实例),然后才会调用构造器代码进行初始化。也就是说,在构造器代码(包括切面逻辑)执行之前,对象已经存在了。 execution切入点的行为:execution(ExampleClass.new())匹配的是构造器代码的执行过程。对于构造器的@Around切面,无论你返回什么值,AspectJ都会忽略这个返回值,最终返回JVM已经创建好的那个实例——因为构造器的语义就是返回新创建的对象,AspectJ会遵循这个语义。
所以你在切面里返回null并没有作用,JVM已经提前创建了对象,最终还是会把这个对象返回给调用者。
正确的解决方案
如果你想替换构造器创建的对象,需要使用call切入点来拦截构造器的调用,而不是execution。call切入点匹配的是构造器的调用动作,此时切面的返回值会被用作调用的结果,从而实现对象替换。
修改后的切面代码如下:
import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.annotation.Around; import org.aspectj.lang.annotation.Aspect; @Aspect public class AnnotationAspect { @Around("call(ExampleClass.new())") public Object aroundObjectCreation(ProceedingJoinPoint joinPoint) throws Throwable { System.out.println("Intercepting object creation call"); // 这里返回你想要替换的对象,比如自定义实例或者null return null; // 如果要创建自定义实例,可以这样: // return new AnotherClass(); } }
此时运行Test类,输出会是:
Intercepting object creation call null
额外说明
- 如果你只是想阻止原对象的初始化逻辑(不执行原构造器的代码),在使用
call切入点时,不要调用joinPoint.proceed(),这样原构造器就不会被执行,原对象也不会被初始化。 - 注意
call和execution的区别:call是拦截调用构造器的地方,execution是拦截构造器本身的执行过程。对于构造器的对象替换需求,call是正确的选择。
内容的提问来源于stack exchange,提问作者Ivan Jevtic
相关产品推荐
相关产品推荐

