如何在EJBClientInterceptor中管理状态

时间:2020-03-04 11:40:56

标签: java wildfly ejb-3.0 interceptor

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

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

另一个问题是拦截器实例是单例的,并且可用于所有调用。因此,您不能像这样使用拦截器中的字段

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

错误的解决方案2-ThreadLocal

另一种方法是使用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");
        }
    }
}

但是我不确定是否保证方法ThreadLocalpublic 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

错误的解决方案3-使用handleInvocation地图

另一种方法是通过ThreadLocal参数传递状态,也许使用contextData属性:

EJBClientInvocationContext

但是这个getContextData()映射被序列化并发送到EJB服务器,这是我不希望的。

以前的Jboss拦截器设计

在先前的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

要与Docker化的演示项目一起玩

我创建了一个演示项目,其中在public interface Interceptor { String getName(); Object invoke(Invocation invocation) throws Throwable; } 中有2个Wildfly实例。一个实例充当ejb客户端,另一个实例充当ejb服务器。有多个带有拦截器的EJB调用场景演示。

this

1 个答案:

答案 0 :(得分:0)

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

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

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

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