2

我有 1 个调用A的接口需要由我在程序启动后动态加载的类来实现。让我们称之为B

该接口提供了 x(多于 1)个方法。a()让我们从to调用 em z()。现在我必须将这个类包装一段时间来测量和控制问题,并在它自己的线程中运行它,以便能够在它花费太长时间时杀死它。

因此,我发明C了包装类,B因为B它本身不能实现可运行。

下一部分是原始程序应该调用的类。新班级DD实现接口A以及隐藏模型的整个控制部分。

现在我必须将接口的方法包装在 D 中,并将它们发送给 C,C 将它们解包并在对象 B 上执行它们。

这里有一些我想象的示例代码:

public class D implements A {

private C ai;

public D(String aiName) {
    ai = new C("trivialKi");
}

private void call(parameters, ORIGIN_METHOD origin) {
    AiTaskExecutor task = new AiTaskExecutor(parameters, origin, ai);
    FutureTask<Long> tsk = new FutureTask<Long>(task);

    Thread thread = new Thread(tsk);
    thread.start();
    if (abort) {
            tsk.cancel(true);
    }
}

@Override
public void a(g g, f f, t t) {
    call(g, f, t, ORIGIN_METHOD.a);
}

@Override
public void b(g g, t t, h h) {
    call(g, t, h, ORIGIN_METHOD.b);
}

@Override
public void c(g g, t t, f f) {
    call(g, t, f, ORIGIN_METHOD.c);
}
}

在 C 类中,带有该枚举的明显 switch 案例将参数传递给 B 类上的正确方法,该方法保存在 C 类的私有字段中。

您有更好的解决方案吗?我个人不喜欢 enum 的东西,如果参数差异太大,这不会很好地工作。

这样的事情有“标准”解决方案吗?

4

1 回答 1

9

对此的标准解决方案是:java.lang.reflect.Proxy为 A 使用“动态代理”( )。这可以节省您几乎所有的样板代码。

该站点和 Google 包含足够的Proxy.

另外:我建议不要为每个调用使用一个新线程——如果调用的方法很短,这将非常昂贵。您可以改用Callable接口Runnable和线程池Executor。这也允许您在界面中具有返回值:-)

编辑

只是为了好玩,我编写了动态代理和执行器的东西。

给定以下接口A和示例实现B

interface A {
    int a(int g, int f, int h);
    int b(int x);
}

class B implements A {
    @Override
    public int a(int g, int f, int t) {
        System.out.println("called a in thread "+Thread.currentThread().getName());
        return 42;
    }
    @Override
    public int  b(int x) {
        System.out.println("called b in thread "+Thread.currentThread().getName());
        return 21;
    }
}

正确Callable使用反射调用 anyjava.lang.reflect.Method如下所示:

class ReflectiveMethodCallable implements Callable<Object> {
    private Object target;
    private Method method;
    private Object[] args;

    public ReflectiveMethodCallable(Object target, Method method, Object[] args) {
        this.target = target;
        this.method = method;
        this.args = args;
    }

    @Override
    public Object call() throws Exception {
        return method.invoke(target, args);
    }
}

创建这样的 aReflectiveMethodCallable并将其赋予 an 的ExecutorService部分是InvocationHandlerof java.lang.reflect.Proxy

class MyInvocationHandler implements InvocationHandler {
    private Object target;
    private ExecutorService executorService;

    public MyInvocationHandler(Object target, ExecutorService executorService) {
        this.target = target;
        this.executorService = executorService;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        try {
            Callable<Object> task = new ReflectiveMethodCallable(target, method, args);
            Future<Object> future = executorService.submit(task);
            return future.get();
        }
        catch(ExecutionException e1){
            try {
                throw e1.getCause();
            } catch(InvocationTargetException e2){
                throw e2.getCause();
            }
        }
    }
}

InvocationHandler在创建新的Proxy时使用createProxyFor。该类的其余部分Main用于 SSCCE 示例:

public class Main {
    public static void main(String[] args) throws InterruptedException {
        ExecutorService executorService = Executors.newCachedThreadPool();

        // get B somehow
        A a = new B();

        // get proxy for B 
        A proxy = createProxyFor(a, executorService);

        // call proxy
        System.out.println("current thread: "+Thread.currentThread().getName());

        int resultA = proxy.a(1,2,3);
        System.out.println("calling a returned "+resultA);

        int resultB = proxy.b(1);
        System.out.println("calling b returned "+resultB);

    }

    static A createProxyFor(A a, ExecutorService executorService){
        InvocationHandler h = new MyInvocationHandler(a, executorService);
        A proxy = (A)Proxy.newProxyInstance(A.class.getClassLoader(), new Class[]{A.class}, h);
        return proxy;
    }
}

输出:

current thread: main
called a in thread pool-1-thread-1
calling a returned 42
called b in thread pool-1-thread-1
calling b returned 21

完成:

  • 每个方法A都将在另一个线程中调用。
  • 线程由线程池重用。
  • 附加逻辑(例如时间测量、超时控制)可以在invoke或 中完成call
  • 无需为每种方法编写任何代码。
于 2012-11-03T09:08:58.603 回答