我如何拦截Java对Groovy的调用——或者如何轻松地模拟它

我如何拦截Java对Groovy的调用——或者如何轻松地模拟它,java,groovy,Java,Groovy,我本来希望使用groovy的invokeMethod来实现这一点,但事实证明,当您从Java调用groovy时,invokeMethod没有被调用,但如果不是这样的话,它会工作得很好 我有一个例子,我将Groovy类提交给Java类(我无法编辑)。Groovy类被注释,Java类扫描注释并将注释后的方法保存为其事件的侦听器 发布事件时,我希望从事件对象获取一些信息,使用它检索数据并将数据注入脚本中的事件处理程序(通过该方法中的带注释的变量) 我可以控制的事情——我实例化脚本,为它们设置基类,并将

我本来希望使用groovy的invokeMethod来实现这一点,但事实证明,当您从Java调用groovy时,invokeMethod没有被调用,但如果不是这样的话,它会工作得很好

我有一个例子,我将Groovy类提交给Java类(我无法编辑)。Groovy类被注释,Java类扫描注释并将注释后的方法保存为其事件的侦听器

发布事件时,我希望从事件对象获取一些信息,使用它检索数据并将数据注入脚本中的事件处理程序(通过该方法中的带注释的变量)

我可以控制的事情——我实例化脚本,为它们设置基类,并将它们传递给另一个系统进行注册。脚本将由其他人编写——我可以控制脚本的设计,但我的目标是简单

我可能会创建一个适配器类,但这似乎非常困难和脆弱,因为我必须手动注册所有这些方法,而不是像现在这样使用注释——有许多不同的事件需要侦听

我想知道是否有我不考虑的绝妙技巧。我对groovy元编程还是相当陌生的。也许有一种方法可以自动创建适配器类,或者当我编译脚本时,用转发方法替换这些方法,这些方法在调用它们的实际方法之前转发给我的代码——诸如此类

请求的源代码:

源代码——好吧,让我们看看,这个过程分布在几个类中

这就是我如何使用ScriptBase设置Groovy类装入器的方法

cconfig.setScriptBaseClass("tv.kress.bill.minecraft.ezplugin.ScriptBase");
GroovyClassLoader gcl = new GroovyClassLoader(getClass().getClassLoader(), cconfig);
然后我将其传递给Groovy脚本引擎(这里我省略了一些内容)

然后我实例化脚本

scriptClass = gse.loadScriptByName(file.getAbsolutePath());
instance = (GroovyObject) scriptClass.newInstance();
然后,如果它是一个“监听器”,它是“罐装”java库用来识别它应该扫描注释的java类的标记接口,我将它传递给该类,以便可以注册任何注释的方法(在某个地方,“实例”变成了“脚本”,尽管是同一个对象:

 if (script instanceof Listener)
     pm.registerEvents((Listener) script, this);
脚本本身有趣的部分如下所示:

@EventHandler
public void userEvent(UserInteractEvent event) {
    @Persist int persistedPerUserData // Or @PersistPerUser? or @Persist(User=true)?
// Attempt (and fail) to intercept calls to an instance of clazz
class Slicer {
    public static Object slice(Class clazz) {
        Object instance;
        def proxy = ProxyMetaClass.getInstance(clazz);
        proxy.interceptor = new MyInterceptor();
        proxy.use {
            instance = clazz.newInstance();
        }
        return instance;
    }       
}
@EventHandler
public void userEvent(UserInteractEvent event) {
    @Persist int persisteData
    // At this point persistedData contains data different depending on which user was passed in
...
我想添加的是在userEvent中添加带注释的局部变量的功能,如下所示:

@EventHandler
public void userEvent(UserInteractEvent event) {
    @Persist int persistedPerUserData // Or @PersistPerUser? or @Persist(User=true)?
// Attempt (and fail) to intercept calls to an instance of clazz
class Slicer {
    public static Object slice(Class clazz) {
        Object instance;
        def proxy = ProxyMetaClass.getInstance(clazz);
        proxy.interceptor = new MyInterceptor();
        proxy.use {
            instance = clazz.newInstance();
        }
        return instance;
    }       
}
@EventHandler
public void userEvent(UserInteractEvent event) {
    @Persist int persisteData
    // At this point persistedData contains data different depending on which user was passed in
...
因此,在调用userEvent之前,我可以截取它。我从UserInteractionEvent中获取用户名,将其与脚本、变量和方法名结合,以获得一个唯一的签名,如“MyScript:userEvent:Bill:persistedPerUserData”,并使用该签名检索可以放入persistedPerUserData中的int

稍后,在该方法返回后,从persistedPerUserData获取值并将其存储回“MyScript:UserEvent:Bill:persistedPerUserData”(目前是一个哈希,但我希望最终将其变成数据库)

这样,脚本永远不必考虑它处理不同用户的事实,它只需要有一组变量,所有的持久性都是有效的。 还有其他事件可以使用,但我相信它们都扩展了相同的事件,根事件有“user”字段

编辑:正如另一件不可尝试的事情一样,我尝试使用ProxyMetaClass/拦截器,如下所示:

@EventHandler
public void userEvent(UserInteractEvent event) {
    @Persist int persistedPerUserData // Or @PersistPerUser? or @Persist(User=true)?
// Attempt (and fail) to intercept calls to an instance of clazz
class Slicer {
    public static Object slice(Class clazz) {
        Object instance;
        def proxy = ProxyMetaClass.getInstance(clazz);
        proxy.interceptor = new MyInterceptor();
        proxy.use {
            instance = clazz.newInstance();
        }
        return instance;
    }       
}
@EventHandler
public void userEvent(UserInteractEvent event) {
    @Persist int persisteData
    // At this point persistedData contains data different depending on which user was passed in
...

在相同的结果下,groovy类的每个调用都得到了很好的插装,但Java的调用没有被截获。回到绘图板上。我想这就是Aspect使用字节码操作的原因。

我真的没有找到这个问题的答案,但我想出了一些我认为会奏效的方法——我想没有人提到它,因为这很明显,但我仍然“用Java思考”,而不是groovy

好的,我希望脚本实现看起来像这样:

@EventHandler
public void userEvent(UserInteractEvent event) {
    @Persist int persistedPerUserData // Or @PersistPerUser? or @Persist(User=true)?
// Attempt (and fail) to intercept calls to an instance of clazz
class Slicer {
    public static Object slice(Class clazz) {
        Object instance;
        def proxy = ProxyMetaClass.getInstance(clazz);
        proxy.interceptor = new MyInterceptor();
        proxy.use {
            instance = clazz.newInstance();
        }
        return instance;
    }       
}
@EventHandler
public void userEvent(UserInteractEvent event) {
    @Persist int persisteData
    // At this point persistedData contains data different depending on which user was passed in
...
我认为如果我使用闭包,我认为我可以做一些闭包:

@EventHandler
public void userEvent(UserInteractEvent event) {
    persistScope(event.user) {
        @Persist int persistedPerUserData // Or @PersistPerUser? or @Persist(User=true)?
        ...
这样,在persistScope中,我就可以扫描闭包中的@Persist注释并执行我的操作。这可能不起作用,因为直到闭包开始,int才被创建,但我认为我可以使用我在问题中提到的方法来修复这个问题,只要我从groovy调用groovy。要么这样,要么我就叫“it”包含持久化用户数据的哈希

这有点尴尬,但我认为它会起作用,而且我喜欢这样一个事实,即它更显式一点(事实上,在我只是假设传入的“事件”有一个.getUser()方法之前,现在我可以将持久性的范围扩展到我想要的任何东西)

我将尝试实现这一点,并给它几天时间,看看是否有人提出了一个答案,原来的问题,我问在接受这一点之前

编辑:我对这个解决方案不满意。因为变量是在这个范围内声明的,所以我不能使用@Persist注释,所以我传入了一个散列,模块可以将其用作数据容器,然后在闭包返回后将其持久化


仍然在寻找更好的答案…

我真的还没有找到答案,但我想到了一些我认为有效的方法——我想没有人提到它,因为它太明显了,但我仍然在“用Java思考”,而不是groovy

好的,我希望脚本实现看起来像这样:

@EventHandler
public void userEvent(UserInteractEvent event) {
    @Persist int persistedPerUserData // Or @PersistPerUser? or @Persist(User=true)?
// Attempt (and fail) to intercept calls to an instance of clazz
class Slicer {
    public static Object slice(Class clazz) {
        Object instance;
        def proxy = ProxyMetaClass.getInstance(clazz);
        proxy.interceptor = new MyInterceptor();
        proxy.use {
            instance = clazz.newInstance();
        }
        return instance;
    }       
}
@EventHandler
public void userEvent(UserInteractEvent event) {
    @Persist int persisteData
    // At this point persistedData contains data different depending on which user was passed in
...
我认为如果我使用闭包,我认为我可以做一些闭包:

@EventHandler
public void userEvent(UserInteractEvent event) {
    persistScope(event.user) {
        @Persist int persistedPerUserData // Or @PersistPerUser? or @Persist(User=true)?
        ...
这样,在persistScope中,我就可以扫描闭包中的@Persist注释并执行我的操作。这可能不起作用,因为直到闭包开始,int才被创建,但我认为我可以使用我在问题中提到的方法来修复这个问题,只要我从groovy调用groovy。要么这样,要么我就叫“it”包含持久化用户数据的哈希

这有点尴尬,但我认为它会起作用,而且我喜欢这样一个事实,即它更显式一点(事实上,在我只是假设传入的“事件”有一个.getUser()方法之前,现在我可以将持久性范围扩展到任何