动机
在做项目的过程中运用到了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基类。
- 1
- 2
- 3
- 1
- 2
- 3
- 1
- 2
- 3
- 1
- 2
- 3
这样只需要直接的调用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由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()
找到下面这些初始化的过程
- 1
- 2
- 3
- 4
- 1
- 2
- 3
- 4
然后再去看看jfinal.init()
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
在Config.configJFinal(jfinalConfig)
里面可以看到:
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
所以回到jfinal.init()
方法里面可以看到initHandler()
,initActionMapping()
把一些类给初始化了。
初始化的大致流程就是这样。我们总结一哈思路:
- 使用Filter
对用户所有的请求进行拦截
- 获得JFinalConfig
里面的配置方法的属性
- 依次对Handler
,Route
,Render
进行初始化
Handler
+ Action
Handler
可以分为程序员定义的和Jfinal自带的(也叫ActionHandler),而这里程序员自定的Handler
只需要实现Handler
接口,然后再注册到JFinalHandler
里面就可以了。而这里主要是讨论ActionHandler
。
在JFinal.initHandler()
这个方法
- 1
- 2
- 3
- 4
- 1
- 2
- 3
- 4
可以看到在这里注册了一个ActionHandler
。那么到现在还没有说什么是ActionHandler
,ActionHandler
就是处理客户端向URI请求,那么ActionHandler
就是处理这类请求的
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
DB + ActiveRecord
就像前面所说的那样ActiveRecord
,Active
是持久化的意思,Record
是数据库里面的记录,在JFinal里面既然实现了这个模式,那么Model
就是所说的ORM记录,只要有一个POJO类继承这个类,然后在把注册到JFinalConfig
里面,然后数据库里面的列和这个POJO里面的属性一一对应的话,就可以调用Model
里面的save()
,find()
,delete()
进行数据库操作了。那么在JFinal里面是怎么实现的了?我们下面进入源码来看看.
首先有个IPlugin
接口,定义了数据库连接池Plugin
的启动与释放.
- 1
- 2
- 3
- 4
- 1
- 2
- 3
- 4
然后ActiveRecordPlugin
实现了这个接口,完成了数据库连接的一些配置,然后里面有个成员变量
private IDataSourceProvider dataSourceProvider = null;
也就是数据库连接池,在这里我们使用C3p0。
那么我们怎么对数据库进行CRUL呢?
在Model
里面,这里贴出find()
的源码
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
使用了PreparedStatement
预处理来防止SQL注入攻击,而dialect
这个变量就是真正进行SQL语句的类,
- 1
- 1
为一个抽象类,其实现类可以为MySQLDialect
,OracleDialect
等,这样做的好处就是无需在意具体的数据库类型,而只要在配置文件里面进行配置了之后,就可以操作数据库了。比如下面
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
Render
后端给前段和移动端提供了API,在我的项目里是返回了JSON数据。那么为什么一行代码renderJson(jsonText)
就可以实现这个功能呢?我们来看看JFinal的构架。
上文说到在JFinal.java
里面有一个initRender()
的方法。
- 1
- 2
- 3
- 1
- 2
- 3
这里采用的工厂方法,得到一个自己的实例,进行初始化。为什么这里要用Singeton模式了??因为对于所有的Controller
只需要一个Render
就可以了哈,这是由逻辑关系而决定的。init()
代码如下:
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
由于我们是renderJson()
,所以这些舒适化暂时没有什么用,我们就不说了,对这个RenderFactory
工厂进行初始化之后。当调用renderJson()
的时候,Controller
里面就可以看到这么简单的一行代码
- 1
- 2
- 3
- 1
- 2
- 3
那么在哪里是生成JSON数据的地方呢?我们进去renderFactory.getJsonRender(key, value)
看看,
- 1
- 2
- 3
- 1
- 2
- 3
进一步的看看JsonRender
,
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 1
- 2
- 3
- 4
- 5
- 6
- 7
重点注意JsonRender
继承Render
类,而Render
里面有一个render()
的抽象方法,可以发现JsonRender.render()
就会把JSON数据render给客户端,事实就是如此:
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
,那么render
什么时候调用了?在一开始给出的图里面,发现Render
是在ActionHandler
里面的,既然在ActionHandler
里面,就会在handler()
方法里面被调用了。
- 1
- 2
- 3
- 1
- 2
- 3
就是这么简单。
一些想法
虽然没有看过SSH和SSM的源码,但是JFinal其核心思想应该都是一样的.
- 首先在Controller
里面利用注解继续路由=>这样的话,每个方法都是一个URI对应的。可以使得每个方法之间降低了耦合度,而在每个方法的内部,只处理一个URI,具有单一职责。
- ORM框架,使得程序员在编写数据库的时候,不必处理数据库的表,记录和POJO的对应关系。而且在代码的阅读与简洁性上更加的清晰。
- Handler
,Interceptor
与责任链模式。可以让程序员进行一些拓展进行AOP编程。使得框架的健壮性更好。
- 利用Dialect
接口,和策略模式。只需在使用Dialect
使用接口就可以了,而不必在意具体的实现类,实现了支持多数据库的操作。这也是策略模式的好处。
- Render
的实现,利用工厂模式,可以制造出不同的Render
类。而减少了Render
的代码,同时工厂类制造的是抽象类,而不是具体实现类,增加了可拓展性。符合多个Render
的需求。同时把Render
具体渲染的逻辑放在了ActionHandler
而不是在Controller
,可以使得子类在继承Controller
时,没有过多的代码逻辑,把程序员不需要关心的类放在了ActionHandler
里面,程序员只需要了解Controller
就可以.