最近(5年前写的未发布)在做CXF restful 风格接口,为了更好的集成框架,所以分析下cxf请求过程,在此记录下,
首先从org.apache.cxf.transport.servlet.CXFServlet作为入口来解析请求过程
从父类AbstractHTTPServlet的service方法开始进入
@Override
public void service(ServletRequest req, ServletResponse res)
throws ServletException, IOException {
HttpServletRequest request;
HttpServletResponse response;
try {
request = (HttpServletRequest) req;
response = (HttpServletResponse) res;
} catch (ClassCastException e) {
throw new ServletException("Unrecognized HTTP request or response object");
}
String method = request.getMethod();
if (KNOWN_HTTP_VERBS.contains(method)) {
super.service(request, response);//这个父类的方法最终调用了CXFServlet的get或者post方法,而两个方法都调用了handleRequest这个方法
} else {
handleRequest(request, response);
}
}
进入handleRequest方法:
protected void handleRequest(HttpServletRequest request, HttpServletResponse response)
throws ServletException {
if ((dispatcherServletPath != null || dispatcherServletName != null)
&& (redirectList != null && matchPath(redirectList, request)
|| redirectList == null)) {
// if no redirectList is provided then this servlet is redirecting only
redirect(request, response, request.getPathInfo());
return;
}
boolean staticResourcesMatch = staticResourcesList != null
&& matchPath(staticResourcesList, request);
boolean staticWelcomeFileMatch = staticWelcomeFile != null
&& (StringUtils.isEmpty(request.getPathInfo()) || request.getPathInfo().equals("/"));
if (staticResourcesMatch || staticWelcomeFileMatch) {
serveStaticContent(request, response,
staticWelcomeFileMatch ? staticWelcomeFile : request.getPathInfo());
return;
}
request = checkXForwardedHeaders(request);
invoke(request, response);// 父类CXFNonSpringServlet的方法,最终调用了ServletController的invoke方法,
}
我们直接进入ServletController的invoke方法:
public boolean invoke(HttpServletRequest request, HttpServletResponse res, boolean returnErrors)
throws ServletException {
try {
String pathInfo = request.getPathInfo() == null ? "" : request.getPathInfo();
AbstractHTTPDestination d = destinationRegistry.getDestinationForPath(pathInfo, true);
if (d == null) {
if (!isHideServiceList && (request.getRequestURI().endsWith(serviceListRelativePath)
|| request.getRequestURI().endsWith(serviceListRelativePath + "/")
|| StringUtils.isEmpty(pathInfo)
|| "/".equals(pathInfo))) {
if (isAuthServiceListPage) {
setAuthServiceListPageAttribute(request);
}
setBaseURLAttribute(request);
serviceListGenerator.service(request, res);
} else {
d = destinationRegistry.checkRestfulRequest(pathInfo);
if (d == null || d.getMessageObserver() == null) {
if (returnErrors) {
LOG.warning("Can't find the the request for "
+ request.getRequestURL() + "'s Observer ");
generateNotFound(request, res);
}
return false;
}
}
}
if (d != null) {
Bus bus = d.getBus();
ClassLoaderHolder orig = null;
try {
if (bus != null) {
ClassLoader loader = bus.getExtension(ClassLoader.class);
if (loader == null) {
ResourceManager manager = bus.getExtension(ResourceManager.class);
if (manager != null) {
loader = manager.resolveResource("", ClassLoader.class);
}
}
if (loader != null) {
//need to set the context classloader to the loader of the bundle
orig = ClassLoaderUtils.setThreadContextClassloader(loader);
}
}
updateDestination(request, d);
invokeDestination(request, res, d); //调用处理方法
} finally {
if (orig != null) {
orig.reset();
}
}
}
} catch (IOException e) {
throw new ServletException(e);
}
return true;
}
public void invokeDestination(final HttpServletRequest request, HttpServletResponse response,
AbstractHTTPDestination d) throws ServletException {
if (LOG.isLoggable(Level.FINE)) {
LOG.fine("Service http request on thread: " + Thread.currentThread());
}
try {
d.invoke(servletConfig, servletConfig.getServletContext(), request, response);//从这里进入
} catch (IOException e) {
throw new ServletException(e);
} finally {
if (LOG.isLoggable(Level.FINE)) {
LOG.fine("Finished servicing http request on thread: " + Thread.currentThread());
}
}
}
org.apache.cxf.transport.http.AbstractHTTPDestination类的invoke方法
public void invoke(final ServletConfig config,
final ServletContext context,
final HttpServletRequest req,
final HttpServletResponse resp) throws IOException {
Message inMessage = retrieveFromContinuation(req);
if (inMessage == null) {
LOG.fine("Create a new message for processing");
inMessage = new MessageImpl();
ExchangeImpl exchange = new ExchangeImpl();
exchange.setInMessage(inMessage);
setupMessage(inMessage,
config,
context,
req,
resp);//这里封装Message对象
exchange.setSession(new HTTPSession(req));
((MessageImpl)inMessage).setDestination(this);
} else {
LOG.fine("Get the message from the request for processing");
}
copyKnownRequestAttributes(req, inMessage);
try {
incomingObserver.onMessage(inMessage);
invokeComplete(context, req, resp, inMessage);
} catch (SuspendedInvocationException ex) {
if (ex.getRuntimeException() != null) {
throw ex.getRuntimeException();
}
//else nothing to do, just finishing the processing
} catch (Fault ex) {
Throwable cause = ex.getCause();
if (cause instanceof RuntimeException) {
throw (RuntimeException)cause;
} else {
throw ex;
}
} catch (RuntimeException ex) {
throw ex;
} finally {
if (LOG.isLoggable(Level.FINE)) {
LOG.fine("Finished servicing http request on thread: " + Thread.currentThread());
}
}
}
这个方法可以了解Message消息的内容,方法内容很容易看明白
protected void setupMessage(final Message inMessage,
final ServletConfig config,
final ServletContext context,
final HttpServletRequest req,
final HttpServletResponse resp) throws IOException {
setupContinuation(inMessage,
req,
resp);
final Exchange exchange = inMessage.getExchange();
DelegatingInputStream in = new DelegatingInputStream(req.getInputStream()) {
public void cacheInput() {
if (!cached && (exchange.isOneWay() || isWSAddressingReplyToSpecified(exchange))) {
//For one-ways and WS-Addressing invocations with ReplyTo address,
//we need to cache the values of the HttpServletRequest
//so they can be queried later for things like paths and schemes
//and such like that.
//Please note, exchange used to always get the "current" message
exchange.getInMessage().put(HTTP_REQUEST, new HttpServletRequestSnapshot(req));
}
super.cacheInput();
}
private boolean isWSAddressingReplyToSpecified(Exchange ex) {
AddressingProperties map = ContextUtils.retrieveMAPs(ex.getInMessage(), false, false, false);
return map != null && !ContextUtils.isGenericAddress(map.getReplyTo());
}
};
inMessage.setContent(DelegatingInputStream.class, in);
inMessage.setContent(InputStream.class, in);
inMessage.put(HTTP_REQUEST, req);
inMessage.put(HTTP_RESPONSE, resp);
inMessage.put(HTTP_CONTEXT, context);
inMessage.put(HTTP_CONFIG, config);
inMessage.put(HTTP_CONTEXT_MATCH_STRATEGY, contextMatchStrategy);
inMessage.put(Message.HTTP_REQUEST_METHOD, req.getMethod());
String requestURI = req.getRequestURI(); //请求的全路径
inMessage.put(Message.REQUEST_URI, requestURI);
String requestURL = req.getRequestURL().toString();
inMessage.put(Message.REQUEST_URL, requestURL);
String contextPath = req.getContextPath();
if (contextPath == null) {
contextPath = "";
}
String servletPath = req.getServletPath();
if (servletPath == null) {
servletPath = "";
}
String contextServletPath = contextPath + servletPath;
inMessage.put(Message.PATH_INFO, contextServletPath + req.getPathInfo());
if (!StringUtils.isEmpty(requestURI)) {//进入这个方法
int index = requestURL.indexOf(requestURI);
if (index > 0) {
// Can be useful for referencing resources with URIs not covered by CXFServlet.
// For example, if we a have web application name 'app' and CXFServlet listening
// on "/services/*" then having HTTP_BASE_PATH pointing to say
// http://localhost:8080/app will make it easy to refer to non CXF resources
String schemaInfo = requestURL.substring(0, index);
String basePathWithContextOnly = schemaInfo + contextPath;
inMessage.put(HTTP_BASE_PATH, basePathWithContextOnly);//请求上下文url
}
} else if (!StringUtils.isEmpty(servletPath) && requestURL.endsWith(servletPath)) {
int index = requestURL.lastIndexOf(servletPath);
if (index > 0) {
inMessage.put(HTTP_BASE_PATH, requestURL.substring(0, index));
}
}
String contentType = req.getContentType();//请求内容类型
inMessage.put(Message.CONTENT_TYPE, contentType);
setEncoding(inMessage, req, contentType);//设置编码,优先从contentType中取,再从req中取
inMessage.put(Message.QUERY_STRING, req.getQueryString());// ?号后面参数
inMessage.put(Message.ACCEPT_CONTENT_TYPE, req.getHeader("Accept"));//客户端接收内容类型
String basePath = getBasePath(contextServletPath); //服务的地址前缀,spring配置文件的address <jaxrs:server address="/rs/supplier">
if (!StringUtils.isEmpty(basePath)) {
inMessage.put(Message.BASE_PATH, basePath);
}
inMessage.put(Message.FIXED_PARAMETER_ORDER, isFixedParameterOrder());
inMessage.put(Message.ASYNC_POST_RESPONSE_DISPATCH, Boolean.TRUE);
final Principal pp = req.getUserPrincipal();
inMessage.put(SecurityContext.class, new SecurityContext() {
public Principal getUserPrincipal() {
return pp;
}
public boolean isUserInRole(String role) {
return req.isUserInRole(role);
}
});
Headers headers = new Headers(inMessage);
headers.copyFromRequest(req);
String credentials = headers.getAuthorization();//从头部获取凭证
AuthorizationPolicy authPolicy = getAuthorizationPolicyFromMessage(credentials, pp);//验证的策略
inMessage.put(AuthorizationPolicy.class, authPolicy);
propogateSecureSession(req, inMessage);//ssl请求处理
inMessage.put(CertConstraints.class.getName(), certConstraints);
inMessage.put(Message.IN_INTERCEPTORS,
Arrays.asList(new Interceptor[] {CertConstraintsInterceptor.INSTANCE}));//针对ssl请求拦截器
}
incomingObserver.onMessage(inMessage);方法调用了org.apache.cxf.transport.ChainInitiationObserver类onMessage
public void onMessage(Message m) {
Bus origBus = BusFactory.getAndSetThreadDefaultBus(bus);
ClassLoaderHolder origLoader = null;
try {
if (loader != null) {
origLoader = ClassLoaderUtils.setThreadContextClassloader(loader);
}
InterceptorChain phaseChain = null;
if (m.getInterceptorChain() != null) {
phaseChain = m.getInterceptorChain();
// To make sure the phase chain is run by one thread once
synchronized (phaseChain) {
if (phaseChain.getState() == InterceptorChain.State.PAUSED
|| phaseChain.getState() == InterceptorChain.State.SUSPENDED) {
phaseChain.resume();
return;
}
}
}
Message message = getBinding().createMessage(m);
Exchange exchange = message.getExchange();
if (exchange == null) {
exchange = new ExchangeImpl();
m.setExchange(exchange);
}
exchange.setInMessage(message);
setExchangeProperties(exchange, message);
InterceptorProvider dbp = null;
if (endpoint.getService().getDataBinding() instanceof InterceptorProvider) {
dbp = (InterceptorProvider)endpoint.getService().getDataBinding();
}
// setup chain
if (dbp == null) {
phaseChain = chainCache.get(bus.getExtension(PhaseManager.class).getInPhases(),
bus.getInInterceptors(),
endpoint.getService().getInInterceptors(),
endpoint.getInInterceptors(),
getBinding().getInInterceptors());
} else {
phaseChain = chainCache.get(bus.getExtension(PhaseManager.class).getInPhases(),
bus.getInInterceptors(),
endpoint.getService().getInInterceptors(),
endpoint.getInInterceptors(),
getBinding().getInInterceptors(),
dbp.getInInterceptors());
}
// <span style="font-family: Arial, Helvetica, sans-serif;">phaseChain 顺序拦截链 </span><span style="font-family: Arial, Helvetica, sans-serif;"> </span><span style="font-family: Arial, Helvetica, sans-serif;">Chain org.apache.cxf.phase.PhaseInterceptorChain@4072455d. Current flow:</span>
// receive [LoggingInInterceptor]
// unmarshal [JAXRSInInterceptor]
// pre-logical [OneWayProcessorInterceptor]
// pre-invoke [BeanValidationInInterceptor]
// invoke [ServiceInvokerInterceptor]
// post-invoke [OutgoingChainInterceptor]
message.setInterceptorChain(phaseChain);
phaseChain.setFaultObserver(endpoint.getOutFaultObserver());
addToChain(phaseChain, message);
phaseChain.doIntercept(message);//处理链
} finally {
if (origBus != bus) {
BusFactory.setThreadDefaultBus(origBus);
}
if (origLoader != null) {
origLoader.reset();
}
}
}
这个方法设置了交换器的属性
protected void setExchangeProperties(Exchange exchange, Message m) {
exchange.put(Endpoint.class, endpoint);
exchange.put(Binding.class, getBinding());
exchange.put(Bus.class, bus);
if (exchange.getDestination() == null) {
exchange.setDestination(m.getDestination());
}
if (endpoint != null && endpoint.getService() != null) {
exchange.put(Service.class, endpoint.getService());
EndpointInfo endpointInfo = endpoint.getEndpointInfo();
if (endpointInfo.getService() != null) {
QName serviceQName = endpointInfo.getService().getName();
exchange.put(Message.WSDL_SERVICE, serviceQName);
QName interfaceQName = endpointInfo.getService().getInterface().getName();
exchange.put(Message.WSDL_INTERFACE, interfaceQName);
QName portQName = endpointInfo.getName();
exchange.put(Message.WSDL_PORT, portQName);
URI wsdlDescription = endpointInfo.getProperty("URI", URI.class);
if (wsdlDescription == null && !endpointInfo.hasProperty("URI")) {
String address = endpointInfo.getAddress();
try {
wsdlDescription = new URI(address + "?wsdl");
} catch (URISyntaxException e) {
// do nothing
}
endpointInfo.setProperty("URI", wsdlDescription);
}
exchange.put(Message.WSDL_DESCRIPTION, wsdlDescription);
}
} else {
exchange.put(Service.class, null);
}
}
phaseChain.doIntercept(message);调用了org.apache.cxf.phase.PhaseInterceptorChain.doIntercept方法
/**
* Intercept a message, invoking each phase's handlers in turn.
*
* @param message the message
* @throws Exception
*/
@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);
}
//System.out.println("-----------" + currentInterceptor);
currentInterceptor.handleMessage(message);
if (state == State.SUSPENDED) {
// throw the exception to make sure thread exit without interrupt
throw new SuspendedInvocationException();
}
} catch (SuspendedInvocationException ex) {
// we need to resume from the same interceptor the exception got originated from
if (iterator.hasPrevious()) {
iterator.previous();
}
pause();
throw ex;
} catch (RuntimeException ex) {
if (!faultOccurred) {
faultOccurred = true;
StringBuilder description = new StringBuilder();
if (message.getExchange() != null) {
Exchange exchange = message.getExchange();
Service service = exchange.get(Service.class);
if (service != null) {
description.append('\'');
description.append(service.getName());
OperationInfo opInfo = exchange.get(OperationInfo.class);
if (opInfo != null) {
description.append("#").append(opInfo.getName());
}
description.append("\' ");
}
}
message.setContent(Exception.class, ex);
unwind(message);
Exception ex2 = message.getContent(Exception.class);
if (ex2 == null) {
ex2 = ex;
}
FaultListener flogger = (FaultListener)
message.getContextualProperty(FaultListener.class.getName());
boolean useDefaultLogging = true;
if (flogger != null) {
useDefaultLogging = flogger.faultOccurred(ex2, description.toString(), message);
}
if (useDefaultLogging) {
doDefaultLogging(message, ex2, description);
}
boolean isOneWay = false;
if (message.getExchange() != null) {
if (message.getContent(Exception.class) != null) {
message.getExchange().put(Exception.class, ex2);
}
isOneWay = message.getExchange().isOneWay()
&& !MessageUtils.isTrue(message.getContextualProperty(Message.ROBUST_ONEWAY));
}
if (faultObserver != null && !isOneWay) {
// CXF-5629. when exchange is one way and robust, it becomes req-resp in order to
// send the fault
message.getExchange().setOneWay(false);
faultObserver.onMessage(message);
}
}
state = State.ABORTED;
}
}
if (state == State.EXECUTING) {
state = State.COMPLETE;
}
return state == State.COMPLETE;
} finally {
CURRENT_MESSAGE.set(oldMessage);
}
}
NOTE: 消息解析帮助类 org.apache.cxf.jaxrs.utils.JAXRSUtils, org.apache.cxf.jaxrs.utils.FormUtils