繁体   English   中英

如何拦截方法调用以延迟执行,将所有调用组合在一起并在java中执行?

[英]How do I intercept method calls in order to delay the execution, group all calls together and execute in java?

我一直试图解决我的问题大约一天,但似乎无法到达任何地方。 问题:

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

public class ExternalClass {
  public method1() {...}
  public method2() {...}
  ...
  ...
  public metod30(...) {...}
}

这个类是一个外部库,我无法修改它的代码。 该类运行良好,但我有一种情况,我需要将未定义的时间跨度上的多个调用组合到所有30个方法,延迟执行,并在某个时刻执行所有(串行或并行,我不关心)。

例如,超过10分钟,方法1到30将被随机调用500次,我希望它们在被调用的时刻什么也不做,但是在10分钟后我想调用最初调用的所有500个调用。

大多数方法都需要我需要记住的参数,我将调用这些方法。

我正在寻找一种方法来扩展/包装/复合这个类,这样当有人调用这些方法中的任何一个时,或者,一个特殊的方法将桥接对原始方法的调用,以便它们被推迟到正确的时刻谈到。

我正在考虑扩展类并重写所有方法,并管理30个Struct-Like类来保存有关调用的信息,但这需要:

  • 30次覆盖
  • 30个清单
  • 30班

很多代码,不是很聪明。

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

确实非常有趣的问题。 第一个问题: 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相同的方法。 这是一个缺失的难题:

public class BatchInvocationHandler implements InvocationHandler {

    private final ExternalClass ext;

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

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

}

这段代码本身并没有做任何有用的事情 - 当你在ExternalClassFacade上调用一个方法时,它会使用相同的参数将调用转发给ExternalClass上的同一个命名方法。 所以我们还没有取得任何成就。 BTW我正在使用Apache Commons Lang的 MethodUtils来简化反射代码。 有可能你已经在CLASSPATH上有这个库,如果没有,它只是几行额外的代码。

现在看看这个改进版本:

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;
    }

}

现在我们到了某个地方:不是调用方法,而是将调用包装在Callable并将其添加到delayedInvocations队列中。 当然,因为我们不再调用实际方法,所以返回值只是一个占位符。 如果ExternalClass方法的返回类型不同于void ,则必须非常小心。

我想你现在看到了光明。 您需要的一切是创建一个线程,该线程将收集队列中收集的所有Callable并批量运行它们。 您可以通过各种方式实现,但基本构建块在那里。 您也可以选择数据结构,如map或set而不是队列。 例如,我可以想象由于某种原因按名称分组方法。


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

使用AspectJ,您可以引入的接口 ,然后代码到接口。 之后,您可以自由添加所需的界面背后的任何行为。 或者,只需使用AspectJ编织您正在寻找的收集/执行行为。

CglibJavassist还允许您通过动态子类化基本代理该类(假设它不是最​​终的)来更干净地完成它。

有很多选择。 这是我发生的三个第三方。 其中一些方法的一个优点是它们可以为您提供对象形式的方法调用的一些表示,您可以在以后轻松地收集和运行它。

您可以使用方面拦截所有调用以执行外部lib方法并暂停Thread,将Thread的ID写入同步Set。 该Set是由另一个Thread监视的单例。

当您的业务规则被触发时,让单例观察者迭代Set并通知每个线程继续处理。 该方面将继续并执行每个最初请求的外部方法。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM