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
可以把它理解為持久的意思,也就是說與資料庫是持久連線。
然後可以參考這些文章
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由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
為一個抽象類,其實現類可以為MySQLDialect
,OracleDialect
等,這樣做的好處就是無需在意具體的資料庫型別,而只要在配置檔案裡面進行配置了之後,就可以操作資料庫了。比如下面
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的執行緒安全是什麼?是對於記憶體上面的資料呀)
好吧,我只是大二本科生,大家別見怪,理解的不是很好。
相關推薦
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的配置物件,容器在初始化