使用Java反射进行事件处理



好吧,这很难解释。我会尽力的。

受Bukkit事件系统的启发,您只需使用@EventHandler就可以使void成为事件处理程序。

示例:

@EventHandler
public void aRandomName(PlayerMoveEvent ev) {
}

正如您所看到的,方法的名称并不重要。传递的事件由事件参数类型决定。

所有事件都扩展Event类。我编了一些代码,除了一件事,我认为这些代码会起作用。

public List<Object> eventContainers;
public void fireEvent(Event e) {
    Method[] methods;
    for (Object o : eventContainers) {
        Object[] classes = o.getClass().getClasses();
        for (Object clss : classes) {
            methods = clss.getClass().getMethods();
            for (Method m : methods) {
                if (m.getAnnotation(EventHandler.class) != null) {
                    try {
                        Class[] requiredTypes = m.getParameterTypes();
                        for(Class cl : requiredTypes) {
                            if(e.equals(cl)) {
                                m.invoke(clss, e);
                            }
                        }
                    } catch (IllegalAccessException ex) {
                    } catch (IllegalArgumentException ex) {
                    } catch (InvocationTargetException ex) {
                    }
                }
            }
        }
    }
}

我的代码的作用:循环遍历eventContainers中的所有类,查找具有@EventHandler注释的方法,并将指定的事件发送到该方法。然而,我想看看fireEvent(事件e)中给定的事件是什么类型的事件,然后看看需要这种事件参数的方法。我该怎么做?我想

Class[] requiredTypes = m.getParameterTypes();
for(Class cl : requiredTypes) {
  if(e.equals(cl)) {
    m.invoke(clss, e);
  }
}

不会起作用。

最终,我希望能够将事件传递给插件。像这样:

EventManager.fireEvent(new PlayerMoveEvent(player));

将发送到所有插件和具有的插件

@EventHandler
public void aVoid(PlayerMoveEvent e) {
//stuff
}

如果你有任何问题,我会尽力解释得更好。提前感谢您的帮助!

您的代码使用e.equals(cl),它将Event的实例与Class的实例(Event实例的类)进行比较,这永远不会返回true。你想做的是:

if(e.getClass().equals(cl)) {
    m.invoke(clss, e);
}

或者,如果您希望用@EventHandler注释的方法处理其方法签名定义的类的所有子类(即,像handle(Event e)这样的方法将用PlayerMoveEvents以及所有其他事件调用),那么您需要:

if(cl.isAssignableFrom(e.getClass())) {
    m.invoke(clss, e);
}

有关更多信息,请参阅此处的Class Javadoc。

请注意,我认为您的代码中还有一些其他问题。例如,Method.invoke应该使用包含用@EventHandler注释的方法的类的实例来调用。从你的代码中有点不清楚,但我相信这应该是:

m.invoke(o, e);

此外,通过调用o.getClass().getClasses(),您正在对o类中定义的类进行迭代-您可能希望直接对o类的方法进行迭代,即:

for (Method m : o.getClass().getMethods()) {
    if (m.getAnnotation(EventHandler.class) != null) {
        Class[] requiredTypes = m.getParameterTypes();
        if (requiredTypes.length == 1 && requiredTypes[0].isAssignableFrom(e.getClass()) {
            m.invoke(o, e);
        }
    }
}

您可以使用method.getGenericParameterTypes()Method获取参数类型,因此:

m.invoke(clss, m.getGenericParameterTypes()[0].class.cast(e));

不确定这是否是你想要的。

假设EventHandler注释方法只有一个参数

Method[] methods = YourClass.class.getDeclaredMethods();
Object yourInstance = null; // get it
Event e = null; // get it
for (Method method : methods) {
    EventHandler handler = method.getAnnotation(EventHandler.class);
    if (handler != null) {
        Class<?>[] parameterTypes = method.getParameterTypes();
        // you're going to need different logic if you have more than one parameter
        if (parameterTypes.length == 1 && parameterTypes[0].isAssignableFrom(e.getClass())) {
            method.invoke(yourInstance, e);
        }
    }
}

我没有包括任何异常处理。

获取事件处理程序候选类的所有方法并对它们进行迭代。如果方法具有@EventHandler注释,则获取其参数类型列表。如果它只有一个参数,并且该类型可以从您的事件类型e.getClass()中赋值,那么在您的事件中调用它。

我现在已经将代码修改为工作事件系统!!!!:D非常感谢安德舒勒!

public void fireEvent(Event e) {
    Method[] methods;
    for (Object o : eventContainers) {
        methods = o.getClass().getMethods();
        for (Method m : methods) {
            if (m.getAnnotation(EventHandler.class) != null) {
                try {
                    if (m.getParameterTypes()[0].isAssignableFrom(e.getClass())) {
                        m.invoke(o, e);
                    }
                } catch (IllegalAccessException ex) {
                } catch (IllegalArgumentException ex) {
                } catch (InvocationTargetException ex) {
                }
            }
        }
    }
}

我把所有的答案都记在心里了,谢谢大家!

相关内容

  • 没有找到相关文章

最新更新