Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/java/313.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
如何拦截方法调用以延迟执行、将所有调用分组并在java中执行?_Java - Fatal编程技术网

如何拦截方法调用以延迟执行、将所有调用分组并在java中执行?

如何拦截方法调用以延迟执行、将所有调用分组并在java中执行?,java,Java,我已经试着解决我的问题大约一天了,但似乎没有任何进展。问题是: 我有一个java类,ExternalClass,里面有30个方法。 我还有一个接口ExternalClassFacade public class ExternalClass { public method1() {...} public method2() {...} ... ... public metod30(...) {...} } 这个类是一个外部库,我不能修改它的代码。 这个类工作得很好,但我遇到了

我已经试着解决我的问题大约一天了,但似乎没有任何进展。问题是:

我有一个java类,ExternalClass,里面有30个方法。 我还有一个接口ExternalClassFacade

public class ExternalClass {
  public method1() {...}
  public method2() {...}
  ...
  ...
  public metod30(...) {...}
}
这个类是一个外部库,我不能修改它的代码。 这个类工作得很好,但我遇到了这样一种情况:我需要在一个未定义的时间跨度上将多个调用组合到所有30个方法,延迟执行,然后在某个时刻一次性执行(串行或并行,我不在乎)

例如,在10分钟内,方法1到30将被随机调用500次,我希望它们在被调用时不做任何事情,但在10分钟后,我希望调用最初调用的所有500次调用

大多数方法都需要参数,在调用这些方法时,我需要记住这些参数

我正在寻找一种方法来扩展/包装/组合这个类,这样当有人调用这些方法中的任何一个时,或者,一个特殊的方法将把调用桥接到原始方法,这样它们就会延迟到合适的时候

我在考虑扩展类并覆盖所有方法,管理30个类似结构的类来保存有关调用的信息,但这需要:

  • 30次覆盖
  • 30张名单
  • 30节课
很多代码,不是很聪明


我正在寻找一种更好的方法来实现这一点,我正在考虑捕获调用并保留指向原始方法调用的指针,但这是java,因此不可能实现。

您可以使用方面来拦截所有调用,以执行外部lib方法并暂停线程,将线程的ID写入同步集。那一集是由另一个线程监视的单件

当您要执行的业务规则被触发时,让singleton watcher迭代集合并通知每个线程继续处理。aspect将继续并执行每个最初请求的外部方法。

使用AspectJ,您可以对类进行修改,然后对接口进行编码。在此之后,您可以自由地在接口后面添加您想要的任何行为。或者,只需使用AspectJ来编织您正在寻找的收集/执行行为

或者也可以让您通过动态子类化(假设它不是最终的)基本上代理类,从而更干净地完成这项工作


有很多选择。这是我想到的三个第三方问题。其中一些方法的一个优点是,它们将以对象形式为方法调用提供一些表示,您可以很容易地在以后收集并运行这些表示。

确实是一个非常有趣的问题。第一个问题:
ExternalClass
是否实现了一些接口?如果有,它会简化很多东西,但是如果没有,你可以创建一个:

interface ExternalClassFacade {
    method1();
    method2();
            //...
    method30();
}
别担心,你不必实施它!只需从
ExternalClass
复制所有方法签名。你知道
java.lang.Proxy
?解决像您这样的问题的绝佳工具:

ExternalClass ext = //obtain target ExternalClass somehow
ExternalClassFacade extFacade = (ExternalClassFacade) Proxy.newProxyInstance(
    ExternalClass.class.getClassLoader(), 
    new Class<?>[]{ExternalClassFacade.class},
    new BatchInvocationHandler(ext));
extFacade.method1();
此代码本身没有做任何有用的事情-当您在
ExternalClassFacade
上调用一个方法时,它将调用转发给
ExternalClass
上具有相同参数的同一命名方法。所以我们还没有取得任何成就。顺便说一句,我使用ApacheCommons简化了一点反射代码。您很可能已经在类路径上有了这个库,如果没有,它只是几行额外的代码

现在看看这个改进的版本:

private static class BatchInvocationHandler implements InvocationHandler {

    private final ExternalClass ext;

    private Queue<Callable<Object>> delayedInvocations = new ConcurrentLinkedQueue<Callable<Object>>();

    public BatchInvocationHandler(ExternalClass ext) {
        this.ext = ext;
    }

    @Override
    public Object invoke(Object proxy, final Method method, final Object[] args) throws Throwable {
        delayedInvocations.add(new Callable<Object>() {
            @Override
            public Object call() throws Exception {
                return MethodUtils.invokeMethod(ext, method.getName(), args);
            }
        });
        return null;
    }

}
私有静态类BatchInvocationHandler实现InvocationHandler{
私有最终外部类ext;
private Queue delayedInvocations=新建ConcurrentLinkedQueue();
公共BatchInvocationHandler(ExternalClass ext){
this.ext=ext;
}
@凌驾
公共对象调用(对象代理、最终方法、最终对象[]args)抛出Throwable{
delayedInvocations.add(newcallable()){
@凌驾
公共对象调用()引发异常{
返回MethodUtils.invokeMethod(ext,method.getName(),args);
}
});
返回null;
}
}
现在我们取得了进展:我们不再调用该方法,而是将调用包装在
Callable
中,并将其添加到
delayedInvocations
队列中。当然,因为我们不再调用实际的方法,所以返回值只是一个占位符。如果
ExternalClass
方法的返回类型不同于
void
,则必须非常小心

我想你现在看到了光明。您需要做的一切就是创建一个线程,该线程将获取队列中收集的所有可调用的,并批量运行它们。你可以用不同的方式来做,但是基本的构建块就在那里。您还可以选择数据结构,如映射或集合,而不是队列。例如,我可以想象出于某种原因按名称对方法进行分组



当然,如果您可以使用AspectJ/SpringAOP,您将避免使用整个代理基础结构代码。但是基本的想法是一样的,只是API会更令人愉快。

+1我想建议类似的东西,我想这就是你的答案。伙计,回答得好。直到最近才发现代理,尽管它在java.lang包中。使用java.util.concurrent类进一步提升了它。如果你能从一篇文章中学到很多东西,那就太好了。谢谢你的回答。今天晚些时候,我会尝试使用你提供的信息,看看我是否能让它发挥作用!我尝试使用您为我发布的代码和指导原则,但不是很成功;)我的问题:1。你写的第一段代码,它去哪里了?在一个新班级里?(包含新代理行的一个。)。我说不出这个例子
private static class BatchInvocationHandler implements InvocationHandler {

    private final ExternalClass ext;

    private Queue<Callable<Object>> delayedInvocations = new ConcurrentLinkedQueue<Callable<Object>>();

    public BatchInvocationHandler(ExternalClass ext) {
        this.ext = ext;
    }

    @Override
    public Object invoke(Object proxy, final Method method, final Object[] args) throws Throwable {
        delayedInvocations.add(new Callable<Object>() {
            @Override
            public Object call() throws Exception {
                return MethodUtils.invokeMethod(ext, method.getName(), args);
            }
        });
        return null;
    }

}