Jfinal控制器源碼解讀

本文對Jfinal的控制器源碼作如下分析。html

PS:控制器是全部請求跳轉的基礎,本文就Jfinal控制器的繼承關係及初始化的方法作出解釋說明。web

囉嗦下:全部的請求和響應都是都是經過web容器封裝,咱們主要跟蹤JFinal的Controller是如何被初始化、如何處理業務邏輯,後續須要研究容器的加載機制,進而晉升到高級甚至技術專家的程度。數組

源碼分析總結:慢慢隨着源碼的深刻,漸漸的對源碼不會那那麼的反感,剛開始讀必定要靜下心來,一步一腳印,別人用好幾年甚至幾十年寫的代碼,對於一個初中級開發來講,很難理解的其設計的核心,咱們只有一邊又一邊的反覆讀取,才能理解做者設計的思想,無形之中提升本身的代碼水平和代碼架構;總之,天道酬勤、鍥而不捨。架構

  • JFinal中Controller的繼承關係
    JFinal中自定義的控制器經過繼承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

  • 自定義Controller的寫法
    如上所示,自定義的Controller經過繼承抽象類Controller便可,實例代碼以下
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的邏輯代碼便可

編碼

  • Web過濾器
    經過從新Servlet.Filter的過濾方法,獲取請求的Request和Response和相關參數,交由用戶自定義的控制去實現相關的邏輯。
    過濾器的代碼以下
    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

相關文章
相關標籤/搜索