好吧,第一个可能建议采用“低技术”方法:为所有处理 Web 服务的基类try/catch
in a invoke
方法。将此方法委托给抽象doInvoke
方法,并使其成为所有 JAXWS 实现仅调用的策略invoke
.
@kan涉及AOP的解决方案也肯定是一个可能的解决方案。
但是,如果您想构建自定义错误拦截,则可以在 CXF 级别执行此操作。
当与 JAX-WS 一起使用时,最简单形式的 CXF 可以被视为围绕 JAX WS 引擎的(复杂)拦截器链:拦截器链是 CXF 的所有“内容”所在的地方。
CXF 拦截器被排列成链(“in”链、“in failure”链、“out”和“out failure”链)。
每个链都有不同的“阶段”,例如:RECEIVE, (PRE/USER/POST)_STREAM, READ, (PRE/USER/POST)PROTOCOL, UNMARSHAL, (PRE/USER/POST)LOGICAL, PRE_INVOKE, INVOKE, POST_INVOKE
是传入链的默认阶段。
拦截器“按顺序”执行(阶段与优先级相关联,拦截器实现声明它们属于哪个阶段。在阶段内,每个拦截器可以选择放置在某个其他拦截器类之前或之后)。
对于您的情况来说,最重要的是ServiceInvokerInterceptor
,属于INVOKE
阶段,负责调用@Webservice
。当处理“in”链中的所有拦截器时,CXF 将响应对象处理到“out”链以序列化输出(或者如果您有单向 SOAP 方法,则停止此处的所有内容,这是一种特殊情况)。
如果标准链中的任何地方发生异常,CXF 将做两件事:
- 它将停止链,并使用handleFault方法调用所有已按相反顺序处理的拦截器
- 然后它将控制转发到故障拦截器链(“故障中”、“故障外”)。
因此,添加自己的 SOAP 错误“捕获所有”错误处理的一种可能方法是使用基于此生命周期的拦截器。
您创建一个拦截器实现(AbstractSoapInterceptor 对此很有用)
您将其绑定到 INVOKE 阶段,在ServiceInvokerInterceptor
public class YourInterceptor extends AbstractSoapInterceptor {
public YourInterceptor() {
super(Phase.INVOKE);
addBefore(Arrays.asList(ServiceInvokerInterceptor.class.getName()));
// This means handleMessage will be called juste before your @WebMethod
// If it fails, you will be the first to be noticed through #handleFault()
}
}
当“正常消息通过”时,该拦截器不会执行任何操作:
@Override
public void handleMessage(SoapMessage message) throws Fault {
// Do nothing
}
但它是为了处理故障:
@Override
public void handleFault(SoapMessage message) {
// Every exception will be wrapped into a Fault object by CXF
Fault f = (Fault) message.getContent(Exception.class);
// You should inspect its g.getCause() to maybe identify what went wrong
// A CXF Fault also much ressembles a SOAPFault element
f.setMessage("Your SOAP Fault message");
// You can access the DOM detail of the fault
Element detail = f.getOrCreateDetail();
Element newDetailEntry = detail.getOwnerDocument().createElementNS("detailNs", "detailName");
newDetailEntry.setTextContent("Content for your soap fault detail");
detail.appendChild(newDetailEntry);
// And so on. f.setFaultCode(qName);...
}
另一种实现方式是交换原来的Fault
按习俗SoapFault
,它也是一个子类Fault
,如果它对你来说更有意义。
诚然,这比启动您自己的异常更困难,但它允许您构建精确的、有意义的肥皂故障。但请注意,最好只启动作为 WSDL 一部分存在的 SOAP 错误,因此为了与客户端“良好”地合作,不要在此处构建与您的 WSDL 不匹配的错误(在您的情况下,@Webfault
定义)。
最后,您必须声明要添加到链中的拦截器。有多种方法可以做到这一点:在每个 bean 的基础上:
<bean id="myIt" class="com.yourInterceptor" />
<jaxws:endpoint implementor="de.MyService" address="/MyService">
<jaxws:inInterceptors>
<ref bean="myIt"/>
</jaxws:inInterceptor>
</jaxws:endpoint>
或者在总线级别。
<cxf:bus>
<cxf:inInterceptors>
<ref bean="myIt"/>
</cxf:inInterceptors>
</cxf:bus>