简体   繁体   English

拦截groovy中所有元类的所有方法调用

[英]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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM