2017-12-27 163 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類。