gpt4 book ai didi

java - 如何在 EJBClientInterceptor 中管理状态

转载 作者:行者123 更新时间:2023-12-05 07:12:11 26 4
gpt4 key购买 nike

org.jboss.ejb.client.EJBClientInterceptor 中处理状态的正确方法是什么?

在 Wildfly 12 中,我想为我们的通用拦截器链创建一个适配器。让我们简化问题以创建简单的持续时间日志记录 EJB 客户端拦截器。不幸的是,EJBClientInterceptor 2 方法设计很奇怪:

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

问题在于,由于拦截器分为 2 个方法,您必须将状态从“之前”部分 (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

错误的解决方案 #1 - 实例字段

另一个问题是拦截器实例是单例的,并且被所有调用重用。所以你不能像这样在拦截器中使用字段

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

错误的解决方案 #2 - ThreadLocal

另一种方法是使用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 的用法。

错误的解决方案 #3 - 使用 contextData map

另一种方法是通过 EJBClientInvocationContext 参数传输状态,可能使用 getContextData() 属性:

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 映射被序列化并发送到 EJB 服务器,这是我不想做的事情。

以前的 Jboss 拦截器设计

在以前的 Jboss 版本中,org.jboss.aop.advice.Interceptor 设计更简单,解决了所有这些问题:

public interface Interceptor {
String getName();

Object invoke(Invocation invocation) throws Throwable;
}

持续时间日志拦截器可以这样写

public class DurationLogging4ClientInterceptor implements Interceptor {

@Override
public String getName() {
return getClass().getName();
}

@Override
public Object invoke(Invocation invocation) throws Throwable {
long startTime = System.currentTimeMillis();

try {
return invocation.invokeNext();
} finally {
long duration = System.currentTimeMillis() - startTime;
System.out.println("Call took " + duration + "ms");
}
}
}

可玩的 Dockerized 演示项目

我在 docker-compose 中创建了一个包含 2 个 Wildfly 实例的演示项目。一个实例充当 ejb-client,另一个实例充当 ejb-server。有多个带拦截器的EJB调用场景演示。

https://github.com/petr-ujezdsky/w2w

最佳答案

我认为解决方案 3 还不错,可以通过一些技巧加以改进。

如果您不想通过线路发送实际对象,也许您可​​以将实际对象的“散列”放入上下文数据中(我假设您可以为此目的使用 Objects.hashcode())。

同时,您在静态 LRUMap 字段 (https://commons.apache.org/proper/commons-collections/apidocs/org/apache/commons/collections4/map/LRUMap.html) 中保存使用此唯一哈希键控的对象。

这样,当您在第二种方法中取回控件时,您可以根据其哈希查找保存到静态 map 中的内容。

关于java - 如何在 EJBClientInterceptor 中管理状态,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/60525250/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com