[英]Intercept all method calls for all metaclasses in groovy
I am trying to intercept all method calls inside Groovy scripts running in a Java environment. 我试图拦截Java环境中运行的Groovy脚本内的所有方法调用。
Specially I want to check the return type of all method calls, and if it is X
I want to replace it with Y
. 特别是我想检查所有方法调用的返回类型,如果它是
X
我想用Y
代替它。
I know you can intercept using invokeMethod
on the MetaClass, but I can only do that to the script class that I compile. 我知道您可以在MetaClass上使用
invokeMethod
进行拦截,但是我只能对我编译的脚本类执行此操作。 If the script in turn calls a method on class A
then it will create a new MetaClass[A]
that I can't intercept without earlier having manually fetched that MetaClass from the registry and overriding it with a meta method. 如果脚本又在类
A
上调用了一个方法,则它将创建一个新的MetaClass[A]
,除非早先从注册表中手动获取该MetaClass并用meta方法覆盖它,否则我将无法拦截该MetaClass[A]
。
I have tried to use GroovySystem.getMetaClassRegistry()
to add listeners for when MetaClasses are created, to add meta methods there, but it seemingly never fires. 我试图使用
GroovySystem.getMetaClassRegistry()
在创建MetaClasses时添加侦听器,在此处添加元方法,但似乎永远不会触发。
I would like this to be automatic, and not in advance having to add an annotation to the methods that I should transform, or knowing which classes' methods I want to transform. 我希望这是自动的,而不必事先为应该转换的方法添加注释,也不必知道我要转换的类的方法。 All methods that return
X
should return Y
. 所有返回
X
方法都应返回Y
Can I globally intercept all method calls? 我可以全局拦截所有方法调用吗?
Can I intercept MetaClass creation instead? 我可以拦截MetaClass的创建吗?
Can I add automatic AST transformations? 我可以添加自动AST转换吗?
Turns out this is possible. 事实证明这是可能的。 You need to replace the MetaClassCreationHandle by calling
GroovySystem.getMetaClassRegistry().setMetaClassCreationHandle
. 您需要通过调用
GroovySystem.getMetaClassRegistry().setMetaClassCreationHandle
来替换MetaClassCreationHandle。
Since the base class MetaClassCreationHandle
is package private, it might be easier to extend from ExpandoMetaClassCreationHandle
instead. 由于基类
MetaClassCreationHandle
是包私有的,因此从ExpandoMetaClassCreationHandle
扩展可能会更容易。 But take into consideration that it might be overkill for your most common need to make all your classes be based on the ExpandoMetaClass
. 但是要考虑到,使您的所有类都基于
ExpandoMetaClass
最常见的需求可能会过大。 So what I did was something like this: 所以我所做的是这样的:
GroovySystem.getMetaClassRegistry().setMetaClassCreationHandle(new ExpandoMetaClassCreationHandle() {
@Override
protected MetaClass createNormalMetaClass(Class theClass, MetaClassRegistry registry) {
final List<Method> propertyMethods = new ArrayList<>();
for (Method method : theClass.getDeclaredMethods()) {
if (method.getReturnType() == TheMethodReturnTypeYouCareAbout.class) {
propertyMethods.add(method);
}
}
final MetaClass mc;
if (propertyMethods.isEmpty() == false) {
final ExpandoMetaClass expando = new ExpandoMetaClass(theClass, true, true);
for (Method mm : propertyMethods) {
final ClassInfo ci = ClassInfo.getClassInfo(mm.getDeclaringClass());
expando.addMetaMethod(new MetaMethod() {
@Override
public int getModifiers() {
return mm.getModifiers();
}
@Override
public String getName() {
return mm.getName();
}
@Override
public Class getReturnType() {
return mm.getReturnType();
}
@Override
public CachedClass getDeclaringClass() {
return ci.getCachedClass();
}
@Override
protected Class[] getPT() {
return mm.getParameterTypes();
}
@Override
public Object invoke(Object object, Object[] arguments) {
try {
final Object value = mm.invoke(object, arguments);
// Do whatever you need with the value.
return value;
} catch (Exception ex) {
throw new RuntimeException(ex);
}
}
});
}
mc = expando;
} else if (GeneratedClosure.class.isAssignableFrom(theClass)) {
mc = new ClosureMetaClass(registry, theClass);
} else {
mc = new MetaClassImpl(registry, theClass);
}
return mc;
}
});
This means that we will only create Expando classes for those that we have a need of adding meta methods to. 这意味着我们只会为需要添加元方法的类创建Expando类。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.