英文:
AspectJ: intercept constructor when method reference is used
问题
For normal constructor calls with new
I use:
pointcut replaceableNew() : call((@Replaceable *).new(..));
Object around() : replaceableNew() {
Constructor<?> constructor = ((ConstructorSignature) thisJoinPointStaticPart.getSignature()).getConstructor ;
Class<?> declaringClass = constructor.getDeclaringClass();
if (!Registry.getInstance().isReplaced(declaringClass)) {
return proceed() ;
}
Class<?>[] parameterTypes = constructor.getParameterTypes();
Object[] args = thisJoinPoint.getArgs();
return Registry.getInstance().create(declaringClass, parameterTypes, args); // creates some sub-class of declaring class
}
但是当构造函数被传递为方法引用时,此方法无效。
对于实例方法引用(带有"*lambda*"),解决方法不起作用。
execution((@Replaceable *).new(..))
切入点不允许替换返回的对象。
也许一些 call(* *(..))
和 execution(...
的组合可以工作,但这样做会增加太多的调用,还会创建和丢弃不必要的可替代实例。看起来不美观。
有其他的想法吗?
英文:
For normal contructor calls with new
I use
pointcut replaceableNew() : call((@Replaceable *).new(..));
Object around() : replaceableNew() {
Constructor<?> constructor = ((ConstructorSignature) thisJoinPointStaticPart.getSignature()).getConstructor ;
Class<?> declaringClass = constructor.getDeclaringClass();
if (!Registry.getInstance().isReplaced(declaringClass)) {
return proceed() ;
}
Class<?>[] parameterTypes = constructor.getParameterTypes();
Object[] args = thisJoinPoint.getArgs();
return Registry.getInstance().create(declaringClass, parameterTypes, args); // creates some sub-class of declaring class
}
but this does not work when contructor is passed as method references.
Stream.of(1,2).map(SomeReplaceable::new) ...
Workaround for instance method references (with "*lambda*" ) does not work.
execution((@Replaceable *).new(..))
pointcut does not allow to replace the returned object.
Maybe some combination of call(* *(..))
and execution(...
above could work, but this way we will advice too much calls and also will create and discard unneccessary replaceable instances. Looks ugly.
Any other ideas?
答案1
得分: 1
> 也许上面某种结合使用 call(* *(..))
和 execution(..)
的方式可能会起作用。
不,不会的,因为在使用方法引用时,AspectJ 中根本没有可以拦截的 call()
连接点。对于反射构造函数调用也是同样的情况。
因此,你的想法只会在简单情况下起作用,即只有在可以拦截普通构造函数调用时才有效。如你已经注意到的,你不能直接替换由构造函数生成的实例,甚至不能使用低级别的字节码工程框架(如ASM或BCEL)或高级框架(如Javassist或ByteBuddy)来实现 - 这只是因为JVM不允许这样做。你可以用这些框架做的最好的事情是转换构造函数代码,使其跳过对象初始化,并使用虚拟参数直接调用超类构造函数(顺便提一下,所有超类构造函数也必须经过处理以跳过初始化)。
此外,即使可以替换 execution(*.new(..))
返回的对象,你的切面也必须排除 this()
和 super()
调用,确保在实例化注解类的子类时能够正确处理等等。
整个想法根本行不通。很抱歉粉碎了你的梦想(我多年前也曾有过这个想法),但这就是事实。
回到 XY 问题:我再次要求你描述清楚 你想要实现什么,而不是描述你认为应该如何在技术上实现。你的用例是什么?你试图解决什么问题?
英文:
> Maybe some combination of call(* *(..))
and execution(..)
above could work
No, it will not, because there simply is no call()
joinpoint to intercept for AspectJ when using method references. The same is true for reflective constructor calls.
Thus, your idea will only work for simple cases, i.e. when there is a normal constructor call you can intercept. As you already noticed, you cannot directly replace the instance generated by a constructor, not even with low level byte code engineering frameworks such as ASM or BCEL or higher level ones like Javassist or ByteBuddy - simply because the JVM does not allow it. The best you can do with such frameworks is to transform the constructor code such that it skips object initialisation and directly calls the super constructor with dummy arguments (all super constructors also have to be instrumented in order to skip initialisation, BTW).
Besides, your aspect, even if it was possible to replace objects returned by execution(*.new(..))
, would have to take care of excluding this()
and super()
calls, make sure to do the right thing if a subclass of the annotated class is being instantiated etc.
The whole idea just does not work. Sorry to destroy your dream (I also used to have this idea many years ago), but these are the facts.
Coming back to the XY problem: I am asking you again to rather describe what you want to achieve, not to describe how you think this ought to be done technically. What is your use case? Which problem are you trying to solve?
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论