我如何拦截从Java到Groovy的调用 - 或者轻松地模拟这个 [英] How can I intercept a call from Java to Groovy--or simulate this easily

查看:128
本文介绍了我如何拦截从Java到Groovy的调用 - 或者轻松地模拟这个的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我希望使用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脚本引擎(我在这里放弃了一些东西) p>

  gse =新GroovyScriptEngine(cpString,gcl); 

然后我实例化脚本

  scriptClass = gse.loadScriptByName(file.getAbsolutePath()); 
instance =(GroovyObject)scriptClass.newInstance();

然后,如果它是一个Listener,它是cannedjava库使用的标记接口为了识别它应该扫描注释的Java类,我将它传递给该类,以便可以注册任何注释的方法(沿着实例的某个地方成为脚本,尽管同一对象:

  if(script instanceof Listener)
pm.registerEvents((Listener)script,this);

脚本本身的有趣部分如下所示:

  @EventHandler 
public void userEvent(UserInteractEvent event){

我想要什么添加是在userEvent内部添加一个注释的局部变量,如下所示:

  @Persist int persistedPerUserData // Or @PersistPerUser?或@Persist(User = true)?

可以拦截它,我会抓住它e UserInteractionEvent中的用户名,将其与脚本,变量和方法名称结合以获得像MyScript:UserEvent:Bill:persistedPerUserData这样的唯一签名,并使用它来检索可放入persistedPerUserData中的int。



稍后在方法返回后从persistedPerUserData中获取值并将其存储回MyScript:UserEvent:Bill:persistedPerUserData(目前为散列,但我希望最终将其作为数据库)。



通过这种方式,脚本永远不必考虑它处理不同用户的事实,它只需要有一组变量并且所有的持久性都能正常工作。



还有其他事件可以用,但我相信它们都会扩展相同的事件,并且根事件具有用户字段。



编辑:就像另一件事不尝试,我试图使用这样的ProxyMetaClass /拦截器:

  //尝试(并失败)拦截呼叫t o 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();
}
返回实例;




$ b $ p
$ b

结果相同,每个来自Groovy类的调用工具很好,但没有拦截来自Java的调用。回到绘图板。我猜这就是为什么Aspects使用字节码操作。

解决方案

我真的没有想出一个答案,但我来与我认为会起作用的东西 - 我想没有人提到它,因为它非常明显,但我仍然是Java中的思考不仅仅是groovy。



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

  @EventHandler 
public void userEvent(UserInteractEvent event){
@Persist int persisteData
//此时persistedData包含的数据不同,具体取决于在$ b $中传入的用户b ...

我认为如果我使用闭包,我想我可以做一些关闭操作:

  @EventHandler 
public void userEvent(UserInteractEvent event){
persistScope(event.user){
@Persist int persistedPerUserData //或@PersistPerUser?或@Persist(User = true)?
...

在persistScope中我可以扫描@Persist注释的闭包并做我的事情。这可能无法正确工作,因为直到闭包开始时int才被创建,但我认为只要从groovy调用groovy,我就可以使用我在问题中提到的方法来解决这个问题。或者我只是用持久化的用户数据制作一个散列。



它稍微笨拙一些,但我认为它会起作用,而且我喜欢这个事实(事实上​​,在我刚刚假设传入的事件有一个.getUser()方法之前,现在我可以将持久性范围限制为我想要的任何东西)。



我会试着实现这一点,并给它几天的时间来看看是否有人在接受这个问题之前提出了我问的原始问题的答案。编辑:我不满意这个解决方案。由于变量是在该范围内声明的,因此我不能使用@Persist注释,所以我传入了模块可以用作数据容器的散列值,然后在封闭返回后将其保留。



仍在寻找更好的答案......


I was hoping to use groovy's invokeMethod to do this, but it turns out that when you call from Java to Groovy, invokeMethod isn't called, but otherwise it would have worked perefectly.

I have a case where I'm submitting a Groovy class to a Java class (Which I can't edit). The Groovy class is annotated and the Java class scans for the annotations and saves the annotated methods as listeners for it's events.

When the event is issued I'd like to grab some information from the event object, use it to retrieve data and inject that data into the event handler in the script (Via annotated variables inside that method).

The things I have control over--I instantiate the scripts, set a base class for them, and pass them to the other system to be registered. The scripts will be written by others--I have control over the script's design but my goal is simplicity.

I could probably create an adapter class, but that seems quite difficult and fragile since I'd have to manually register all those methods instead of using the annotations like it does now--there are a lot of different events to listen to.

I'm wondering if there are groovy tricks I'm not considering. I'm still pretty new to groovy meta-programming. Perhaps there is a way to create the adapter class automatically, or when I compile the scripts, replace the methods with forwarding methods that forward to my code before calling their real method--anything like that possible?

Requested source code:

Source code--well let's see, this process is spread across a few classes...

This is how I set up the Groovy Class Loader with a ScriptBase

cconfig.setScriptBaseClass("tv.kress.bill.minecraft.ezplugin.ScriptBase");
GroovyClassLoader gcl = new GroovyClassLoader(getClass().getClassLoader(), cconfig);

Then I pass it to the Groovy Scripting Engine (I'm leaving out some stuff here)

gse = new GroovyScriptEngine(cpString, gcl);

Then I instantiate the script

scriptClass = gse.loadScriptByName(file.getAbsolutePath());
instance = (GroovyObject) scriptClass.newInstance();

Then, if it's a "Listener" which is the marker interface that the "canned" java library uses to identify java classes it should scan for annotations, I pass it off to that class so that any annotated methods can be registered (Somewhere along the line "instance" became "script", same object though:

 if (script instanceof Listener)
     pm.registerEvents((Listener) script, this);

The interesting part of the script itself looks like this:

@EventHandler
public void userEvent(UserInteractEvent event) {

What I'd like to add is the ability to, inside the userEvent, add an annotated local variable like this:

    @Persist int persistedPerUserData // Or @PersistPerUser? or @Persist(User=true)?

so that just before userEvent is called, I can intercept it. I'd grab the user name from the UserInteractionEvent, combine it with the script, variable and method name to get a unique signature like "MyScript:UserEvent:Bill:persistedPerUserData" and use that to retrieve an int I can place into persistedPerUserData.

Later after the method returns grab the value from persistedPerUserData and store it back into "MyScript:UserEvent:Bill:persistedPerUserData" (Currently a hash but I expect to make it a database eventually).

In this way, the script never has to consider the fact that it's dealing with different users, it just has to have a single set of variables and all the persistence just works.

There are other events this will work for, but I believe they all extend the same event and that root event has the "user" field.

EDIT: Just as another thing NOT to try, I tried to use the ProxyMetaClass/interceptor like this:

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

With the same results, every call from a groovy class was instrumented fine, but no calls from Java were intercepted. Back to the drawing board. I guess this is why Aspects use bytecode manipulation.

解决方案

I really haven't figured out an answer to this, but I came up with something that I think will work--I suppose nobody mentioned it because it was so obvious, but I'm still "Thinking in Java" More than groovy.

Okay, where I was hoping for the script implementation to look something like this:

@EventHandler
public void userEvent(UserInteractEvent event) {
    @Persist int persisteData
    // At this point persistedData contains data different depending on which user was passed in
...

I think that if I use a closure I think I can do something close:

@EventHandler
public void userEvent(UserInteractEvent event) {
    persistScope(event.user) {
        @Persist int persistedPerUserData // Or @PersistPerUser? or @Persist(User=true)?
        ...

and that way within persistScope I can scan the closure for @Persist annotations and do my thing. This may not work exactly because that int hasn't been created until the closure starts, but I think I can fix that using the methods I mentioned in the question as long as I'm calling from groovy to groovy. Either that or I'll just make "it" a hash with the persisted user data.

It's slightly more awkward but I think it will work, and I like the fact that it's a little more explicit (In fact before I was just assuming that the "event" passed in had a .getUser() method, now I can scope persistence to anything I want).

I'll go try to implement this and give it a few days to see if anyone comes up with an answer to the original question I asked before accepting this.

EDIT: I'm unhappy with this solution. Since the variables are declared inside that scope I couldn't use the @Persist annotation, so i passed in a hash that the module can use as a data container, then I persist it after the closure returns.

Still looking for better answers...

这篇关于我如何拦截从Java到Groovy的调用 - 或者轻松地模拟这个的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆