在@WebService类上调用@AroundInvoke拦截器两次,
如果截取的方法是通过端点作为SOAP Web服务从应用程序外部调用的.
如果从另一个bean内部调用相同的方法,它只被调用一次(正如我所料).
问题2:如果我不能,是否有可转移的(服务器独立的)方式来决定我在哪个拦截器中,那么我可以忽略多余的拦截器?
问题3:这种行为是否常见(并在某些文档中定义和描述),
或者它是否依赖于我的特定环境(JBoss EAP 6.4.0)?
观察:
>这两个调用不在同一个拦截器链中.
>它与拦截器类的实例不同.
>两个调用的InvocationContext的实现类都不同.
>有趣的是,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"; } }
TestInterceptor类
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 {},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服务实现类只会委托给它,不再包含拦截器注释.
示例代码可以在这里看到:OSCM Project