JFinal技术架构浅析
1.1 空间架构 框架采用微内核全方位扩展架构,全方位是指其扩展方式在空间上的表现形式。整 个空间架构由 Handler、Interceptor、Controller、Render、Plugin 五大部分组 成。每个部分都是基于接口实现的,支持完整的自定义,使用灵活,扩展性强。 空间架构图。 Handler Interceptor Controller Render Plugin
1.2 逻辑架构 框架的逻辑架构非常简单,就是一个经典的 MVC 模式演化、大致分成以下 6 个步骤: 1.2.1 Http
包括 HttpServletRequest、HttpServletResponse,即客户端请求与响应 1.2.2 Filter 所有请求的拦截器,相当于是一个闸口 1.2.3 Handler 请求处理器,是对所有请求的公共处理,在这里我们可以做权限控制、安全验证等。 它是一个责任 链模式变种, 由初始配置的 Handler 以及框架默认的 ActionHandler 构成一个链条,依次执行。 1.2.4 Action 动作处理器,它是一个 AOP Command 模式变种。由 ActionInvocation 进行调度, 先执行拦截器,最后执行具体方法。
1.2.5 Model 模型层。在这个层面包括实体模型的定义、 DB ActiveRecord 模式数据库操作的支持、从请求中解析出对应参数构造 Model 实例,可以把它看成是 DAO。 1.2.6 Render
渲染层。负责将
服务端的数据组装成客户端需要的数据格式,然后 response 到客户端。支持多种视图的渲染:FreeMark、Jsp、VELOCITY 以及自定义。
HttpServletRequest
HttpServletResponse JFinalFilter(所有请求的拦截器
) Interceptorn(拦截器
) Method(具体的方法
) Action ActionInvocation(动作调度器
) ActionHandler(动作处理器
) Handler(处理器
) Handler ModelInjector (模型构造器
) Model(模型定义
)
DB(数据库操作工具
) Render(渲染器
)
JSP FreeMark VeloCity 自定义
View 1.3 代码架构 1.3.1 包结构 1.3.2 包简介 Aop:拦截器 Config:通用配置 Core:核心处理,主要是 Controller 的调度 Ext:扩展包 Handler:全局处理器 I18N:国际化 util:一些工具类 Log:日志类 Plugin:插件处理 Render:渲染处理,比如对于 FreeMark 的支持 Server:服务器 Token:令牌
Upload:文件上传处理 Validate:验证处理,主要是数据持久化时的验证 1.4 运作原理
我们从一次新增页面的 add 操作请求来看: 1. 首先是客户端点击新增按钮,提交一个新增请求,请求中会带上服务端处理地址 url
2. 所有请求都会被 JFinalFilter 拦截,然后调用 Handler 进行详细处理 3. Handler 是 一 个 链 条 形 式 的 调 用 , 包 括 0-n 个 自 定 义 Handler , 以 及最 后 一 个ActionHandler,依次执行,当然可以自定义跳出。 4. 进入 ActionHandler 后,首先会根据请求的 target 从缓存的 ActionMapping 中映射获取 到具体操作对应的 Action 对象,这个对象里面封装了方法名、方法 上面的拦截器,方法所在的 Controller, controller 上面的拦截器等。 然后根据 Action 以及 Controller 实例构造出 ActionInvocation。
5. 接下来通过 ActionInvocation 的 invoke 进行具体处理,这是一个明显的
Command模式的实现。首先是拦截器的调用,拦截器调用中会重新回调
ActionInvocation 的 invoke, 当拦截器调用完毕后,会调用当前操的 method
6. 当进入具
体 controller 的新增方法时, 调用基类getModel(Systemparam.class);这个方法会从 request 中解析出所需要的数据,通过反射设置给具体的 Model 实体类,最终通过 ActiveRecord 来进行数据存储 .
7. 最后是页面渲染 render
核心代码简介
我们还是从一次新增请求的流程来看涉及到的核心类有哪些本身就是一个 Http 的过滤器,主要有 3 个方法
2.1.1 init(初始化)
在服务器启动时执行一次,负责初始化框架的全局配置
2.1.2 destory(销毁)
在服务器停止时运行一次,负责销毁框架的全局配置
2.1.3 doFilter(拦截)
拦截所有的客户端请求,处理具体逻辑
2.2 JFinalConfig
是一个 API 引导式配置接口,供使用者自定义实现。里面主要配置常量、配置路由、配置处理器、配置插件、配置拦截器。
2.2.1 Config
自定义配置执行的调度器
2.3 JFinal
初始化框架全局配置的调度器, 由它来负责调用各个组件的初始化接口。 包括 path、 Handler、 Action、Render、ActiveRecord、Const、i18n 等。 另外它 还提供了启动内置服务器 Jetty 的 api
2.4 Action
即一个具体的动作,比如新增、修改、删除等,你也可以称之为方法。它包含以 下内容:controllerClass、controllerKey、actionKey、method、methodName、interceptors、 viewPath
2.4.1 ActionMapping
初始化请求 url 与 Action 的映射关系,是 Controller 的解析器,最终的产出物是生成一个个 actionKey 以及与之对应的 Action 对象。
2.4.2 ActionInvocation
动作调度器,负责调用各个拦截器以及具体的方法
2.4.3 ActionKey
自定义的注解,用于自定义 action 的路由规则
2.5 Handler
通用的全局处理器
2.5.1 ActionHandler
动作处理器,负责分析请求,找到对应的 Action 并执行
2.5.2 HandlerFactory
全局 Handelr 的工厂,负责生成一个 handler 链
2.6 Render
渲染器
2.6.1 RenderFactory
渲染器的工厂类,负责构造出各种视图的渲染器
2.6.1.1 FreeMarkerRenderFactory
FreeMarkerRender
2.6.1.2
JspRenderFactory
JspRender
2.6.1.3
VelocityRenderFactory
VelocityRender
2.7 Iplugin 插件
2.7.1 C3p0Plugin、DruidPlugin
数据源插件
2.7.2 EhCachePlugin
缓存插件
2.7.3 ActiveRecordPlugin
model 初始化插件
2.7.3.1 AutoTableBindPlugin
自动绑定 model 与数据库表的插件
2.7.3.2 TableInfoBuilder
负责初始化 model 与数据库表的映射关系
2.7.3.2.1 TableInfo
存储的即映射关系,包括 tableName、primaryKey、columnTypeMap
2.7.3.3 Model
所有 Model 的基类,封装了通用的数据库操作的方法
2.7.3.3.1 ModelInjector
从请求中构造出 model 实例的工具类
2.7.3.4 Db、DbKit 通用的数据库操作工具类
2.7.4 SpringPlugin
负责与 spring 框架进行集成
2.7.5 QuartzPlugin、Cron4jPlugin 任务调度插件
2.7.6 ConfigPlugin 配置文件插件
2.8 Routes 路由规则
2.8.1 MyRoutesUtil 自动绑定 Controller 的工具类
2.9 Controller
所有 Controller 的基类,封装了通用的方法
2.10拦截器
Interceptor
2.10.1 Before
自定义拦截器的注解,配合
拦截器实现类使用,实现热插拔拦截的功能
2.10.2 Tx
事务处理的拦截器
2.11Validator
服务端的校验器,供用户自定义实现
2.12服务器接口
IServer
2.12.1 JettyServer
内置的 jetty 服务器实现
2.13MultipartRequest 对于文件上传的处理
另外还有 1 个分布式缓存的实现,具体可以参照 MemcacheTool
3.详细介绍
3.1初始化
初始化在服务器启动时进行,入口即在 JFinalFilter 中,下面讲最重要的
2 项初 始化过程
3.1.1 控制器的初始化
控制器的初始化包括 2 个方面,1 个是
路由,1 个是 action。
路由仅仅是初始化得到一个path—ControllerClass 的映
射。 Action 的初始化则是将 Controller 中每个方法进行封装。 Action 的初始化依
靠 ActionMapping 的 buildActionMapping 方法实现,
下面是实现过程, 当然我们首先
得明白 actioin 初始化目标是方法:
1. 获取不需要初始化的方法的名称,主要是基类
Controller 里面封装的一些通用方法 2. 获取全局拦截器
3. 遍历前面初始化好的
路由,也就是每个自己写的业务 Controller
4. 解析每个 Controller 的拦截器
5. 反射获取到每个 Controller 里面的所有方法,进行遍历 6. 排除掉不需要的,剩下
的方法首先解析出方法上面的拦截器
7. 进行 Action 的构造 new Action
(controllerKey, actionKey, controllerClass, method, methodName, actionInter
s, routes.getViewPath(controllerKey));
8. 最后用一个 actionKey 与 Action 进
行映射,然后全局缓存住,这个 actionKey 即为客户 端调用这个方法的唯一钥匙
从上面
可以看出, jfinal 中实际上是不保留对于 Controller 的初始化信息的,它的单位只
有 1 个,就是 Action (动作 or 方法)。在我看来,这样的组织有点不合理,个人觉得
应该是 Controller 保留,Action 依附在 Controller 中,这样才是最合理的,而且全局
的和类的成员 将不需要重复出现在每个 Action 中。 客户端请求过来之后,应该首先执行
全局方法,再获取到 Controller,执行类方法,最后获取 对应 Action,执行具体动作。
3.1.2 Model 初始化
对于 model 的初始化,最终结果就是获取 TableInfo 对象 这个对象里面包含表名、主键名、实体名以及 1 个 columnTypeMap,这个 map 里面存放的是
数据库列名与该列数据类型 Class 的映射入口也是 JfinalFilter 的 init 方法
这里的 TableInfo 的 list 就是我们使用 Jfinal 的 config 需要初始配置的,
3.1.3 自动初始化实体的实现
编写了一个 AutoTableBindPlugin 插件类,通过查询出所有继承自 Model 的实体类,然后通过解析 TableBind 这个自定义注解,获取表名以及主键名
3.2 Jdbc 是如何与连接池插件集成的
比如我们原先使用的 proxool, hibernate 就支持与之整合。 亦或者 spring 中默认使用的 c3p0。 连接池, 说白了就是用来管理数据库连接的, 比如最大允许多少连接数, 最长连接时间等等, 那么我们自己写的 jdbc, 怎么来整合它获得它所提供的功能呢?其实和简单, 我们只需要将 数据库连接交由连接池去托管即可:
1. 自定义一个类,比如叫 C3p0Plugin,它负责与连接池的整合衔接,其实就是根据数据库 的配置,生成一个 javax.sql.DataSource即可,这个 DataSource 对象必须是连接池的, 比如要整合 C3p0,那么就必须
DataSource dataSource = new
ComboPooledDataSource(); dataSource.setJdbcUrl
(jdbcUrl); dataSource.setUser(user); dataSource.setPassword(password); 。。。 。。
2. 所有需要使用到数据库 Connection 的地方,全部通过 C3p0Plugin 中
的 DataSource 获取,获取方法为 dataSource.getConnection()。 这样就完成了将数据库连接纳入连接池的工作
3.3 声明式事务是如何实现的
框架的事务是通过注解来支持的,spring 也是如此,即在需要事务的方法上面加上事务 特有的注解,比如 这就是所谓的声明式事务。要讲它是如何实现的,那么就必须先明白注解是怎么起作用 的。
1. 注解的意义 注解的诞生就是为了解放 xml 的冗繁,从这一点可以看出,其实它最核心的功能就 是为了配置。当然它还有其他一些作用,比如功能聚簇。实体的映射 xml 文件其实就应该 与实体在一起的。
2. 自定义注解 自己定义一些需要使用到
的注解,这个简单的应用还是比较简单的, 这里不多说 3. 注解解析
注解的核心作用既然是配置,那么肯定是需要解析的,而且这种固定式的配置,显 然只需要解析一次即可。
Jfinal 中是在 Filter 的 init 方法中进行注解的解析工作的。 它会将方法以及方法上面的注解的映射关系都解析好缓存起来 4. 注解应用 当调用到具体该方法时,首先会从缓存中获取该方法上面的注解,然后根据具体的 注解内容进行相关的处理,比如这里的 Tx.class,就是一个事务处理的拦截器,那么 就会调用事务处理的操作。 这里是一个递归调用方式:
ActionInvocation 调用 invoke 方法 Invoke 方法中调用各个拦截器的拦 截方法 intercept Intercept 方法中会通过传入的 ActionInvocation 对象回 调 invoke 方法继续处理 这样如下的事务操作就可以囊括整个方法的执行了,这就是个命令模式。
3.4DB ActiveRecord
DB 的话很简单明了,就是一个数据库操作工具
类, ActiveRecord 说白了就是在我们 的 model 中加入了数据库操作的能力。 比如 add、 remove、 update。 这些通用的处理, JFinal 在 中统一交由最上层的基类 Model 进行,我们的实体类继承自 Model,就拥有了该能力。
3.5表单数据自动绑定是怎么实现的
Jfinal 中一个表单的新增,后台只需要写如下代码即可
完成,它是如何实现的 1. 表单页面
首先我们看下这个表单页面,注意 3 个输入域的 name 属性,都是 blog.xxx,这里 实际上就是设置了值与实体属性之间的映射关系了
2. 服务端从 request 中获取到所有的值,过滤出其中 blog 属性,缓存到 Model基类 的 attrs 这个 map 中 3. 最后通过 Record 的 add 方法进行新增。
3.6关于路由
3.6.1 ioc
这里的路由就相当于我们原先 seam 框架的 ioc 了, 不过它功能更简单一些, 需要在初始 的时候进行配置,也就是页面与 Controller 之间的映射关系。
3.6.2 如何去除路由配置
很简单,使用我们原先的注解的方式即可。自定义一个 Controller 的注解,比如 Name,然 后解析初始化到全局配置中即可。
3.6.3 自动绑定 controller 的实现 原理与我们原先猜想的一样
1. 自定义了一个 RouteBind 注解,然后每个 Controller 进行绑定
2. 编写了一个工具类 MyRoutesUtil
该工具类将 webinf/classes 目录下面所有的.class 二进制文件查找出来,然后通过反射 和解析注解生成 jfinal 的 Routes
3. 在服务器启动的时候进行初始化即可
3.7 拦截器
Jfinal 中所有的拦截都通过 Interceptor 实现,根据位置大致可以分成以下几种
3.7.1 全局拦截器
在 config 中进行注册
3.7.2 类拦截器
在类上面通过注解注册
3.7.3 方法拦截器
在方法上面通过注解注册
3.8 插件机制
插件的机制主要就是一个基于接口动态扩展实现的架构,最顶层是一个 IPlugin。其实说白 了,它就是一个全局性配置的扩展实现。
3.8.1 插件的注册
在自定义实现的 JfinalConfig 中进行配置
3.8.2 插件运行
所有插件的运行都是在服务器启动的 时候进行一次,具体是在 Config 类中的如下方法:
3.8.3 插件停止
停止即在服务器关闭时 JfinalFiler 的 destroy 中进行
3.9 缓存
3.9.1 EcachePlugin
框架本身内置了 Ecache 这个插件,可以直接使用,只需要在 JfinalConfig 中进行注册即可
3.9.2 MemcachedClient
另外还有 1 个分布式缓存的实现,具体可以参照 MemcacheTool
4如何使用
4.1 拦截器
参照上面的 3.7
4.2 控制器
编写一个普通 java 类,继承 Controller,类上面使用注解进行路由绑定
@RouteBind(path = "/power") public class PowerContraller extends BaseController
4.2.1 调用 controller 的方法
1. 页面进来的时候默认会走 index 方法,这个实现在 ActionMapping
2. 调用指定方法 默认调用 controller 中方法的方式 为/controllername/methodname <form action="${path}/power/list"> 比如上面的路径,power 为 controller 的路由注解、list 即为方法名字。 但是也可以通过 ActionKey 这个注解来进行自定义,比如如下就将 list 方法名改成了 hello。 @ActionKey(value="hello") public void list() {}
4.3 数据库操作
Jfinal 中数据库的增删改查都可以通过 Db 这个工具类或者实体对象进行。也就是所谓的 DB ActiveRecord 方式。里面的数据库方法基本涵盖了所有需要的,而且它支持 POJO 形式 的操作。
4.4
参数获取 后台获取请求中的参数,都是通过基类 Controller 中封装好的 getPara…方法进行。
4.5 如何实现事务
框架的事务是方法级别的,在需要实现事务的方法上面使用注解@Before{Tx.class},加上事 务 Tx 的拦截器即可。
4.6 如何实现文件上传
前端用一个 js 上传控件,后台有通用的 FileController 支持处理 比如前端用的 swf 的上传控件 后端:
5 后续扩展改进
5.1 懒加载
主要是针对 Model 以及 Controller 的初始化, 使其支持懒加载的实现, 最后可以通过 devMode 参数来进行控制
5.2 带参数方法的支持
在前端需要能够调用服务端带参数的方法,参数能够直接绑定到方法上面
5.3 如何实现多数据源
多数据源的实现主要在于事务的控制上面,大家看下这个事务拦截器的实现:
这里 DbKit.getDataSource 获取到的是一个静态的全局框架性数据源的定义,显然是唯一的。 但是我们可以对其进行改造, 只需要这里事务可以从当前使用的数据源进行控制即可达到目 的。
5.4 集群与负载均衡
Session 的信息可以采用分布式的 memcache 来支持共享。而无状态的框架架构可以支持集 群与负载均衡的实现
5.5 前端的技术选型
目前 js 控件库准备采用 jQuery-eazyUI 与 dwz 中的一个,模板则使用 freeMark 或者 jsp 来进 行,具体情况需 要等到实际尝试测试后确定
5.6 Webservice 支持
restful 方式,还是传统的 webservice。这个个人觉得可以
放到后面去考虑,实现肯定不是问 题的。
Copyright © 2024 妖气游戏网 www.17u1u.com All Rights Reserved