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方法
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,以执行循环和选择的功能。请自行阅读LoopValve
和ChooseValve
代码。
执行的最后
别忘了,走了这么久还没有到我们定义的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由内而外的执行。