java - @AroundInvoke 拦截器在@WebService 类上被调用两次

标签 java web-services jakarta-ee aop interceptor

总结

@AroundInvoke拦截器在 @WebService 上被调用两次类(class), 如果拦截的方法是通过作为 SOAP Web 服务的端点从应用程序的外部调用的。
如果从另一个 bean 中内部调用非常相同的方法,它只会被调用一次(如我所料)。

被拦截的方法本身总是只被调用一次!

问题一:能否让拦截器只调用一次?

问题 2:如果我不能,是否有一种可转移(独立于服务器)的方式来决定我在哪个拦截器中,以便我可以忽略多余的拦截器?

问题 3:这种行为是否常见(并在某些文档中定义和描述), 还是取决于我的特定环境 (JBoss EAP 6.4.0)?

观察:

  1. 这两个调用在同一个拦截器链中。
  2. 它不是拦截器类的同一个实例。
  3. InvocationContext 的实现类两个调用都不同。
  4. 有趣的是,contextData 之一, InvocationContext用于沿拦截器链传递数据的字段不是 HashMap 的实例,但是 WrappedMessageContext , 但它不包裹另一个 contextData无论如何。

最少的可重现代码

(我去掉了包名。)

MyEndpoint 界面

import javax.jws.WebService;

@WebService
public interface MyEndpoint {
    public static final String SERVICE_NAME = "MyEndpointService";
    public String getHello();
}

MyEndpointImpl 类

import javax.interceptor.Interceptors;
import javax.jws.WebService;

@WebService(endpointInterface = "MyEndpoint", serviceName = MyEndpoint.SERVICE_NAME)
@Interceptors({TestInterceptor.class})
public class MyEndpointImpl implements MyEndpoint {
    @Override
    public String getHello() {
        System.out.println("MyEndpointImpl.getHello() called");
        return "Hello";
    }
}

测试拦截器类

import javax.interceptor.AroundInvoke;
import javax.interceptor.InvocationContext;

public class TestInterceptor {
    @AroundInvoke
    private Object countCalls(InvocationContext ic) throws Exception {
        System.out.println("Interceptor called");
        return ic.proceed();
    }
}

输出

Interceptor called
Interceptor called
MyEndpointImpl.getHello() called

更多详情

为了获得更多运行时信息,我添加了更多日志记录。

MyEndpointImpl 类

import java.lang.reflect.Method;
import java.util.Map;
import javax.interceptor.AroundInvoke;
import javax.interceptor.InvocationContext;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class TestInterceptor {
    private static Logger logger = LoggerFactory.getLogger(TestInterceptor.class);
    private static int callCnt = 0;

    @AroundInvoke
    private Object countCalls(InvocationContext ic) throws Exception {
        final String interceptorClass = this.toString();
        final String invocationContextClass = ic.getClass().getName();
        final Method method = ic.getMethod();
        final String calledClass = method.getDeclaringClass().getName();
        final String calledName = method.getName();
        final String message = String.format(
                "%n    INTERCEPTOR: %s%n    InvocationContext: %s%n    %s # %s()",
                interceptorClass, invocationContextClass, calledClass, calledName);
        logger.info(message);

        final int call = ++callCnt;
        final Map<String, Object> contextData = ic.getContextData();
        contextData.put("whoami", call);

        logger.info("BEFORE PROCEED {}, {}", call, contextData);
        final Object ret = ic.proceed();
        logger.info("AFTER PROCEED {}, {}", call, contextData);
        return ret;
    }
}

输出

    INTERCEPTOR: TestInterceptor@74c90b72
    InvocationContext: org.jboss.invocation.InterceptorContext$Invocation
    MyEndpointImpl # getHello()
BEFORE PROCEED 1, org.apache.cxf.jaxws.context.WrappedMessageContext@2cfccb1d
    INTERCEPTOR: TestInterceptor@5226f6d8
    InvocationContext: org.jboss.weld.interceptor.proxy.InterceptorInvocationContext
    MyEndpointImpl # getHello()
BEFORE PROCEED 2, {whoami=2}
MyEndpointImpl.getHello() called
AFTER PROCEED 2, {whoami=2}
AFTER PROCEED 1, org.apache.cxf.jaxws.context.WrappedMessageContext@2cfccb1d

最佳答案

我不能直接回答你的问题,但也许对上下文的一些澄清可能会对你有所帮助。

Java EE JAX-WS 实现因服务器而异。例如,Glassfish 使用 Metro,JBoss 使用 Apache CXF。

有不同类型的拦截器链允许以编程方式控制请求/响应处理前后的条件。

SOAP Web 服务调用的拦截器是 SOAP 处理程序和逻辑处理程序(参见 Oracle documentation)。两者都可以访问不同级别的 SOAP 消息(整个或仅有效负载)。

我的假设是您的拦截器调用了两次,一次是通过 HTTP/SOAP 访问,一次是通过 RMI 访问。

在第一个拦截器调用中,您看到的上下文是 org.apache.cxf.jaxws.context.WrappedMessageContext,它是一个 Map 实现。参见 WarppedMessageContext , Apache CXF web service context .它被调用以进行 HTTP/SOAP 访问。

第二次调用是您在使用 RMI 时所期望的(可能在处理 SOAP 消息后从 Apache CXF 触发)。

为避免这种情况,您可以使用第三类来定义拦截器的逻辑实现。已有的web服务实现类只会委托(delegate)给它,不再包含拦截器注解。

示例代码可以在这里看到:OSCM Project

关于java - @AroundInvoke 拦截器在@WebService 类上被调用两次,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/33640762/

相关文章:

java - Gson 忽略映射中键的自定义序列化器

java - 响应来自 Java 套接字的 AJAX 请求

c# - 将 FedEx .net Web 服务从开发人员环境转移到生产环境的最佳方法是什么?

java - WSDL2Java Axis 之后无法建立与 SOAP WebService 的连接

java - 具有容器管理事务的单例 EJB 并发

css - 错误验证css时jsf形式的位移组件

java - JAX-WS : [java] java. io.IOException:WSDL2Java 发射器超时(这通常意味着指定 URL 处的 WSDL 无法访问)

java - ATG JavaBean over RepositoryItem

web-services - 如何使用HATEOAS和查询参数进行RESTful搜索?

java - 接收序列化对象时出现 "javax.servlet.ServletException: java.lang.RuntimeException: No ClassLoaders found for:"异常