简体   繁体   English

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

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

I've been attempting to resolve my problem for about a day now, but can't seem to get anywhere. 我一直试图解决我的问题大约一天,但似乎无法到达任何地方。 The problem: 问题:

I have a java class, ExternalClass which has 30 methods in it. 我有一个java类,ExternalClass,里面有30个方法。 I also have an interface ExternalClassFacade. 我还有一个ExternalClassFacade接口。

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

This class is an external library and I cannot modify it's code. 这个类是一个外部库,我无法修改它的代码。 The class works well but I have a situation where I need to group up multiple calls on an undefined timespan to all 30 methods, delay the execution, and execute all at once (serial or parallel I don't care) at some moment. 该类运行良好,但我有一种情况,我需要将未定义的时间跨度上的多个调用组合到所有30个方法,延迟执行,并在某个时刻执行所有(串行或并行,我不关心)。

For example, over 10 minutes, methods 1 to 30 will be called randomly 500 times, I want them to do nothing at the moment of being invoked, but after 10 minutes I want to invoke all 500 calls as they were originally called. 例如,超过10分钟,方法1到30将被随机调用500次,我希望它们在被调用的时刻什么也不做,但是在10分钟后我想调用最初调用的所有500个调用。

most of the methods require parameters which I need to remember for the moment in which i will call the methods. 大多数方法都需要我需要记住的参数,我将调用这些方法。

I'm looking for a way to extend/wrap/composite this class, so that when someone calls any of these methods, or, a special method that will bridge the calls to the original methods so that they will be delayed till the right moment comes. 我正在寻找一种方法来扩展/包装/复合这个类,这样当有人调用这些方法中的任何一个时,或者,一个特殊的方法将桥接对原始方法的调用,以便它们被推迟到正确的时刻谈到。

I was thinking about extending the class and overriding all methods, and managing 30 Struct-Like classes to hold the info about the calls, but that would require : 我正在考虑扩展类并重写所有方法,并管理30个Struct-Like类来保存有关调用的信息,但这需要:

  • 30 overrides 30次覆盖
  • 30 lists 30个清单
  • 30 classes 30班

Lots of code, not very smart. 很多代码,不是很聪明。

I'm looking for a better way to do this, I was thinking about catching the calls and keeping the pointer to the original method call, but this is java, so it's not possible. 我正在寻找一种更好的方法来实现这一点,我正在考虑捕获调用并保持指向原始方法调用的指针,但这是java,所以这是不可能的。

Very interesting problem indeed. 确实非常有趣的问题。 First question: does ExternalClass implement some interface? 第一个问题: ExternalClass实现了一些接口? If it does, it simplifies stuff a lot, however if it doesn't, you can create one: 如果是这样,它会简化很多东西,但如果没有,你可以创建一个:

interface ExternalClassFacade {
    method1();
    method2();
            //...
    method30();
}

Don't worry, you don't have to implement it! 别担心,你不必实现它! Just copy all the method signatures from the ExternalClass . 只需复制ExternalClass所有方法签名。 Do you know java.lang.Proxy ? 你知道java.lang.Proxy吗? Wonderful tool in such problems like yours: 像你这样的问题的奇妙工具:

ExternalClass ext = //obtain target ExternalClass somehow
ExternalClassFacade extFacade = (ExternalClassFacade) Proxy.newProxyInstance(
    ExternalClass.class.getClassLoader(), 
    new Class<?>[]{ExternalClassFacade.class},
    new BatchInvocationHandler(ext));
extFacade.method1();

As you can see this magic and obscure code created something that implements ExternalClassFacade and allows you to run the same methods as ExternalClass . 正如您所看到的,这个神奇而模糊的代码创建了一些实现ExternalClassFacade东西,并允许您运行与ExternalClass相同的方法。 Here is the missing puzzle: 这是一个缺失的难题:

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

}

This code itself is not doing anything useful - when you call a method on the ExternalClassFacade it forwards the call to the same named method on ExternalClass with the same arguments. 这段代码本身并没有做任何有用的事情 - 当你在ExternalClassFacade上调用一个方法时,它会使用相同的参数将调用转发给ExternalClass上的同一个命名方法。 So we haven't achieved anything yet. 所以我们还没有取得任何成就。 BTW I am using MethodUtils from Apache Commons Lang to simplify the reflection code a bit. BTW我正在使用Apache Commons Lang的 MethodUtils来简化反射代码。 Chances are you already have this library on the CLASSPATH, if not, it is just few lines of extra code. 有可能你已经在CLASSPATH上有这个库,如果没有,它只是几行额外的代码。

Now look at this improved version: 现在看看这个改进版本:

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

}

Now we are getting somewhere: instead of calling the method we are wrapping the call inside Callable and adding it to a delayedInvocations queue. 现在我们到了某个地方:不是调用方法,而是将调用包装在Callable并将其添加到delayedInvocations队列中。 Of course since we are no longer calling the actual method, the return value is just a placeholder. 当然,因为我们不再调用实际方法,所以返回值只是一个占位符。 If ExternalClass methods have return types different than void , you must be very careful. 如果ExternalClass方法的返回类型不同于void ,则必须非常小心。

I think you see the light now. 我想你现在看到了光明。 Everything you need is to create a thread that will take all the Callable s collected in the queue and run them in batch. 您需要的一切是创建一个线程,该线程将收集队列中收集的所有Callable并批量运行它们。 You can do it in various ways, but the basic building blocks are there. 您可以通过各种方式实现,但基本构建块在那里。 Also you might choose data structure like map or set rather than a queue. 您也可以选择数据结构,如map或set而不是队列。 I can for instance imagine grouping methods by name for some reason. 例如,我可以想象由于某种原因按名称分组方法。


Of course if you can use AspectJ/Spring AOP you will avoid the whole proxy infrastructure code. 当然,如果您可以使用AspectJ / Spring AOP,您将避免使用整个代理基础结构代码。 But the basic idea will be the same only that the API will be more pleasent. 但基本的想法将只是API将更加愉快。

Using AspectJ, you could introduce an interface to the class, then code to the interface. 使用AspectJ,您可以引入的接口 ,然后代码到接口。 After that, you're free to add whatever behavior behind the interface that you want. 之后,您可以自由添加所需的界面背后的任何行为。 Alternately, just use AspectJ to weave in the collecting/executing behavior you're looking for. 或者,只需使用AspectJ编织您正在寻找的收集/执行行为。

Cglib or Javassist would also let you do it more cleanly by letting you basically proxy the class by dynamic subclassing (assuming it's not final). CglibJavassist还允许您通过动态子类化基本代理该类(假设它不是最​​终的)来更干净地完成它。

There are plenty of options. 有很多选择。 Those are three third-party ones that occurred to me. 这是我发生的三个第三方。 An advantage of some of these approaches is that they'll give you some representation of a method invocation in object form, which you can easily collect and run at a later time. 其中一些方法的一个优点是它们可以为您提供对象形式的方法调用的一些表示,您可以在以后轻松地收集和运行它。

You could use an aspect to intercept all calls to execute external lib methods and to pause the Thread, writing the Thread's ID to a synchronized Set. 您可以使用方面拦截所有调用以执行外部lib方法并暂停Thread,将Thread的ID写入同步Set。 That Set is in a singleton being watched by another Thread. 该Set是由另一个Thread监视的单例。

When your business rule to execute is fired, have the singleton watcher iterate the Set and notify each thread to continue processing. 当您的业务规则被触发时,让单例观察者迭代Set并通知每个线程继续处理。 The aspect will continue on and execute each originally requested external method. 该方面将继续并执行每个最初请求的外部方法。

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

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