本文将讨论一下,在CXF服务端的异常处理,主要包括服务实现类抛出异常与拦截器中抛出异常,抛出异常后CXF的处理流程。其实在CXF中,异常无论是在服务实现类还是拦截器中抛出,其处理方式是一样的,当服务方法中的异常抛出后,CXF会将异常包装为org.apache.cxf.interceptor.Fault类型,而拦截器中只能抛出Fault类型异常,其间接继承于java.lang.RuntimeException,也就是属于运行时异常。根据CXF中Web服务请求处理流程知道,CXF对请求的处理流程是,先调用输入拦截器链,然后调用服务方法,再调用输出拦截器链,最后将结果返回给客户端。拦截器链的实现类为org.apache.cxf.phase.PhaseInterceptorChain,调用拦截器的方法为:doIntercept,下面是源码:
@SuppressWarnings("unchecked")
public synchronized boolean doIntercept(Message message) {
updateIterator();
Message oldMessage = CURRENT_MESSAGE.get();
try {
CURRENT_MESSAGE.set(message);
if (oldMessage != null
&& !message.containsKey(PREVIOUS_MESSAGE)
&& message != oldMessage
&& message.getExchange() != oldMessage.getExchange()) {
message.put(PREVIOUS_MESSAGE, new WeakReference<Message>(oldMessage));
}
while (state == State.EXECUTING && iterator.hasNext()) {
try {
Interceptor<Message> currentInterceptor = (Interceptor<Message>)iterator.next();
if (isFineLogging) {
LOG.fine("Invoking handleMessage on interceptor " + currentInterceptor);
}
currentInterceptor.handleMessage(message);
if (state == State.SUSPENDED) {
// 如果拦截器链处于挂起状态,则抛出异常
throw new SuspendedInvocationException();
}
} catch (SuspendedInvocationException ex) {
// 只处理SuspendedInvocationException
if (iterator.hasPrevious()) {
iterator.previous();
}
pause();
throw ex;
} catch (RuntimeException ex) {
//因为在拦截器链的执行过程抛出的异常都是Fault类型,所以异常由该处理器处理
if (!faultOccurred) {//如果之前没有发生异常
//标记为已发生异常
faultOccurred = true;
StringBuilder description = new StringBuilder();
//拼凑日志内容
//将异常对象作为消息内容
message.setContent(Exception.class, ex);
//调用该拦截器链中之前的拦截器处理异常
unwind(message);
//省略...
//如果异常观察者不为空并且,Exchange不是单向的,调用异常观察者的onMessage方法
//这与消息观察者的机制是一致的,只不过异常观察者针对的是FaultInterceptor
//详情可参见http://blog.csdn.net/xtayfjpk/article/details/45116833
if (faultObserver != null && !isOneWay) {
faultObserver.onMessage(message);
}
}
//标记状态为意外终止
state = State.ABORTED;
}
}
if (state == State.EXECUTING) {
state = State.COMPLETE;
}
return state == State.COMPLETE;
} finally {
CURRENT_MESSAGE.set(oldMessage);
}
}
@SuppressWarnings("unchecked")
public void unwind(Message message) {
while (iterator.hasPrevious()) {
Interceptor<Message> currentInterceptor = (Interceptor<Message>)iterator.previous();
if (isFineLogging) {
LOG.fine("Invoking handleFault on interceptor " + currentInterceptor);
}
try {
//依次调用已经迭代过的拦截器的handleFault方法
//如果异常是在拦截器中抛出的,则会包含抛出异常的拦截器
currentInterceptor.handleFault(message);
} catch (RuntimeException e) {
LOG.log(Level.WARNING, "Exception in handleFault on interceptor " + currentInterceptor, e);
throw e;
} catch (Exception e) {
LOG.log(Level.WARNING, "Exception in handleFault on interceptor " + currentInterceptor, e);
throw new RuntimeException(e);
}
}
}
从上面的源码分析,我们可以得到几个结论:
1. 无论是服务方法还是在拦截器中抛出的异常都被包装为了org.apache.cxf.interceptor.Fault类型
2. 异常一旦抛出,拦截器链立即终止执行,转入异常拦截器链执行
3. 输入拦截器链与输出拦截器链之间异常不会相互影响,也就是说如果输出拦截器链中抛出异常不会调用到输入拦截器链的异常处理方法
4. 在异常处理过程中,异常对象作为消息内容放置Message中,即:message.setContent(Exception.class, ex);