有 Java 编程相关的问题?

你可以在下面搜索框中键入要查询的问题!

使用Java反射进行事件处理

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

受Bukkit事件系统的启发,您可以通过使用@EventHandler使事件处理程序无效

例如:

@EventHandler
public void aRandomName(PlayerMoveEvent ev) {

}

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

所有事件都扩展事件类。 我已经编写了一些代码,我认为这些代码可以工作,除了一件事

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(event 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
}

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


共 (4) 个答案

  1. # 1 楼答案

    假设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()分配,则在传递事件时调用它

  2. # 2 楼答案

    我现在已将代码修改为工作事件系统D非常感谢Anderschuller

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

    我记住了所有的答案,谢谢大家

  3. # 3 楼答案

    您的代码使用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);
    }
    

    有关更多信息,请参见^{} Javadoc here

    请注意,我认为代码中还有一些其他问题。例如,应使用类的实例调用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);
            }
        }
    }
    
  4. # 4 楼答案

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

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

    不确定这是否是你想要的