截取groovy

时间:2017-12-27 11:03:23

标签: java groovy

我试图拦截在Java环境中运行的Groovy脚本中的所有方法调用。

特别我想检查所有方法调用的返回类型,如果是X我想用Y替换它。

我知道你可以在MetaClass上使用invokeMethod进行拦截,但我只能对我编译的脚本类进行拦截。如果脚本依次调用类A上的方法,那么它将创建一个我无法拦截的新MetaClass[A],而不必事先从注册表中手动获取该MetaClass并使用meta覆盖它方法

我曾尝试使用GroovySystem.getMetaClassRegistry()为MetaClasses创建时添加侦听器,以便在那里添加元方法,但它似乎永远不会触发。

我希望这是自动的,而不是事先必须在我应该转换的方法中添加注释,或者知道哪些类'我想要转换的方法。返回X的所有方法都应返回Y

我可以全局拦截所有方法调用吗?

我可以拦截MetaClass创作吗?

我可以添加自动AST转换吗?

1 个答案:

答案 0 :(得分:0)

事实证明这是可能的。您需要通过调用GroovySystem.getMetaClassRegistry().setMetaClassCreationHandle替换MetaClassCreationHandle。

由于基类MetaClassCreationHandle是包私有的,因此从ExpandoMetaClassCreationHandle扩展可能更容易。但是考虑到你最常见的需要使你的所有课程都基于ExpandoMetaClass可能会有点过分。所以我做的是这样的:

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;
    }
});

这意味着我们只会为那些需要添加元方法的人创建Expando类。