Java反射:通过接口名调用方法

3
我有一个接口的名称,并且我想调用由其具体实现类定义的方法。因此,我利用了Java反射。
接口:
package tsb.learning.reflection;

public interface IAnyThing {

    void doSomething();
}

它的实现类:
package tsb.learning.reflection;

public class AnyThing implements IAnyThing {

    public void doSomething() {
        System.out.println("JYM");
    }
}

InvocationHandler的实现:

package tsb.learning.reflection;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;

public class AnyInvocationHandler implements InvocationHandler {

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        return method.invoke(proxy, args);
    }
}

控制器:
package tsb.learning.reflection;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy;

public class Controller {

    /**
     * @param args
     * @throws ClassNotFoundException
     */
    public static void main(String[] args) throws ClassNotFoundException {
        String interfaceName = "tsb.learning.reflection.IAnyThing";
        ClassLoader classLoader = Class.forName(interfaceName).getClassLoader();
        Class<?>[] interfaces = new Class<?>[] { Class.forName(interfaceName) };
        InvocationHandler handler = new AnyInvocationHandler();
        IAnyThing anyThing = (IAnyThing) Proxy.newProxyInstance(classLoader, interfaces, handler);
        anyThing.doSomething();
    }
}

但它没有起作用,我遇到了以下异常:
Caused by: java.lang.reflect.InvocationTargetException
    at sun.reflect.GeneratedMethodAccessor1.invoke(Unknown Source)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
    at java.lang.reflect.Method.invoke(Unknown Source)
    at tsb.learning.reflection.AnyInvocationHandler.invoke(AnyInvocationHandler.java:10)
    at $Proxy0.doSomething(Unknown Source)
    at sun.reflect.GeneratedMethodAccessor1.invoke(Unknown Source)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
    at java.lang.reflect.Method.invoke(Unknown Source)
    at tsb.learning.reflection.AnyInvocationHandler.invoke(AnyInvocationHandler.java:10)
    at $Proxy0.doSomething(Unknown Source)
    at sun.reflect.GeneratedMethodAccessor1.invoke(Unknown Source)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
    at java.lang.reflect.Method.invoke(Unknown Source)
    at tsb.learning.reflection.AnyInvocationHandler.invoke(AnyInvocationHandler.java:10)
    at $Proxy0.doSomething(Unknown Source)
    at sun.reflect.GeneratedMethodAccessor1.invoke(Unknown Source)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
    at java.lang.reflect.Method.invoke(Unknown Source)
    at tsb.learning.reflection.AnyInvocationHandler.invoke(AnyInvocationHandler.java:10)

例外情况是在循环中在控制台打印,我需要停止程序。
任何信息对我都非常有帮助。
3个回答

4

Caused by: java.lang.reflect.InvocationTargetException

这意味着你调用的方法抛出了异常。你需要查看出现在方法后面的异常并处理它,它与你如何调用该方法无关。

我怀疑你正在遇到StackOverflowError。

// calls the same method on the same proxy which will recurse until you get an error.
return method.invoke(proxy, args);

相反,尝试调用真实对象上的方法来执行某些操作。

public class AnyInvocationHandler implements InvocationHandler {
    final IAnyThing iat;

    public AnyInvocationHandler(IAnyThing iat) {
        this.iat = iat;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        // call the same method on a real object.
        return method.invoke(iat, args);
    }
}

顺便提一下,您可以编写

Class interfaceClass = tsb.learning.reflection.IAnyThing.class;
ClassLoader classLoader = interfaceClass.getClassLoader();
Class<?>[] interfaces = new Class<?>[] { interfaceClass };

谢谢。那么 AnyInvocationHandler#invoke 方法的实现没问题吗? - Tapas Bose
@TapasBose 我重新编写的方式是对的,是的。 ;) - Peter Lawrey
谢谢你提供的代码片段,它已经可以工作了。但是如果我不知道实现类(这里是AnyThing),那么我该怎么办呢?实际上这只是一个演示应用程序,在真正的应用程序中,我不知道哪个类实现了它。 - Tapas Bose
你不需要知道谁来实现接口,但是你必须要有一个真实的实例。否则,你不能指望它做任何事情。 - Peter Lawrey
我已经修改了它,这样你就可以看到它如何调用接口的实现,而不需要知道它将是什么。 - Peter Lawrey

1

在你的AnyInvocationHandler中,你可以将调用委托给你的AnyThing实例:

public class AnyInvocationHandler implements InvocationHandler {

    private AnyThing delegate = new AnyThing();

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {

        // to something useful here
        [...]
        // finally, invoke the method on implementation class.
        return method.invoke(delegate, args);
    }
}

1
< p > 在 < code > IAnyThing 中唯一的方法是 < code > doSomething() ,所以我猜在 < code > InvocationHandler 中你知道这个方法是什么。只需将您的实现放在那里即可。此外,除了 < code > doSomething() 之外,您还应处理从 < code > java.lang.Object 继承的三种方法: < / p>
public static class AnyInvocationHandler implements InvocationHandler {

    private static final Method doSomething;

    static {
        try {
            doSomething = IAnyThing.class.getMethod("doSomething");
        } catch (NoSuchMethodException e) {
            throw new ExceptionInInitializerError(e);
        }
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        if (method.getDeclaringClass() == Object.class)
            return handleObjectMethod(proxy, method, args);

        if (doSomething.equals(method)) {
            doSomethingImpl();
            return null;
        }

        throw new AbstractMethodError(method.toString());
    }

    private Object handleObjectMethod(Object proxy, Method method, Object[] args) {
        switch (method.getName()) {
            case "equals":
                return proxy == args[0];
            case "hashCode":
                return System.identityHashCode(proxy);
            case "toString":
                return proxy.getClass().getName() + "@" + Integer.toHexString(System.identityHashCode(proxy));
            default:
                throw new AssertionError();
        }
    }

    private void doSomethingImpl() {
        // implement....
    }

}

谢谢,你的例子很有帮助,但这不是真实情况。在真实情况中,我只有接口名称,并且我从RMI获取它,我需要调用该接口的方法。 - Tapas Bose
1
你是在告诉我,你想实现一个接口却不知道它是什么,甚至没有具体的实现类?如果你从RMI收到java.util.Collection对象,那你会怎么做呢?你会模拟ArrayList、HashSet还是LinkedBlockingQueue等等?而现在已经有成千上万的这些接口存在。 - Saintali

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接