我一直试图解决我的问题大约一天,但似乎无法到达任何地方.问题:
我有一个java类,ExternalClass,里面有30个方法.我还有一个ExternalClassFacade接口.
public class ExternalClass {
public method1() {...}
public method2() {...}
...
...
public metod30(...) {...}
}
Run Code Online (Sandbox Code Playgroud)
这个类是一个外部库,我无法修改它的代码.该类运行良好,但我有一种情况,我需要将未定义的时间跨度上的多个调用组合到所有30个方法,延迟执行,并在某个时刻执行所有(串行或并行,我不关心).
例如,超过10分钟,方法1到30将被随机调用500次,我希望它们在被调用的时刻什么也不做,但是在10分钟之后我想调用最初调用的所有500个调用.
大多数方法都需要我需要记住的参数,我将调用这些方法.
我正在寻找一种方法来扩展/包装/复合材料这个类,这样,当有人拨打任一方法,或者,这将弥补原来的方法的调用,以便它们将被推迟,直到合适的时机特殊方法谈到.
我正在考虑扩展类并重写所有方法,并管理30个Struct-Like类来保存有关调用的信息,但这需要:
很多代码,不是很聪明.
我正在寻找一种更好的方法来实现这一点,我正在考虑捕获调用并保持指向原始方法调用的指针,但这是java,所以这是不可能的.
确实非常有趣的问题.第一个问题:是否ExternalClass实现了一些接口?如果是这样,它会简化很多东西,但如果没有,你可以创建一个:
interface ExternalClassFacade {
method1();
method2();
//...
method30();
}
Run Code Online (Sandbox Code Playgroud)
别担心,你不必实现它!只需从中复制所有方法签名即可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();
Run Code Online (Sandbox Code Playgroud)
正如您所看到的,这个神奇而晦涩的代码创建了一些实现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);
}
}
Run Code Online (Sandbox Code Playgroud)
这段代码本身并没有做任何有用的事情 - 当你调用一个方法时,ExternalClassFacade它会ExternalClass使用相同的参数将调用转发给相同的命名方法.所以我们还没有取得任何成就.顺便说一下,我使用MethodUtilsApache Commons Lang来简化反射代码.有可能你已经在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;
}
}
Run Code Online (Sandbox Code Playgroud)
现在我们到了某个地方:不是调用方法,而是将内部包含调用Callable并将其添加到delayedInvocations队列中.当然,因为我们不再调用实际方法,所以返回值只是一个占位符.如果ExternalClass方法的返回类型不同void,则必须非常小心.
我想你现在看到了光明.您需要的一切就是创建一个线程,该线程将Callable收集队列中收集的所有内容并批量运行它们.您可以通过各种方式实现,但基本构建块在那里.您也可以选择数据结构,如map或set,而不是队列.例如,我可以想象由于某种原因按名称分组方法.
当然,如果您可以使用AspectJ/Spring AOP,您将避免使用整个代理基础结构代码.但基本的想法将只是API将更加愉快.
| 归档时间: |
|
| 查看次数: |
1341 次 |
| 最近记录: |