2017-12-27 253 views
0

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

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

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

我曾尝试使用GroovySystem.getMetaClassRegistry()为创建MetaClasses时添加侦听器,以在其中添加元方法,但它似乎从不触发。

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

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

我可以拦截MetaClass创建吗?

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

回答

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

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