运行时入口
由于filter拦截了所有的请求url,所以所有访问jfinal webapp 的请求都会被JfinalFilter的doFilter处理。故运行时入口为JFinalFilter的doFilter方法。
public void doFilter(ServletRequest req, ServletResponse res,
FilterChain chain) throws IOException, ServletException {
// 获取request,response和为request设置编码
HttpServletRequest request = (HttpServletRequest) req;
HttpServletResponse response = (HttpServletResponse) res;
request.setCharacterEncoding(encoding);
// 获取requestUrl,url:port/webappname/xx,/webappname/xx即requestUrl
String target = request.getRequestURI();
// contextPath为/webappname,而action中的actionkey是不包含contextpath,所以此处需将requestUrl中的contextPath这一段截掉
// 此时对于初始化时contextPathLength的作用就明了了,截取之后的只为/xx
if (contextPathLength != 0)
target = target.substring(contextPathLength);
// 声明hander处理的标识量
boolean[] isHandled = { false };
try {
// 很明显,此句才是这段代码中的重点戏,延后分析,实际的请求处理调用。
handler.handle(target, request, response, isHandled);
} catch (Exception e) {
// 异常日志记录
if (log.isErrorEnabled()) {
String qs = request.getQueryString();
log.error(qs == null ? target : target + "?" + qs, e);
}
}
// 此句涉及到handle链的结构和handler的具体处理,初始化阶段时,handler链的末端是actionHandler,而只有调用acitonHandler的handle方法之后,isHandled[0]才会被赋值为true
// handler.handle方法内部会递归调用下一个handler的handle方法,而有些处理可能调用不到最末端的actionHandle的handle方法,此时isHandled[0]为false
// 虽然demo中没有配置额外的handler,但是如ServerNameRedirect301Handler,UrlSkipHandler都是在满足一定条件才会继续递归往下调用的
if (isHandled[0] == false)
// jfinal程序中可以配置多个filter,由此句可见端倪
chain.doFilter(request, response);
}
handler.handle(target, request, response, isHandled)的探究
由于暂时没有其他的handle配置,所以暂时只分析ActionHandler的handle方法实现
public final void handle(String target, HttpServletRequest request,
HttpServletResponse response, boolean[] isHandled) {
// target含有‘.’ 则不进行处理
if (target.indexOf('.') != -1) {
return;
}
// 使标识量为true,说明handler链上的所有handler都已经处理过了
isHandled[0] = true;
String[] urlPara = { null };
// 获取target对应的action,即actionKey对应的action
Action action = actionMapping.getAction(target, urlPara);
// 没有获取到对应的action,记录异常,并渲染404页面,处理完成,响应返回客户端
if (action == null) {
if (log.isWarnEnabled()) {
String qs = request.getQueryString();
log.warn("404 Action Not Found: "
+ (qs == null ? target : target + "?" + qs));
}
renderFactory.getErrorRender(404).setContext(request, response)
.render();
return;
}
try {
// 为每个请求都新建一个controller实例,感觉此处应有改善空间,对于低并发来说,没什么压力,高并发的话,开销不应该考虑么?
// 以空间换效率应该是可行的,由actionHandle维护一个Controller缓存池
Controller controller = action.getControllerClass().newInstance();
// 初始化 简单赋值
controller.init(request, response, urlPara[0]);
// 根据devMode决定是否在控制台打印处理信息
if (devMode) {
boolean isMultipartRequest = ActionReporter
.reportCommonRequest(controller, action);
new ActionInvocation(action, controller).invoke();
if (isMultipartRequest)
ActionReporter.reportMultipartRequest(controller, action);
} else {
// 1.核心代码,延后分析
new ActionInvocation(action, controller).invoke();
}
// 获取render,render的具体值是由上一步得到的,因为每个action最终都需要渲染,最后一步执行的方法确定了render的值,对于某些不涉及到渲染操作的action,比如对数据库进行存储的,会返回null。
Render render = controller.getRender();
// 暂时没碰到ActionRender,碰到了再回头分析
if (render instanceof ActionRender) {
String actionUrl = ((ActionRender) render).getActionUrl();
if (target.equals(actionUrl))
throw new RuntimeException(
"The forward action url is the same as before.");
else
handle(actionUrl, request, response, isHandled);
return;
}
// 对于某些不涉及到渲染操作的action,比如对数据库进行存储的,会返回null,此时将render赋值为jfinal的缺省设置
if (render == null)
render = renderFactory.getDefaultRender(action.getViewPath()
+ action.getMethodName());
// 为render设置上下文环境并渲染
// 至此actionhandler的整个处理流程走完
// jspRender的render方法延后分析
render.setContext(request, response, action.getViewPath()).render();
}
// 处理异常,记录日志,渲染错误页
catch (RenderException e) {
if (log.isErrorEnabled()) {
String qs = request.getQueryString();
log.error(qs == null ? target : target + "?" + qs, e);
}
} catch (ActionException e) {
int errorCode = e.getErrorCode();
if (errorCode == 404 && log.isWarnEnabled()) {
String qs = request.getQueryString();
log.warn("404 Not Found: "
+ (qs == null ? target : target + "?" + qs));
} else if (errorCode == 401 && log.isWarnEnabled()) {
String qs = request.getQueryString();
log.warn("401 Unauthorized: "
+ (qs == null ? target : target + "?" + qs));
} else if (errorCode == 403 && log.isWarnEnabled()) {
String qs = request.getQueryString();
log.warn("403 Forbidden: "
+ (qs == null ? target : target + "?" + qs));
} else if (log.isErrorEnabled()) {
String qs = request.getQueryString();
log.error(qs == null ? target : target + "?" + qs, e);
}
e.getErrorRender().setContext(request, response).render();
} catch (Throwable t) {
if (log.isErrorEnabled()) {
String qs = request.getQueryString();
log.error(qs == null ? target : target + "?" + qs, t);
}
renderFactory.getErrorRender(500).setContext(request, response)
.render();
}
}
new ActionInvocation(action, controller).invoke();的探究
1.新对象
ActionInvocation(Action action, Controller controller) {
this.controller = controller;
this.inters = action.getInterceptors();
this.action = action;
}
2.invoke()方法
public void invoke() {
// inters为此action的拦截器栈,依次执行此action的拦截器,由初始化过程可知,拦截器顺序依次为 全局,controller,method
// 此方法是个递归方法 拦截器01_before->拦截器02_before->xx_before->action.getMethod.invoke(controller,NULL_ARGS)->xx_after->拦截器02_after->拦截器01_after
if (index < inters.length)
inters[index++].intercept(this);
else if (index++ == inters.length) // index++ ensure invoke action only one time
// 拦截器执行完,则反射执行action对应的method方法
try {
action.getMethod().invoke(controller, NULL_ARGS);
}
// 异常处理
catch (InvocationTargetException e) {
Throwable cause = e.getTargetException();
if (cause instanceof RuntimeException)
throw (RuntimeException)cause;
throw new RuntimeException(e);
}
catch (RuntimeException e) {
throw e;
}
catch (Exception e) {
throw new RuntimeException(e);
}
}
render.setContext(request, response, action.getViewPath()).render()的探究
只分析jspRender的render方法
public void render() {
// 在 jsp 页面使用如下指令则无需再指字符集, 否则是重复指定了,与页面指定的不一致时还会出乱码
// <%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
// response.setContentType(contentType);
// response.setCharacterEncoding(encoding);
try {
// 暂时不清楚activeRecord是何许神圣,暂不分析
if (isSupportActiveRecord)
supportActiveRecord(request);
// jspRender利用基础的分发器转发请求,至此一个请求到相应的流程走完,即V->C->V
request.getRequestDispatcher(view).forward(request, response);
} catch (Exception e) {
throw new RenderException(e);
}
}
运行时小结
运行时从filter接管->处理器链处理->actionHandle处理->具体action处理->render 结束,此中包含了actionRender,由于暂时没有碰到actionRender,暂不分析