在i = 0
中处理状态的正确方法是什么?
在Wildfly 12中,我想为我们的通用拦截器链创建一个适配器。
让我们简化问题以创建简单的持续时间记录EJB客户端拦截器。
不幸的是b
2方法的设计很奇怪:
org.jboss.ejb.client.EJBClientInterceptor
问题在于,由于拦截器分为两种方法,您必须将状态从“之前”部分(EJBClientInterceptor
)转移到“之后”部分(public interface EJBClientInterceptor {
/**
* Handle the invocation. Implementations may short-circuit the invocation by throwing an exception. This method
* should process any per-interceptor state and call {@link EJBClientInvocationContext#sendRequest()}.
*
* @param context the invocation context
* @throws Exception if an invocation error occurs
*/
void handleInvocation(EJBClientInvocationContext context) throws Exception;
/**
* Handle the invocation result. The implementation should generally call {@link EJBClientInvocationContext#getResult()}
* immediately and perform any post-invocation cleanup task in a finally block.
*
* @param context the invocation context
* @return the invocation result, if any
* @throws Exception if an invocation error occurred
*/
Object handleInvocationResult(EJBClientInvocationContext context) throws Exception;
}
)。
handleInvocation
此输出结果
handleInvocationResult
另一个问题是拦截器实例是单例的,并且可用于所有调用。因此,您不能像这样使用拦截器中的字段
@Override
public void handleInvocation(EJBClientInvocationContext ejbClientInvocationContext) throws Exception {
System.out.println("handleInvocation - before sendRequest");
ejbClientInvocationContext.sendRequest();
System.out.println("handleInvocation - after sendRequest");
}
@Override
public Object handleInvocationResult(EJBClientInvocationContext ejbClientInvocationContext) throws Exception {
System.out.println("handleInvocationResult - before getResult");
try {
return ejbClientInvocationContext.getResult();
} finally {
System.out.println("handleInvocationResult - after getResult");
}
}
另一种方法是使用client_1 | 16:50:24,575 INFO [stdout] (default task-1) handleInvocation - before sendRequest
client_1 | 16:50:24,718 INFO [stdout] (default task-1) handleInvocation - after sendRequest
server_1 | 16:50:25,737 INFO [stdout] (default task-2) Doing work at EJB server
client_1 | 16:50:25,771 INFO [stdout] (default task-1) handleInvocationResult - before getResult
client_1 | 16:50:25,795 INFO [stdout] (default task-1) handleInvocationResult - after getResult
:
public class DurationLogging1ClientInterceptor implements EJBClientInterceptor {
private long startTime;
@Override
public void handleInvocation(EJBClientInvocationContext ejbClientInvocationContext) throws Exception {
startTime = System.currentTimeMillis();
ejbClientInvocationContext.sendRequest();
}
@Override
public Object handleInvocationResult(EJBClientInvocationContext ejbClientInvocationContext) throws Exception {
try {
return ejbClientInvocationContext.getResult();
} finally {
long duration = System.currentTimeMillis() - startTime;
System.out.println("Call took " + duration + "ms");
}
}
}
但是我不确定是否保证方法ThreadLocal
与public class DurationLogging2ClientInterceptor implements EJBClientInterceptor {
private final ThreadLocal<Long> startTimeTL = new ThreadLocal<>();
@Override
public void handleInvocation(EJBClientInvocationContext ejbClientInvocationContext) throws Exception {
startTimeTL.set(System.currentTimeMillis());
ejbClientInvocationContext.sendRequest();
}
@Override
public Object handleInvocationResult(EJBClientInvocationContext ejbClientInvocationContext) throws Exception {
try {
return ejbClientInvocationContext.getResult();
} finally {
long startTime = startTimeTL.get();
long duration = System.currentTimeMillis() - startTime;
System.out.println("Call took " + duration + "ms");
}
}
}
在同一线程中被调用。即使是这样,我也不喜欢使用handleInvocationResult
。
handleInvocation
地图另一种方法是通过ThreadLocal
参数传递状态,也许使用contextData
属性:
EJBClientInvocationContext
但是这个getContextData()
映射被序列化并发送到EJB服务器,这是我不希望的。
在先前的Jboss版本中,public class DurationLogging3ClientInterceptor implements EJBClientInterceptor {
@Override
public void handleInvocation(EJBClientInvocationContext ejbClientInvocationContext) throws Exception {
ejbClientInvocationContext.getContextData().put("startTime", System.currentTimeMillis());
ejbClientInvocationContext.sendRequest();
}
@Override
public Object handleInvocationResult(EJBClientInvocationContext ejbClientInvocationContext) throws Exception {
try {
return ejbClientInvocationContext.getResult();
} finally {
long startTime = (Long) ejbClientInvocationContext.getContextData().get("startTime");
long duration = System.currentTimeMillis() - startTime;
System.out.println("Call took " + duration + "ms");
}
}
}
设计更容易解决了所有这些问题:
contextData
持续时间记录拦截器可以这样写
org.jboss.aop.advice.Interceptor
我创建了一个演示项目,其中在public interface Interceptor {
String getName();
Object invoke(Invocation invocation) throws Throwable;
}
中有2个Wildfly实例。一个实例充当ejb客户端,另一个实例充当ejb服务器。有多个带有拦截器的EJB调用场景演示。
答案 0 :(得分:0)
我认为解决方案 3 还不错,可以通过一些技巧加以改进。
如果您不想通过网络发送实际对象,也许您可以将实际对象的“哈希”放入上下文数据中(我假设您可以为此使用 Objects.hashcode())。
同时,您将使用此唯一哈希键控的对象保存在静态 LRUMap 字段 (https://commons.apache.org/proper/commons-collections/apidocs/org/apache/commons/collections4/map/LRUMap.html) 中。
这样,当您在第二种方法中取回控制权时,您可以根据哈希值查找您保存到静态 Map 中的内容。