Handler 处理流程
doFilter - Handler 链中每个 handler.handle(...)
容器初始化时访问 web.xml 配置的 JFinalFilter.doFilter。沿着 Handler 链,每个 handler 调用 handle 方法进行处理,然后交给下一个 handler。
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException { // 获取 request、response,设置编码 HttpServletRequest request = (HttpServletRequest)req; HttpServletResponse response = (HttpServletResponse)res; request.setCharacterEncoding(encoding); // 初始化的时候可以看出 contextPathLength 为 0 或者为 项目名称的长度 // 比如 target = webapp/xx/yy/zz,则截取后的 target = /xx/yy/zz String target = request.getRequestURI(); if (contextPathLength != 0) target = target.substring(contextPathLength); // 在调用了 ActionHandler的 handle 方法之后,isHandled[0] 才会被置为 true。 boolean[] isHandled = {false}; try { // 重头戏,handler 链首到链尾 ActionHandler 依次进行处理 handler.handle(target, request, response, isHandled); } catch (Exception e) { if (log.isErrorEnabled()) { String qs = request.getQueryString(); log.error(qs == null ? target : target + "?" + qs, e); } } // 若最后 isHandled[0] = false,会执行下一个 Filter。 if (isHandled[0] == false) chain.doFilter(request, response); }
JFinal 初始化过程中可以 add JFinal 库中的Handler 或自定义的 Handler。
例如:ContextPathHandler,JFinal 自身扩展的 Handler。
访问项目时就会走过 handler 方法设置 contextPath。这样在前端就可以通过 ${CONTEXT_PATH} 得到项目根路径。
public class ContextPathHandler extends Handler { private String contextPathName; public ContextPathHandler() { contextPathName = "CONTEXT_PATH"; } public ContextPathHandler(String contextPathName) { if (StrKit.isBlank(contextPathName)) throw new IllegalArgumentException("contextPathName can not be blank."); this.contextPathName = contextPathName; } public void handle(String target, HttpServletRequest request, HttpServletResponse response, boolean[] isHandled) { request.setAttribute(contextPathName, request.getContextPath()); System.out.println("哈哈哈"); next.handle(target, request, response, isHandled); } }
FakeStaticHandler,也是 JFinal 自身扩展的 Handler。new FakeStaticHandler 时可定义后缀,访问路径 target 必须是以这个后缀结尾才可以进行下去。
public class FakeStaticHandler extends Handler { private String viewPostfix; public FakeStaticHandler() { viewPostfix = ".html"; } public FakeStaticHandler(String viewPostfix) { if (StrKit.isBlank(viewPostfix)) throw new IllegalArgumentException("viewPostfix can not be blank."); this.viewPostfix = viewPostfix; } public void handle(String target, HttpServletRequest request, HttpServletResponse response, boolean[] isHandled) { if ("/".equals(target)) { next.handle(target, request, response, isHandled); return; } if (target.indexOf('.') == -1) { HandlerKit.renderError404(request, response, isHandled); return ; } int index = target.lastIndexOf(viewPostfix); if (index != -1) target = target.substring(0, index); next.handle(target, request, response, isHandled); } }
到达 Handler 链尾 ActionHandler 处理
访问交给 Handler 链尾 ActionHandler,调用 handle 方法进行处理:根据访问路径 target 得到 Action,由 Action 得到 Controller。接着 new Invocation(action, controller).invoke() 进入责任链,反射机制调用 Controller 的方法处理(此方法可根据 Action 得到)。
public final void handle(String target, HttpServletRequest request, HttpServletResponse response, boolean[] isHandled) { if (target.indexOf('.') != -1) { return ; } isHandled[0] = true; String[] urlPara = {null}; // actionMapping 根据 target 得到对应的 action Action action = actionMapping.getAction(target, urlPara); 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 { // 由 action 得到对应的 Controller Controller controller = action.getControllerClass().newInstance(); // Controller 初始化 controller.init(request, response, urlPara[0]); if (devMode) { if (ActionReporter.isReportAfterInvocation(request)) { new Invocation(action, controller).invoke(); ActionReporter.report(controller, action); } else { ActionReporter.report(controller, action); new Invocation(action, controller).invoke(); } } else { // 调用 Controller 相应的处理方法 new Invocation(action, controller).invoke(); } // 获取对应的 Render,如果是一个 ActionRender,就再交给 handler 处理;如果 Render == null会按照默认Render处理; Render render = controller.getRender(); 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 ; } if (render == null) render = renderFactory.getDefaultRender(action.getViewPath() + action.getMethodName()); // 调用 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, action.getViewPath()).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, action.getViewPath()).render(); } }
View Cod
下面看看拦截器链如何处理
public class Invocation { private Action action; private static final Object[] NULL_ARGS = new Object[0]; // Prevent new Object[0] by jvm for paras of action invocation. boolean useInjectTarget; private Object target; private Method method; private Object[] args; private MethodProxy methodProxy; private Interceptor[] inters; private Object returnValue = null; private int index = 0; // InvocationWrapper need this constructor protected Invocation() { this.action = null; } public Invocation(Action action, Controller controller) { this.action = action; this.inters = action.getInterceptors(); this.target = controller; this.args = NULL_ARGS; } public Invocation(Object target, Method method, Object[] args, MethodProxy methodProxy, Interceptor[] inters) { this.action = null; this.target = target; this.method = method; this.args = args; this.methodProxy = methodProxy; this.inters = inters; } public void invoke() { if (index < inters.length) { inters[index++].intercept(this); } else if (index++ == inters.length) { // index++ ensure invoke action only one time try { // Invoke the action if (action != null) { returnValue = action.getMethod().invoke(target, args); } // Invoke the method else { // if (!Modifier.isAbstract(method.getModifiers())) // returnValue = methodProxy.invokeSuper(target, args); if (useInjectTarget) returnValue = methodProxy.invoke(target, args); else returnValue = methodProxy.invokeSuper(target, args); } } catch (InvocationTargetException e) { Throwable t = e.getTargetException(); throw t instanceof RuntimeException ? (RuntimeException)t : new RuntimeException(e); } catch (RuntimeException e) { throw e; } catch (Throwable t) { throw new RuntimeException(t); } } } // ... many sets and gets }
public class AInterceptor implements Interceptor { public void intercept(Invocation inv) { System.out.println("Before invoking "); inv.invoke(); System.out.println("After invoking "); } } public class BInterceptor implements Interceptor { public void intercept(Invocation inv) { System.out.println("Before invoking "); inv.invoke(); System.out.println("After invoking "); } } public class BInterceptor implements Interceptor { public void intercept(Invocation inv) { System.out.println("Before invoking "); inv.invoke(); System.out.println("After invoking "); } }
new Invocation(action, controller).invoke(),由 action 得到拦截器数组,如果数组为空,就可以利用反射机制进入 Controller 的方法进行处理的了;
如果拦截器数组不为空,就会遇到拦截器1 的拦截 - inters[0].intercept(this),数组下标 +1,拦截器1 继续调用这个 Invocation 实例的 invoke() 方法,并且会在前后加上一些目的性操作;
若下标未越界,接着会遇到拦截器2 的拦截 - inters[1].intercept(this),数组下标 +1,拦截器2 继续调用这个 Invocation 实例的 invoke() 方法,并且会在前面加上一些目的性操作;
如此继续直到下标越界,接着反射机制进入 Controller 的方法进行处理。
render(...)
接着上面 ActionHandler.handle 继续,在 Controller 方法处理的最后,往往要调用 render 方法来实例化 render 变量。例如,我自定义的一个 IndexController,在最后一步调用 render("next.html")。
public class IndexController extends Controller { public void index() { // ... render("next.html"); } }
用到父类 Controller 的 render 方法,通过 renderFactory.getRender 得到 render 实例。
public abstract class Controller { // ... // render below --- private static final RenderFactory renderFactory = RenderFactory.me(); /** * Hold Render object when invoke renderXxx(...) */ private Render render; public Render getRender() { return render; } /** * Render with any Render which extends Render */ public void render(Render render) { this.render = render; } /** * Render with view use default type Render configured in JFinalConfig */ public void render(String view) { render = renderFactory.getRender(view); } /** * Render with jsp view */ public void renderJsp(String view) { render = renderFactory.getJspRender(view); } /** * Render with freemarker view */ public void renderFreeMarker(String view) { render = renderFactory.getFreeMarkerRender(view); } /** * Render with velocity view */ public void renderVelocity(String view) { render = renderFactory.getVelocityRender(view); } /** * Render with json * <p> * Example:<br> * renderJson("message", "Save successful");<br> * renderJson("users", users);<br> */ public void renderJson(String key, Object value) { render = renderFactory.getJsonRender(key, value); } /** * Render with json */ public void renderJson() { render = renderFactory.getJsonRender(); } /** * Render with attributes set by setAttr(...) before. * <p> * Example: renderJson(new String[]{"blogList", "user"}); */ public void renderJson(String[] attrs) { render = renderFactory.getJsonRender(attrs); } /** * Render with json text. * <p> * Example: renderJson("{\"message\":\"Please input password!\"}"); */ public void renderJson(String jsonText) { render = renderFactory.getJsonRender(jsonText); } /** * Render json with object. * <p> * Example: renderJson(new User().set("name", "JFinal").set("age", 18)); */ public void renderJson(Object object) { render = object instanceof JsonRender ? (JsonRender)object : renderFactory.getJsonRender(object); } /** * Render with text. The contentType is: "text/plain". */ public void renderText(String text) { render = renderFactory.getTextRender(text); } /** * Render with text and content type. * <p> * Example: renderText("<user id='5888'>James</user>", "application/xml"); */ public void renderText(String text, String contentType) { render = renderFactory.getTextRender(text, contentType); } /** * Render with text and ContentType. * <p> * Example: renderText("<html>Hello James</html>", ContentType.HTML); */ public void renderText(String text, ContentType contentType) { render = renderFactory.getTextRender(text, contentType); } /** * Forward to an action */ public void forwardAction(String actionUrl) { render = new ActionRender(actionUrl); } /** * Render with file */ public void renderFile(String fileName) { render = renderFactory.getFileRender(fileName); } /** * Render with file */ public void renderFile(File file) { render = renderFactory.getFileRender(file); } /** * Redirect to url */ public void redirect(String url) { render = renderFactory.getRedirectRender(url); } /** * Redirect to url */ public void redirect(String url, boolean withQueryString) { render = renderFactory.getRedirectRender(url, withQueryString); } /** * Render with view and status use default type Render configured in JFinalConfig */ public void render(String view, int status) { render = renderFactory.getRender(view); response.setStatus(status); } /** * Render with url and 301 status */ public void redirect301(String url) { render = renderFactory.getRedirect301Render(url); } /** * Render with url and 301 status */ public void redirect301(String url, boolean withQueryString) { render = renderFactory.getRedirect301Render(url, withQueryString); } /** * Render with view and errorCode status */ public void renderError(int errorCode, String view) { throw new ActionException(errorCode, renderFactory.getErrorRender(errorCode, view)); } /** * Render with render and errorCode status */ public void renderError(int errorCode, Render render) { throw new ActionException(errorCode, render); } /** * Render with view and errorCode status configured in JFinalConfig */ public void renderError(int errorCode) { throw new ActionException(errorCode, renderFactory.getErrorRender(errorCode)); } /** * Render nothing, no response to browser */ public void renderNull() { render = renderFactory.getNullRender(); } /** * Render with javascript text. The contentType is: "text/javascript". */ public void renderJavascript(String javascriptText) { render = renderFactory.getJavascriptRender(javascriptText); } /** * Render with html text. The contentType is: "text/html". */ public void renderHtml(String htmlText) { render = renderFactory.getHtmlRender(htmlText); } /** * Render with xml view using freemarker. */ public void renderXml(String view) { render = renderFactory.getXmlRender(view); } public void renderCaptcha() { render = renderFactory.getCaptchaRender(); } }
根据 Controller 可以看出还能使用 renderJosn、renderText 等多种方法,这里一笔带过。
就先写到这里吧~~