本文對Jfinal的控制器源碼作如下分析。html
PS:控制器是全部請求跳轉的基礎,本文就Jfinal控制器的繼承關係及初始化的方法作出解釋說明。web
囉嗦下:全部的請求和響應都是都是經過web容器封裝,咱們主要跟蹤JFinal的Controller是如何被初始化、如何處理業務邏輯,後續須要研究容器的加載機制,進而晉升到高級甚至技術專家的程度。數組
源碼分析總結:慢慢隨着源碼的深刻,漸漸的對源碼不會那那麼的反感,剛開始讀必定要靜下心來,一步一腳印,別人用好幾年甚至幾十年寫的代碼,對於一個初中級開發來講,很難理解的其設計的核心,咱們只有一邊又一邊的反覆讀取,才能理解做者設計的思想,無形之中提升本身的代碼水平和代碼架構;總之,天道酬勤、鍥而不捨。架構
public abstract class Controller { //Web請求 private HttpServletRequest request;
//響應 private HttpServletResponse response; //Url參數 private String urlPara;
//Url參數數組 private String[] urlParaArray; }
PS:後續咱們重點跟蹤request、response、urlPara是如何被初始化,咱們猜測:這些變量都是Jfinal框架自己從web容器中獲取。
留個疑問:自定義的Controller是 單例嗎?app
public class BlogController extends Controller { static BlogService service = new BlogService(); public void index() { setAttr("blogPage", service.paginate(getParaToInt(0, 1), 10)); render("blog.html"); } }
將Controller綁定給容器
經過JFinalConfig實現的ConfigRoute方式將自定義的Controller綁定給容器,供請求時候調用,以下框架
/** * 配置路由 */ public void configRoute(Routes me) { me.add("/", IndexController.class, "/index"); // 第三個參數爲該Controller的視圖存放路徑 me.add("/blog", BlogController.class); // 第三個參數省略時默認與第一個參數值相同,在此即爲 "/blog" }
PS:爲什麼不弄個註解,讓JFinal自動掃描自定義的控制器呢?按照常規思想,實現註解,省略了配置代碼多好源碼分析
若是不弄註解:我將全部的Controller可以統一管理,可以快速找到我自定義的控制器有哪些,鄙人猜想,這應該是做者不整個Controller上的註解的緣由吧,但我以爲這樣的註解仍是有必要的,畢竟每一個人的使用方法不同,框架自己應該要知足龐大客戶羣體的需求,該註解可自定添加,從新JFinal加載Controller的邏輯代碼便可
編碼
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException { //從過濾器中獲取請求
HttpServletRequest request = (HttpServletRequest)req;
//從過濾器中獲取響應 HttpServletResponse response = (HttpServletResponse)res;
//設置請求的編碼 request.setCharacterEncoding(encoding); //獲取請求的URI String target = request.getRequestURI(); if (contextPathLength != 0) {
//從URI中去除根目錄 target = target.substring(contextPathLength); } boolean[] isHandled = {false}; try {
//處理器這塊須要重點關注,通過分析處理器的實現爲: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); } } if (isHandled[0] == false) { chain.doFilter(request, response); } }
接下里咱們分析ActionHandler中handle方法的邏輯
url
public final void handle(String target, HttpServletRequest request, HttpServletResponse response, boolean[] isHandled) { if (target.indexOf('.') != -1) { return ; } isHandled[0] = true; String[] urlPara = {null};
//基於URI和參數獲取請求的action Action action = actionMapping.getAction(target, urlPara); if (action == null) { if (log.isWarnEnabled()) { String qs = request.getQueryString();
//Action爲null的狀況下,記錄查詢字符串的日誌 log.warn("404 Action Not Found: " + (qs == null ? target : target + "?" + qs)); } renderManager.getRenderFactory().getErrorRender(404).setContext(request, response).render(); return ; } try {
//從Action中獲取當前請求對應的Controller的對象,這個地方每次都是實例化一次,因此JFinal的Controller不是單例 Controller controller = action.getControllerClass().newInstance();
//controller調用初始化方法,賦值請求、響應和url參數 controller.init(request, response, urlPara[0]); if (devMode) { if (ActionReporter.isReportAfterInvocation(request)) { new Invocation(action, controller).invoke(); ActionReporter.report(target, controller, action); } else { ActionReporter.report(target, controller, action); new Invocation(action, controller).invoke(); } } else {
//反射調用,仔細跟 new Invocation(action, controller).invoke(); } Render render = controller.getRender(); if (render instanceof ForwardActionRender) { String actionUrl = ((ForwardActionRender)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 = renderManager.getRenderFactory().getDefaultRender(action.getViewPath() + action.getMethodName()); } 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(); String msg = null; if (errorCode == 404) { msg = "404 Not Found: "; } else if (errorCode == 401) { msg = "401 Unauthorized: "; } else if (errorCode == 403) { msg = "403 Forbidden: "; } if (msg != null) { if (log.isWarnEnabled()) { String qs = request.getQueryString(); log.warn(msg + (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 (Exception e) { if (log.isErrorEnabled()) { String qs = request.getQueryString(); log.error(qs == null ? target : target + "?" + qs, e); } renderManager.getRenderFactory().getErrorRender(500).setContext(request, response, action.getViewPath()).render(); } }
以上代碼經過反射代理的方式調用了Controller中的方法,最終renderManager實現跳轉,Render是Jfinal對應web響應的一個封裝,這種設計思想在實際開發中頗有必要,簡潔代碼,使用方便,下文給出Render實現類spa
PS:Render這塊是對響應進行了封裝,具體更多的東西還須要自行分析和總機。
天道酬勤,努力每一天,也歡迎您的指正,共同進步,請聯繫:xieyang@e6yun.com/cnxieyang@163.com