1. 程式人生 > >JFinal原始碼解析與思想理解

JFinal原始碼解析與思想理解

動機

在做專案的過程中運用到了JFinal,由於是第一次看這樣框架的原始碼,所以很多東西都不知道。想多瞭解一些架構的思想和Web學習的基本知識。本文主要從大致的方面介紹JFinal,對於細節不做深究,而且本文的原始碼只剪輯了真正原始碼的部分。

總體思想

首先要了解一哈基本的知識:

  • ORM:Object Relational Mapper,is the layer that sits between your database and your application. In Object -Oriented Programming, you work with Objects as your main point of reference.也就是說假如在資料庫裡面有一個表,那麼在應用程式裡面就有一個類與這個表相對應,類中的成員變數是資料庫的列名,一個類的例項是資料庫的一行資料。
  • ActiveRecord:是一種模式。然後我們先來理解Record,欄位,也就是資料庫裡面的一行資料。對於ORM來說,我們定義一個Model類作為ORM的基類,然後繼承這個ORM基類。
class User extends Model{
 ....
}
User user = new User();
user.setName("Ferrair");
user.save();

這樣只需要直接的呼叫save()方法,就會在資料庫裡面插入一條資料。而在基類Model裡面,save()方法封裝了SQL語句。
對於Active可以把它理解為持久的意思,也就是說與資料庫是持久連線。
然後可以參考這些文章

什麼是ActiveRecord
What’s the difference between Active Record and Data Mapper?(這裡面還有一種思想叫Data-Mapper)
- POJO:什麼是POJO(Plain Ordinary Java Object),即簡單Java物件,關於POJO可以把它和JavaBean進行比較,POJO比JavaBean限制要多的多,一般在應用程式裡面的與資料庫對映的物件就叫POJO.在JFinal裡面也就是繼承Model的類.

然後看看JFinal作者給出的圖:

JFinal

JFinal由Handler、Interceptor、Controller、Render、Plugin五大部分組成,以Action為參照,Handler處在擴充套件的最外圍,Interceptor處在更貼近Action的周圍,Controller承載Action處在擴充套件的中心,Render處於Action後端,Plugin處於在Action側邊位置。

在上面的圖中首先一個Request過來進入到JFinalFiter,然後運用了責任鏈的模式,將請求一層層的傳遞,直到ActionHandler,在這裡進行路由,路由到具體的Controller,而對於Model則是在Plugin裡面實現的.這一切進行好了之後,就可以使用Render來渲染View了。大致流程如上。

採用了下面的模式:
- DB + ActiveRecord
- Web MVC + ORM
- 責任鏈的設計模式

Jfinal初始化

首先在web.xml定義了JFinalFilter,所以這個框架才可以被執行,在JFinalFilter裡面的init()找到下面這些初始化的過程

temp = Class.forName(configClass).newInstance();
jfinalConfig = (JFinalConfig)temp; // 在web.xml得到具體的配置檔案

jfinal.init(jfinalConfig, filterConfig.getServletContext()) // 真正的初始化過程

然後再去看看jfinal.init()

    boolean init(JFinalConfig jfinalConfig, ServletContext servletContext) {
        this.servletContext = servletContext;
        this.contextPath = servletContext.getContextPath();

        initPathUtil();

        Config.configJFinal(jfinalConfig);  // start plugin and init log factory in this method
        constants = Config.getConstants();

        initActionMapping();
        initHandler();
        initRender();
        initOreillyCos();
        initTokenManager();

        return true;
    }

    private void initTokenManager() {
        ITokenCache tokenCache = constants.getTokenCache();
        if (tokenCache != null)
            TokenManager.init(tokenCache);
    }

    private void initHandler() {
        Handler actionHandler = new ActionHandler(actionMapping, constants);
        // 由於Handler是責任鏈模式,這是隻是得到鏈的第一個元素
        handler = HandlerFactory.getHandler(Config.getHandlers().getHandlerList(), actionHandler);
    }

    private void initOreillyCos() {
        OreillyCos.init(constants.getBaseUploadPath(), constants.getMaxPostSize(), constants.getEncoding());
    }

    private void initPathUtil() {
        String path = servletContext.getRealPath("/");
        PathKit.setWebRootPath(path);
    }

    private void initRender() {
        RenderFactory.me().init(constants, servletContext);
    }

    private void initActionMapping() {
        actionMapping = new ActionMapping(Config.getRoutes(), Config.getInterceptors());
        // buildActionMapping 運用反射設定好了Route,即把URI和Controller進行了匹配
        actionMapping.buildActionMapping();
        Config.getRoutes().clear();
    }

Config.configJFinal(jfinalConfig)裡面可以看到:


/*
 * Config order: constant, route, plugin, interceptor, handler
 * JFinalConfig其實是我們自己實現JFinalConfig的一個類 這裡得到具體的配置
 */
static void configJFinal(JFinalConfig jfinalConfig) {
        jfinalConfig.configConstant(constants);             initLogFactory();
        jfinalConfig.configRoute(routes);
        jfinalConfig.configPlugin(plugins);                 startPlugins(); // very important!!!
        jfinalConfig.configInterceptor(interceptors);
        jfinalConfig.configHandler(handlers);
    }

所以回到jfinal.init()方法裡面可以看到initHandler()initActionMapping()把一些類給初始化了。

初始化的大致流程就是這樣。我們總結一哈思路:
- 使用Filter對使用者所有的請求進行攔截
- 獲得JFinalConfig裡面的配置方法的屬性
- 依次對Handler,Route,Render進行初始化

Handler + Action

Handler可以分為程式設計師定義的和Jfinal自帶的(也叫ActionHandler),而這裡程式設計師自定的Handler只需要實現Handler介面,然後再註冊到JFinalHandler裡面就可以了。而這裡主要是討論ActionHandler

JFinal.initHandler()這個方法

private void initHandler() {
        Handler actionHandler = new ActionHandler(actionMapping, constants);
        handler = HandlerFactory.getHandler(Config.getHandlers().getHandlerList(), actionHandler);
    }

可以看到在這裡註冊了一個ActionHandler。那麼到現在還沒有說什麼是ActionHandler,ActionHandler就是處理客戶端向URI請求,那麼ActionHandler就是處理這類請求的

// 得到請求的Action
Action action = actionMapping.getAction(target, urlPara);
// 初始化Controller
Controller controller = action.getControllerClass().newInstance();
            controller.init(request, response, urlPara[0]);
// 在這裡呼叫請求的URI對應的方法
new Invocation(action, controller).invoke();

// 這裡重新定位到其他的URI對於的方法裡面去
handle(actionUrl, request, response, isHandled);

DB + ActiveRecord

就像前面所說的那樣ActiveRecord,Active是持久化的意思,Record是資料庫裡面的記錄,在JFinal裡面既然實現了這個模式,那麼Model就是所說的ORM記錄,只要有一個POJO類繼承這個類,然後在把註冊到JFinalConfig裡面,然後資料庫裡面的列和這個POJO裡面的屬性一一對應的話,就可以呼叫Model裡面的save(),find(),delete()進行資料庫操作了。那麼在JFinal裡面是怎麼實現的了?我們下面進入原始碼來看看.
首先有個IPlugin介面,定義了資料庫連線池Plugin的啟動與釋放.

public interface IPlugin {
    boolean start();
    boolean stop();
}

然後ActiveRecordPlugin實現了這個介面,完成了資料庫連線的一些配置,然後裡面有個成員變數
private IDataSourceProvider dataSourceProvider = null;
也就是資料庫連線池,在這裡我們使用C3p0。

那麼我們怎麼對資料庫進行CRUL呢?

Model裡面,這裡貼出find()的原始碼

/**
     * Find model.
     */
    private List<M> find(Connection conn, String sql, Object... paras) throws Exception {
        Config config = getConfig();
        Class<? extends Model> modelClass = getUsefulClass();
        if (config.devMode)
            checkTableName(modelClass, sql);

        PreparedStatement pst = conn.prepareStatement(sql);
        config.dialect.fillStatement(pst, paras);
        ResultSet rs = pst.executeQuery();
        List<M> result = ModelBuilder.build(rs, modelClass);
        DbKit.close(rs, pst);
        return result;
    }

使用了PreparedStatement預處理來防止SQL注入攻擊,而dialect這個變數就是真正進行SQL語句的類,

public abstract class Dialect

為一個抽象類,其實現類可以為MySQLDialectOracleDialect等,這樣做的好處就是無需在意具體的資料庫型別,而只要在配置檔案裡面進行配置了之後,就可以操作資料庫了。比如下面

public String forModelDeleteById(Table table) {
        String[] pKeys = table.getPrimaryKey();
        StringBuilder sql = new StringBuilder(45);
        sql.append("delete from `");
        sql.append(table.getName());
        sql.append("` where ");
        for (int i=0; i<pKeys.length; i++) {
            if (i > 0) {
                sql.append(" and ");
            }
            sql.append("`").append(pKeys[i]).append("` = ?");
        }
        return sql.toString();
    }

Render

後端給前段和移動端提供了API,在我的專案裡是返回了JSON資料。那麼為什麼一行程式碼renderJson(jsonText)就可以實現這個功能呢?我們來看看JFinal的構架。

上文說到在JFinal.java裡面有一個initRender()的方法。

private void initRender() {
        RenderFactory.me().init(constants, servletContext);
    }

這裡採用的工廠方法,得到一個自己的例項,進行初始化。為什麼這裡要用Singeton模式了??因為對於所有的Controller只需要一個Render就可以了哈,這是由邏輯關係而決定的。init()程式碼如下:

public void init(Constants constants, ServletContext servletContext) {
        this.constants = constants;
        this.servletContext = servletContext;

        // init Render
        Render.init(constants.getEncoding(), constants.getDevMode());
        initFreeMarkerRender(servletContext);
        initVelocityRender(servletContext);
        initJspRender(servletContext);
        initFileRender(servletContext);

        // create mainRenderFactory
        if (mainRenderFactory == null) {
            ViewType defaultViewType = constants.getViewType();
            if (defaultViewType == ViewType.FREE_MARKER) {
                mainRenderFactory = new FreeMarkerRenderFactory();
            } else if (defaultViewType == ViewType.JSP) {
                mainRenderFactory = new JspRenderFactory();
            } else if (defaultViewType == ViewType.VELOCITY) {
                mainRenderFactory = new VelocityRenderFactory();
            } else {
                throw new RuntimeException("View Type can not be null.");
            }
        }

        // create errorRenderFactory
        if (errorRenderFactory == null) {
            errorRenderFactory = new ErrorRenderFactory();
        }

        if (xmlRenderFactory == null) {
            xmlRenderFactory = new XmlRenderFactory();
        }
    }

由於我們是renderJson(),所以這些舒適化暫時沒有什麼用,我們就不說了,對這個RenderFactory工廠進行初始化之後。當呼叫renderJson()的時候,Controller裡面就可以看到這麼簡單的一行程式碼

public void renderJson(String key, Object value) {
        render = renderFactory.getJsonRender(key, value);
    }

那麼在哪裡是生成JSON資料的地方呢?我們進去renderFactory.getJsonRender(key, value)看看,

public Render getJsonRender(String key, Object value) {
        return new JsonRender(key, value);
    }

進一步的看看JsonRender

public class JsonRender extends Render{...}


public abstract class Render 
{
    public abstract void render();
}

重點注意JsonRender繼承Render類,而Render裡面有一個render()的抽象方法,可以發現JsonRender.render()就會把JSON資料render給客戶端,事實就是如此:

public void render() {
        if (jsonText == null)
            buildJsonText();

        PrintWriter writer = null;
        try {
            response.setHeader("Pragma", "no-cache");   // HTTP/1.0 caches might not implement Cache-Control and might only implement Pragma: no-cache
            response.setHeader("Cache-Control", "no-cache");
            response.setDateHeader("Expires", 0);

            response.setContentType(forIE ? contentTypeForIE : contentType);
            writer = response.getWriter();
            writer.write(jsonText);
            writer.flush();
        } catch (IOException e) {
            throw new RenderException(e);
        }
        finally {
            if (writer != null)
                writer.close();
        }
    }

,那麼render什麼時候呼叫了?在一開始給出的圖裡面,發現Render是在ActionHandler裡面的,既然在ActionHandler裡面,就會在handler()方法裡面被呼叫了。

if (render == null)
                render = renderFactory.getDefaultRender(action.getViewPath() + action.getMethodName());
            render.setContext(request, response, action.getViewPath()).render();

就是這麼簡單。

一些想法

雖然沒有看過SSH和SSM的原始碼,但是JFinal其核心思想應該都是一樣的.
- 首先在Controller裡面利用註解繼續路由=>這樣的話,每個方法都是一個URI對應的。可以使得每個方法之間降低了耦合度,而在每個方法的內部,只處理一個URI,具有單一職責。
- ORM框架,使得程式設計師在編寫資料庫的時候,不必處理資料庫的表,記錄和POJO的對應關係。而且在程式碼的閱讀與簡潔性上更加的清晰。
- Handler,Interceptor與責任鏈模式。可以讓程式設計師進行一些拓展進行AOP程式設計。使得框架的健壯性更好。
- 利用Dialect介面,和策略模式。只需在使用Dialect使用介面就可以了,而不必在意具體的實現類,實現了支援多資料庫的操作。這也是策略模式的好處。
- Render的實現,利用工廠模式,可以製造出不同的Render類。而減少了Render的程式碼,同時工廠類製造的是抽象類,而不是具體實現類,增加了可拓展性。符合多個Render的需求。同時把Render具體渲染的邏輯放在了ActionHandler而不是在Controller,可以使得子類在繼承Controller時,沒有過多的程式碼邏輯,把程式設計師不需要關心的類放在了ActionHandler裡面,程式設計師只需要瞭解Controller就可以.

問題

  • Jfinal是怎麼處理多執行緒的?
    這篇部落格看到了一個答案:上面說,在WEB應用程式裡面開一個執行緒,這個執行緒是屬於JVM級別的,如果你在Web 應用中啟動一個執行緒,這個執行緒的生命週期並不會和Web應用程式保持同步。也就是說,即使你停止了Web應用,這個執行緒依舊是活躍的。所以多執行緒的問題是由中介軟體Tomcat來解決的。每一個請求Tomcat就會分給客戶端請求一個執行緒來處理,所以不需要在WEB應用程式裡面來處理多執行緒。

  • Controller執行緒安全嗎?操作資料庫是同步的嗎?
    資料庫操作在資料庫層面已經做好了執行緒安全的問題,即使用SQL語句會避免這個問題的。(那麼java的執行緒安全是什麼?是對於記憶體上面的資料呀)

好吧,我只是大二本科生,大家別見怪,理解的不是很好。

郵箱:[email protected]

相關推薦

JFinal原始碼解析思想理解

動機 在做專案的過程中運用到了JFinal,由於是第一次看這樣框架的原始碼,所以很多東西都不知道。想多瞭解一些架構的思想和Web學習的基本知識。本文主要從大致的方面介紹JFinal,對於細節不做深究,而且本文的原始碼只剪輯了真正原始碼的部分。 總體思想

Java併發程式設計高階技術-高效能併發框架原始碼解析實戰(資源同步)

第1章 課程介紹(Java併發程式設計進階課程) 什麼是Disruptor?它一個高效能的非同步處理框架,號稱“單執行緒每秒可處理600W個訂單”的神器,本課程目標:徹底精通一個如此優秀的開源框架,面試秒殺面試官。本章會帶領小夥伴們先了解課程大綱與重點,然後模擬千萬,億級資料進行壓力測試。讓大

Java併發程式設計高階技術-高效能併發框架原始碼解析實戰(無密連結)

第1章 課程介紹(Java併發程式設計進階課程) 什麼是Disruptor?它一個高效能的非同步處理框架,號稱“單執行緒每秒可處理600W個訂單”的神器,本課程目標:徹底精通一個如此優秀的開源框架,面試秒殺面試官。本章會帶領小夥伴們先了解課程大綱與重點,然後模擬千萬,億級資料進行壓力測試。讓大

Java併發程式設計高階技術-高效能併發框架原始碼解析實戰

Java併發程式設計高階技術-高效能併發框架原始碼解析與實戰 第1章 課程介紹 什麼是Disruptor?它一個高效能的非同步處理框架,號稱“單執行緒每秒可處理600W個訂單”的神器,本課程目標:徹底精通一個如此優秀的開源框架,面試秒殺面試官。本章會帶領小夥伴們先了解課程大綱與重點,然後

redux 原始碼解析實際應用

Redux createStore createStore是一個函式,接收三個引數recdcer,initState,enhancer enhancer是一個高階函式,用於增強create出來的store,他的引數是createStore,返回一個更強大的store生

某課最新Java併發程式設計高階技術-高效能併發框架原始碼解析實戰

第1章 課程介紹(Java併發程式設計進階課程) 什麼是Disruptor?它一個高效能的非同步處理框架,號稱“單執行緒每秒可處理600W個訂單”的神器,本課程目標:徹底精通一個如此優秀的開源框架,面試秒殺面試官。本章會帶領小夥伴們先了解課程大綱與重點,然後模擬千萬,億級

某課最全Java併發程式設計高階技術-高效能併發框架原始碼解析實戰

第1章 課程介紹(Java併發程式設計進階課程) 什麼是Disruptor?它一個高效能的非同步處理框架,號稱“單執行緒每秒可處理600W個訂單”的神器,本課程目標:徹底精通一個如此優秀的開源框架,面試秒殺面試官。本章會帶領小夥伴們先了解課程大綱與重點,然後模擬千萬,億級

2018最新Java併發程式設計高階技術-高效能併發框架原始碼解析實戰(已完結)

第1章 課程介紹(Java併發程式設計進階課程) 什麼是Disruptor?它一個高效能的非同步處理框架,號稱“單執行緒每秒可處理600W個訂單”的神器,本課程目標:徹底精通一個如此優秀的開源框架,面試秒殺面試官。本章會帶領小夥伴們先了解課程大綱與重點,然後模擬千萬,億級資料進行壓力測試。讓大

某課無加密Java併發程式設計高階技術-高效能併發框架原始碼解析實戰

第1章 課程介紹(Java併發程式設計進階課程) 什麼是Disruptor?它一個高效能的非同步處理框架,號稱“單執行緒每秒可處理600W個訂單”的神器,本課程目標:徹底精通一個如此優秀的開源框架,面試秒殺面試官。本章會帶領小夥伴們先了解課程大綱與重點,然後模擬千萬,億級

Java併發程式設計高階技術-高效能併發框架原始碼解析實戰(雲盤分享)

第1章 課程介紹(Java併發程式設計進階課程) 什麼是Disruptor?它一個高效能的非同步處理框架,號稱“單執行緒每秒可處理600W個訂單”的神器,本課程目標:徹底精通一個如此優秀的開源框架,面試秒殺面試官。本章會帶領小夥伴們先了解課程大綱與重點,然後模擬千萬,億級

某網Java併發程式設計高階技術-高效能併發框架原始碼解析實戰(雲盤下載)

第1章 課程介紹(Java併發程式設計進階課程) 什麼是Disruptor?它一個高效能的非同步處理框架,號稱“單執行緒每秒可處理600W個訂單”的神器,本課程目標:徹底精通一個如此優秀的開源框架,面試秒殺面試官。本章會帶領小夥伴們先了解課程大綱與重點,然後模擬千萬,億級

Java併發程式設計高階技術-高效能併發框架原始碼解析實戰無加密

第1章 課程介紹(Java併發程式設計進階課程) 什麼是Disruptor?它一個高效能的非同步處理框架,號稱“單執行緒每秒可處理600W個訂單”的神器,本課程目標:徹底精通一個如此優秀的開源框架,面試秒殺面試官。本章會帶領小夥伴們先了解課程大綱與重點,然後模擬千萬,億級

Java併發程式設計高階技術-高效能併發框架原始碼解析實戰無加密分享

第1章 課程介紹(Java併發程式設計進階課程) 什麼是Disruptor?它一個高效能的非同步處理框架,號稱“單執行緒每秒可處理600W個訂單”的神器,本課程目標:徹底精通一個如此優秀的開源框架,面試秒殺面試官。本章會帶領小夥伴們先了解課程大綱與重點,然後模擬千萬,億級

Java併發程式設計高階技術-高效能併發框架原始碼解析實戰目前同步

第1章 課程介紹(Java併發程式設計進階課程) 什麼是Disruptor?它一個高效能的非同步處理框架,號稱“單執行緒每秒可處理600W個訂單”的神器,本課程目標:徹底精通一個如此優秀的開源框架,面試秒殺面試官。本章會帶領小夥伴們先了解課程大綱與重點,然後模擬千萬,億級

Java併發程式設計高階技術-高效能併發框架原始碼解析實戰最新無加密

第1章 課程介紹(Java併發程式設計進階課程) 什麼是Disruptor?它一個高效能的非同步處理框架,號稱“單執行緒每秒可處理600W個訂單”的神器,本課程目標:徹底精通一個如此優秀的開源框架,面試秒殺面試官。本章會帶領小夥伴們先了解課程大綱與重點,然後模擬千萬,億級

Java併發程式設計高階技術-高效能併發框架原始碼解析實戰(資源連結)

第1章 課程介紹(Java併發程式設計進階課程) 什麼是Disruptor?它一個高效能的非同步處理框架,號稱“單執行緒每秒可處理600W個訂單”的神器,本課程目標:徹底精通一個如此優秀的開源框架,面試秒殺面試官。本章會帶領小夥伴們先了解課程大綱與重點,然後模擬千萬,億級

最新Java併發程式設計高階技術-高效能併發框架原始碼解析實戰

第1章 課程介紹(Java併發程式設計進階課程) 什麼是Disruptor?它一個高效能的非同步處理框架,號稱“單執行緒每秒可處理600W個訂單”的神器,本課程目標:徹底精通一個如此優秀的開源框架,面試秒殺面試官。本章會帶領小夥伴們先了解課程大綱與重點,然後模擬千萬,億級

阿神Java併發程式設計高階技術-高效能併發框架原始碼解析實戰

第1章 課程介紹(Java併發程式設計進階課程) 什麼是Disruptor?它一個高效能的非同步處理框架,號稱“單執行緒每秒可處理600W個訂單”的神器,本課程目標:徹底精通一個如此優秀的開源框架,面試秒殺面試官。本章會帶領小夥伴們先了解課程大綱與重點,然後模擬千萬,億級

Java併發程式設計高階技術-高效能併發框架原始碼解析實戰資源分享

第1章 課程介紹(Java併發程式設計進階課程) 什麼是Disruptor?它一個高效能的非同步處理框架,號稱“單執行緒每秒可處理600W個訂單”的神器,本課程目標:徹底精通一個如此優秀的開源框架,面試秒殺面試官。本章會帶領小夥伴們先了解課程大綱與重點,然後模擬千萬,億級

SpringMVC原始碼解析思考

首先要知道servletContext與servletConfig servletContext是web應用級別,是jvm程序級別;servletConfig是servlet服務級別,是執行緒級別。 定義: ServletConfig:Servlet的配置物件,容器在初始化