[英]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类来保存有关调用的信息,但这需要:
很多代码,不是很聪明。
我正在寻找一种更好的方法来实现这一点,我正在考虑捕获调用并保持指向原始方法调用的指针,但这是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将更加愉快。
您可以使用方面拦截所有调用以执行外部lib方法并暂停Thread,将Thread的ID写入同步Set。 该Set是由另一个Thread监视的单例。
当您的业务规则被触发时,让单例观察者迭代Set并通知每个线程继续处理。 该方面将继续并执行每个最初请求的外部方法。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.