JFinal源码解析与思想理解
2016-06-06 15:50
246 查看
动机
在做项目的过程中运用到了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作者给出的图:
![](http://cms.csdnimg.cn/article/201312/10/52a67b9259de2.jpg)
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的线程安全是什么?是对于内存上面的数据呀)
好吧,我只是大二本科生,大家别见怪,理解的不是很好。
邮箱:1906362072@qq.com
相关文章推荐
- Incompatible integer to pointer conversion assigning to 'NSInteger *' (aka 'long *') from 'int'
- javascript闭包
- IDC行业往何处去?
- 协同过滤推荐算法的原理及实现
- 多线程之:正确使用 Volatile 变量
- SQL注入总结
- 启用物料账后,有两种物料价格确定方式
- 蓝牙attribute protocol
- MySQL的多列索引
- 重构-C++实现矩阵的简单实例
- 最短路
- 小改GooFlow》》》goflow
- css中的display
- hihocoder字典树
- Apache服务器常规操作
- oc 多线程UI更新
- python定时杀进程
- 山东多校联合模拟赛 Day1第一题
- iOS开发中可能会用到的一些函数。。。
- java泛型(一)、泛型的基本介绍和使用