Webx的执行流程

Webx的总体流程

WebxFrameworkFilter->AbstractWebxRootController->WebxRootControllerImpl->WebxControllerImpl->Pipeline

具体执行流程

Webx的主要流程,由于Webx是基于Servlet的Filter开发的

Request Contexts服务该服务负责访问和修改request和response,但不负责改变应用执行的流程。

Pipeline服务提供应用执行的流程,但不关心request和response。

public final void service(HttpServletRequest request, HttpServletResponse response, FilterChain chain)
        throws Exception {
    RequestContext requestContext = null;

    try {
        requestContext = assertNotNull(getRequestContext(request, response), "could not get requestContext");

        // 如果请求已经结束,则不执行进一步的处理。例如,当requestContext已经被重定向了,则立即结束请求的处理。
        if (isRequestFinished(requestContext)) {
            return;
        }

        // 请求未结束,则继续处理...
        request = requestContext.getRequest();
        response = requestContext.getResponse();

        // 如果是一个内部请求,则执行内部请求
        if (handleInternalRequest(request, response)) {
            return;
        }

        // 如果不是内部的请求,并且没有被passthru,则执行handleRequest
        if (isRequestPassedThru(request) || !handleRequest(requestContext)) {
            // 如果请求被passthru,或者handleRequest返回false(即pipeline放弃请求),
            // 则调用filter chain,将控制交还给servlet engine。
            giveUpControl(requestContext, chain);
        }
    } catch (Throwable e) {
        handleException(requestContext, request, response, e);
    } finally {
        commitRequest(requestContext);
    }
}

其中getRequestContext对应RequestContext的执行过程,handleRequest函数对应执行pipeline流程,而commitRequest表示RequestContext的提交过程。

首先看一下RequestContext

public interface RequestContext {
/**
 * 取得被包装的context。
 *
 * @return 被包装的<code>RequestContext</code>对象
 */
RequestContext getWrappedRequestContext();

/**
 * 取得servletContext对象。
 *
 * @return <code>ServletContext</code>对象
 */
ServletContext getServletContext();

/**
 * 取得request对象。
 *
 * @return <code>HttpServletRequest</code>对象
 */
HttpServletRequest getRequest();

/**
 * 取得response对象。
 *
 * @return <code>HttpServletResponse</code>对象
 */
HttpServletResponse getResponse();

/** 开始一个请求。 */
void prepare();

/**
 * 结束一个请求。
 *
 * @throws RequestContextException 如果失败
 */
void commit() throws RequestContextException;
}

可以看到执行需要重写prepare方法,提交需要重写commit方法。

RequestContext执行过程

会根据顺序的执行所有的RequestContext(已经init方法中进行了初始化)

public RequestContext getRequestContext(ServletContext servletContext, HttpServletRequest request,
                                        HttpServletResponse response) {
    assertInitialized();

    // 异步请求(dispatcherType == ASYNC)开始时,如果已经存在request context,则直接取得并返回之。
    boolean asyncDispatcher = request_isDispatcherType(request, DISPATCHER_TYPE_ASYNC);
    RequestContext requestContext = null;

    if (asyncDispatcher) {
        requestContext = RequestContextUtil.getRequestContext(request);
    }

    if (requestContext == null) {
        SimpleRequestContext innerReuestContext = new SimpleRequestContext(servletContext, request, response, this);

        requestContext = innerReuestContext;

        // 将requestContext放入request中,以便今后只需要用request就可以取得requestContext。
        // 及早设置setRequestContext,以便随后的prepareRequestContext就能使用。
        RequestContextUtil.setRequestContext(requestContext);

        for (RequestContextFactory<?> factory : factories) {
            requestContext = factory.getRequestContextWrapper(requestContext);

            // 调用<code>requestContext.prepare()</code>方法
            prepareRequestContext(requestContext);

            // 将requestContext放入request中,以便今后只需要用request就可以取得requestContext。
            RequestContextUtil.setRequestContext(requestContext);
        }

        innerReuestContext.setTopRequestContext(requestContext);

        getLogger().debug("Created a new request context: {}", requestContext);
    }

    // 无论是否是从async恢复,都需要重新设置thread的上下文环境。
    bind(requestContext.getRequest());

    return requestContext;
}

RequestContext 提交过程

会一层层的拿到内部的被包装的RequestContext并执行commit方法

扫描二维码关注公众号,回复: 2564367 查看本文章
private void doCommit(RequestContext requestContext) {
    CommitMonitor monitor = getCommitMonitor(requestContext);

    synchronized (monitor) {
        if (!monitor.isCommitted()) {
            boolean doCommitHeaders = !monitor.isHeadersCommitted();
            monitor.setCommitted(true);

            HttpServletRequest request = requestContext.getRequest();

            for (RequestContext rc = requestContext; rc != null; rc = rc.getWrappedRequestContext()) {
                if (getLogger().isTraceEnabled()) {
                    getLogger().trace("Committing request context: {}", rc.getClass().getSimpleName());
                }

                if (rc instanceof TwoPhaseCommitRequestContext && doCommitHeaders) {
                    ((TwoPhaseCommitRequestContext) rc).commitHeaders();
                }

                rc.commit();
            }

            // 将request和requestContext断开
            RequestContextUtil.removeRequestContext(request);

            getLogger().debug("Committed request: {}", request);
        }
    }
}

进入Pipeline流程

首先在执行Pipeline之前需要路由app

路由app

首先根据访问的url来路由到对应的子app

protected boolean handleRequest(RequestContext requestContext) throws Exception {
    HttpServletRequest request = requestContext.getRequest();

    // Servlet mapping有两种匹配方式:前缀匹配和后缀匹配。
    // 对于前缀匹配,例如:/servlet/aaa/bbb,servlet path为/servlet,path info为/aaa/bbb
    // 对于前缀匹配,当mapping pattern为/*时,/aaa/bbb,servlet path为"",path info为/aaa/bbb
    // 对于后缀匹配,例如:/aaa/bbb.html,servlet path为/aaa/bbb.html,path info为null
    //
    // 对于前缀匹配,取其pathInfo;对于后缀匹配,取其servletPath。
    String path = ServletUtil.getResourcePath(request);

    // 再根据path查找component
    WebxComponent component = getComponents().findMatchedComponent(path);
    boolean served = false;

    if (component != null) {
        try {
            WebxUtil.setCurrentComponent(request, component);
            served = component.getWebxController().service(requestContext);
        } finally {
            WebxUtil.setCurrentComponent(request, null);
        }
    }

    return served;
}

我们一般的项目配置应该是这样,根据上面的匹配规则当访问/aaa/bbb,就能够计算出servlet path为"",path info为/aaa/bbb

<filter-mapping>
    <filter-name>webx</filter-name>
    <url-pattern>/*</url-pattern>  
</filter-mapping>

这是就能得到子app的WebComponent了(如何初始化的?看我的另一篇博客:http://blog.csdn.net/wsrspirit/article/details/52179416),之后进入子app的WebController

public boolean service(RequestContext requestContext) throws Exception {
    PipelineInvocationHandle handle = pipeline.newInvocation();

    handle.invoke();

    // 假如pipeline被中断,则视作请求未被处理。filter将转入chain中继续处理请求。
    return !handle.isBroken();
}

执行Pipeline

Pipeline的主要的执行流程

public void invokeNext() {
        assertInitialized();

        if (broken) {
            return;
        }

        try {
            executingIndex++;

            if (executingIndex <= executedIndex) {
                throw new IllegalStateException(descCurrentValve() + " has already been invoked: "
                                                + valves[executingIndex]);
            }

            executedIndex++;

            if (executingIndex < valves.length) {
                Valve valve = valves[executingIndex];

                try {
                    if (log.isTraceEnabled()) {
                        log.trace("Entering {}: {}", descCurrentValve(), valve);
                    }

                    valve.invoke(this);
                } catch (PipelineException e) {
                    throw e;
                } catch (Exception e) {
                    throw new PipelineException("Failed to invoke " + descCurrentValve() + ": " + valve, e);
                } finally {
                    if (log.isTraceEnabled()) {
                        log.trace("...Exited {}: {}", descCurrentValve(), valve);
                    }
                }

                if (executedIndex < valves.length && executedIndex == executingIndex) {
                    if (log.isTraceEnabled()) {
                        log.trace("{} execution was interrupted by {}: {}", new Object[] { descCurrentPipeline(),
                                                                                           descCurrentValve(), valve });
                    }
                }
            } else {
                if (log.isTraceEnabled()) {
                    log.trace("{} reaches its end.", descCurrentPipeline());
                }
            }
        } finally {
            executingIndex--;
        }
    }

我们可以看到借助executingIndex对所有的valve进行计数执行,在执行的最后finally会释放executingIndex。那么pipeline的嵌套执行在哪里体现的呢?

看一下Webx中定义的pipeline.xml

<services:pipeline xmlns="http://www.alibaba.com/schema/services/pipeline/valves">

<!-- 初始化turbine rundata,并在pipelineContext中设置可能会用到的对象(如rundata、utils),以便valve取得。 -->
<prepareForTurbine />

<!-- 设置日志系统的上下文,支持把当前请求的详情打印在日志中。 -->
<setLoggingContext />

<!-- 分析URL,取得target。 -->
<analyzeURL homepage="homepage" />

<!-- 检查csrf token,防止csrf攻击和重复提交。假如request和session中的token不匹配,则出错,或显示expired页面。 -->
<checkCsrfToken />

<loop>
    <choose>
        <when>
            <!-- 执行带模板的screen,默认有layout。 -->
            <pl-conditions:target-extension-condition extension="null, vm, jsp" />
            <performAction />
            <performTemplateScreen />
            <renderTemplate />
        </when>
        <when>
            <!-- 执行不带模板的screen,默认无layout。 -->
            <pl-conditions:target-extension-condition extension="do" />
            <performAction />
            <performScreen />
        </when>
        <otherwise>
            <!-- 将控制交还给servlet engine。 -->
            <exit />
        </otherwise>
    </choose>

    <!-- 假如rundata.setRedirectTarget()被设置,则循环,否则退出循环。 -->
    <breakUnlessTargetRedirected />
</loop>

</services:pipeline>

我们以PrepareForTurbinValve为例:

public void invoke(PipelineContext pipelineContext) throws Exception {
    TurbineRunData rundata = getTurbineRunData(request, true);
    boolean contextSaved = false;

    try {
        pipelineContext.setAttribute("rundata", rundata);

        for (Map.Entry<String, Object> entry : Utils.getUtils().entrySet()) {
            pipelineContext.setAttribute(entry.getKey(), entry.getValue());
        }

        pipelineContext.invokeNext();
    } catch (Throwable e) {
        saveTurbineRunDataContext(rundata);
        contextSaved = true;

        if (e instanceof Exception) {
            throw (Exception) e;
        } else if (e instanceof Error) {
            throw (Error) e;
        }
    } finally {
        cleanupTurbineRunData(request, !contextSaved);
    }

我们看到函数在执行完该valve的最后执行了pipelineContext.invokeNext(),这样就实现了在PipelineImpl开始的嵌套执行,在执行的最后的finally是pipeline执行结束之后的提交过程。

Pipeline逻辑功能

Pipeline对比Servlet功能就是多出了对于执行逻辑加入逻辑,子流程等功能,例如loop等。

<loop>
    <choose>
        <when>
            <!-- 执行带模板的screen,默认有layout。 -->
            <pl-conditions:target-extension-condition extension="null, vm, jsp" />
            <performAction />
            <performTemplateScreen />
            <renderTemplate />
        </when>
        <when>
            <!-- 执行不带模板的screen,默认无layout。 -->
            <pl-conditions:target-extension-condition extension="do" />
            <performAction />
            <performScreen />
        </when>
        <otherwise>
            <!-- 将控制交还给servlet engine。 -->
            <exit />
        </otherwise>
    </choose>

    <!-- 假如runda-ta.setRedirectTarget()被设置,则循环,否则退出循环。 -->
    <breakUnlessTargetRedirected />
</loop>

其实loop,chose都是不同的valve,以执行循环和选择的功能。请自行阅读LoopValveChooseValve代码。

执行的最后

别忘了,走了这么久还没有到我们定义的web controller,也就是逻辑控制,我们以RPC逻辑为例。我们首先定义RPC的执行valve

<when>
    <pl-conditions:target-extension-condition extension="json,jsonp,xml,xhtml"/>
    <pl-valves:valve class="com.alibaba.citrus.extension.rpc.integration.RPCServiceHandlerValve"/>
</when>

代码逻辑:

public void invoke(PipelineContext pipelineContext) throws Exception {

    TurbineRunData rundata = getTurbineRunData(this.request);

    if (!rundata.isRedirected()) { // 如果没有重定向(通常会在权限验证不通过时出现重定向的操作)

        // 处理RPC的请求
        rundata.getRequest().setAttribute("_target", rundata.getTarget());
        this.rpcServiceHandler.handleRequest(rundata.getRequest(), rundata.getResponse());
    }

    pipelineContext.invokeNext();

}

可以看到调用了RPCServiceHandler.handleRequest方法:

public void handleRequest(HttpServletRequest request, HttpServletResponse response) throws RPCException {

    // 错误信息的上下文
    ErrorContext errorContext = new DefaultErrorContext();

    // 构建RequestContext
    RPCRequestContext requestContext = this.createRequestContext(request, response, errorContext);

    ServiceComponent serviceComponent = null;
    Object result = null;

    try {

        // 通过MappingService来查找对应的ServiceComponent
        for (ServiceComponentMapping mapping : this.mappings) {

            serviceComponent = mapping.findServiceComponent(requestContext);
            if (serviceComponent != null) {
                break;
            }
        }

        // 设置requestContext的requestAcceptType
        ((DefaultRPCRequestContext) requestContext).setRequestAcceptType(this.getRequestAcceptType(request));

        if (serviceComponent != null) {
            // 执行RPC方法
            result = serviceComponent.execute(requestContext, errorContext);

            // 设置输出的字符集
            String charset = requestContext.getCharset();
            if (StringUtils.hasText(charset)) {
                response.setCharacterEncoding(charset);
            }

            if (result != null) {
                if (result instanceof MimeResult) { // 处理自定义方式的结果输出

                    this.writeMimeResult(response, (MimeResult) result, requestContext);
                    return;
                }
            }
        } else { // 构建 404的返回结果

            errorContext.addError(ErrorItem.create("rpc_404", "404",
                    String.format("resource '%s' is not found !", request.getRequestURI())));
            ((DefaultErrorContext) errorContext).setHasSystemError(true);
        }
    } catch (Exception e) {

        this.handleException(requestContext, errorContext, e);
    }

    // 处理响应结果
    this.processResult(request, errorContext, requestContext, result);

}

可以看到先获得serviceComponent然后调用serviceComponent.execute

public Object execute(RPCRequestContext requestContext, ErrorContext errorContext) throws RPCException {
    // 设置RPC的RequestContext
    if (requestContext instanceof DefaultRPCRequestContext) {

        DefaultRPCRequestContext context = (DefaultRPCRequestContext) requestContext;

        this.setupRequestContext(context);
    }

    this.checkPermission(requestContext);

    List<RPCInvokeInterceptor> interceptors = requestContext.getMethodInvokeInterceptors();
    int currentInterceptorIndex = 0;

    Object result = null;

    try {

        // 参数绑定
        Object[] args = this.databindService.bindMethod(method, requestContext.getRequest(),
                requestContext.getResponse(), requestContext, errorContext);

        // 参数验证
        if (args != null && args.length > 0) {

            ValidateResult validateResult = this.validateService.validate(method, args);
            if (validateResult.hasError()) { // 验证失败,往ErrorContext中写入验证错误信息
                for (Map.Entry<String, String> entry : validateResult.getFieldErrors().entrySet()) {
                    errorContext.addError(ErrorItem.create(entry.getKey(), null, entry.getValue()));
                }
            }
        }

        if (errorContext.hasError()) { // 不执行业务方法,直接返回null
            return null;
        } else {

            boolean breaked = false;

            if (interceptors != null) {

                for (RPCInvokeInterceptor interceptor : interceptors) {

                    if (interceptor.beforeInvoke(this.serviceInstance, args, requestContext, errorContext)) {
                        currentInterceptorIndex++;
                    } else {
                        breaked = true;
                        break;
                    }

                }
            }

            // 如果没有被interceptor所中断,继续执行业务代码
            if (!breaked) {
                //设置当前线程的errorContext
                ErrorContextUtil.setCurrentErrorContext(errorContext);
                //执行业务代码
                result = method.invoke(this.serviceInstance, args);
            }
        }

    } catch (Exception e) {
        throw new RPCException(String.format("invoke rpc rpcMethod '%s' failed !", requestContext.getMethodName()),
                e);
    } finally {
        //清除当前线程的errorContext
        ErrorContextUtil.setCurrentErrorContext(null);

        if (interceptors != null) {
            for (int i = 0; i < currentInterceptorIndex; i++) {

                interceptors.get(i).afterInvoke(this.serviceInstance, result, requestContext, errorContext);

            }
        }
    }

    return result;
}

result = method.invoke(this.serviceInstance, args)这一行代码就进入了我们设定的rpc方法内部,例如:

@ResourceMapping(value = "/getStandardTemplate",charset = "UTF-8")
@NoneResultDecrator
@Security(xssType = XssType.none)
public void ****(){
}

在method.invoke前后会有RPCInvokeInterceptor在前后执行,例如CSRF验证功能等。

method就是根据@ResourceMapping注解获得的。至此我们就终于执行到了我们的RPC函数。

结束

结束的流程在之前就已经介绍了,会从Pipeline和RequestContext由内而外的执行。

猜你喜欢

转载自blog.csdn.net/WSRspirit/article/details/52245049
今日推荐