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