CXF restful 请求过程分析

最近(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

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值