公司做技术专题,这一期选择的JFinal,历时两天多完成,特将研究结果贴上来,其中难免有不妥之处,多多沟通交流,个人认为JFinal是非常好的一个框架,在某些应用场景下是很不错的选择!
===============正文开始===============
1. JFinal简介
1.1. 摘自官方:
JFinal 是基于 Java 语言的极速 WEB + ORM 框架,其核心设计目标是开发迅速、代码量少、学习简单、功能强大、轻量级、易扩展、Restful。在拥有Java语言所有优势的同时再拥有ruby、python、php等动态语言的开发效率!为您节约更多时间,去陪恋人、家人和朋友 :)
1.2. 个人总结
主要实现功能类似现在流行的SSM框架,只是在开发变成方式上和运行速度上有区别,强调开发速度和运行速度快。
1.3. 主要技术
和其他主流技术对比
层次 | JFinal | JEECG | 普通框架 | 后台接口 |
前端 | Enjoy/Freemarker | Freemarker | JSP+JQuery+EasyUI | 无 |
控制层 | JFinal Controller | SpringMVC | SpringMVC | SpringMVC |
IOC容器 | 无 | Spring | Spring | Spring |
ORM | ActiveRecordPlugin | Hibernate | MyBatis | MyBatis |
2. 研究原因
现有Spring为核心的技术框架已经比较普及,但在某些情况下存在开发速度慢,不灵活的问题(通过JEECG可解决代码创建时候的速度问题),听说过JFinal的极速开发,这次一探究竟,使用如下业务场景进行“代码量”和“开发速度”方面的比对测试:
l 开发新功能(前台+后台)-产品管理-单表
l 后台接口(获取警报)
l 增加一个字段
l 增加一个业务接口
l 修改业务逻辑
3. JFinal主要特点
官方API参考:
3.1. 总体架构
参考JFinal文档
上图看似简单,其实好多功能是在”Plugin”中实现,包括ORM,Cache等功能
3.2. 快速上手(以Maven为例)
1) 开发环境选择:
l 通用Maven
l 特定开发环境Eclipse、IDEA
l Eclipse插件JBolt
2) 配置:数据库参数,Maven
3) 启动
l 内置Jetty:一般开发使用,速度快,打开开发模式会打印详细日志和进行热加载
l 外部Tomcat:一般生产环境使用,关闭开发模式
3.3. 配置参数:JFinalConfig
此处是各种配置参数的入口,每个Project需实现此抽象类,并在web.xml中作为filter的参数进行配置,系统在启动的时候会初始化此类并进行调用,完成运行参数的配置。
1) 常量配置(constant):一些系统使用的全局参数配置,比如开发模式(devMode),日志工厂等
2) 路由配置(route):主要功能类似Spring的RequestMapping,将URL和类关联起来,此处配置类级别的路径;可添加多个Route,每个Route可设置自己的路径;可添加route级别的拦截器
3) 模板引擎配置(engine):模板可包括HTML模板和SQL模板,以及其他用途的模板,此处配置模板引擎的全局参数.
4) 插件配置(plugin):各种插件注册到系统,例如数据库插件,EHCache,Redis等.
5) 拦截器配置(interceptor):此处添加全局的拦截器,类和方法的拦截器在Class中设置.
6) 处理器配置(handler):handler类似Spring中的mappingHandler,除了系统默认的Handler,可以增加自己的Handler,在HTTP请求的过程中进行干预,做修改数据等操作.
相比SpringMVC:
方式不同,效果相同,相比Spring的xml,如果配置出现问题在Java代码里面应该更好查找,因为可以debug:),相反XML文件则相对复杂,但如果是在生产环境需要修改的配置参数,还是需要独立为properties文件,因为在生产环境不能通过修改源代码来修改配置
3.4. 控制器:Controller
此和SpringMVC的Controller作用基本相同,主要作为前端和后端应用之间的控制层存在
3.4.1. URL路径映射
类路径:和SpringMVC不同,JFinal不用注解实现路径映射,类的base路径在JFinalConfig的route设置
类方法路径:类中所有public的方法自动映射为二级路径,直接访问类路径(例如”/goods”)会执行类的index()方法
相比SpringMVC:无大差别,实现只是省略了方法级别的注释
3.4.2. 获得业务数据
在Controller的方法中获得业务数据通常有以下方法,不用使用注解
l Action参数注入:增加相关Bean类型的参数作为方法的参数,则自动生成相关Bean,此需要JDK8支持,打开“保留方法参数”的编译参数
l getPara():可使用此类函数获得所有参数
l getModel()/getBean():从request里面生成相关业务bean,getModel()用于没有getter/setter方法的bean,要求request参数名和数据库字段名相同; getBean()则需要有相关方法,要求request参数名和bean的get/set属性名相同
相比SpringMVC:无大差别,最大差别在于此处支持没有getter/setter方法的bean,但是此和面向对象原则相违背,不过这样更简洁,看个人各取所需
3.4.3. 返回数据
l 原生render*()接口,基本支持返回大部分数据类型的接口:HTML,JSP,Enjoy模板,Freemarker模板,Velocity模板,二维码,json,返回错误信息
l renderJson*(): 返回json提供若干个个接口,供不同需要进行选择,并且提供四种json转换框架JFinalJson、FastJson、Jackson,MixedJson(JFinalJson+FastJson)供选择,默认使用JFinalJson,如需改变在configConstant()中使用setJsonFactory()修改
l 自定义的render:render(new MyRender())
相比SpringMVC:实现功能相同,但是易用性,简单性较强
扩展性方面:Spring可以对接各种ViewResolver和MessageConverter,只是需要进行额外的xml配置,JFinal则直接定义class,方法简单粗暴,比较容易使用
3.5. AOP
1) 拦截器
可使用注解@Before和@Clear进行拦截器的使用,和SpringMVC类似
2) 对象注入
目前在Controller中可使用@Inject注入业务对象
相比SpringMVC:实现基本需要使用的功能,没有Spring复杂,简单易用,可满足一般需要
3.6. Service层
JFinal没有明确的提出Service层,在小型项目中,如果追求简介和快速,各业务之间逻辑重用不多的情况下,可将数据库访问调用代码直接在Controller完成,否则需要构建Service层访问数据库实现业务逻辑
3.7. DAO层:ORM
此节主要阐述JFinal在数据库ORM方面的概念和使用方法,并和SpringJdbcTemplate以及MyBatis进行对比
JFinal的ORM使用ActiveRecordPlugin实现,其特点是灵活,简单,使用方便,编码量少
3.7.1. 数据Bean
3.7.1.1. SSM中的贫血模式
数据Bean,在SSM中我们 一般都称之为POJO,也就是只是一个数据承载对象,不包含业务逻辑,即所谓的“贫血模式”,在此模式下,优点是所有业务逻辑集中在Service层,这样分层比较清晰,缺点是不太符合面向对象的原则。
3.7.1.2. JFinal中的充血模式
在JFinal中,数据Bean不是一个简单的POJO,其包含了后台的所有数据库操作,简单理解所有的CRUD操作都不用使用DAO来完成,其自身具备此能力,即所谓的“充血模式”,在此模式下,优点是面向对象,缺点是业务逻辑分散在Bean和Service层,代码不太清晰
3.7.1.3. JFinal中的Bean特点
3.7.1.3.1. 代码简化,编码加快
所有后台逻辑和参与数据库操作的操作可直接通过Bean来完成,在某些情况下极大简化了代码,例如:
empoyee.set(“code”,”A001”).set(“name”,”张三”).save()
1行代码完成了一般3行的功能
3.7.1.3.2. 不需要设置属性,没有getter/setter
l Bean不需要每个属性增加get/set方法,包括从ORM取数,Controller中的Bean生成,传给ORM进行数据更新等阶段的操作,都可使用此种Bean,此极大的简化了传统Bean需要大量Set/Get方法,不需要进行字段的mapping配置,取到对象后直接使用。
l 实现方式
后台使用Map采用key/value的方式存储数据,可使用get*(fieldName),set*(fieldName,fieldValue)操作属性值,其中字段名和数据库表字段名相同
l 此种方式的缺点(如果单纯考虑开发速度,以下不算缺点)
所有字段名为数据表的字段名,一方面不好记忆,另一方面数据库表字段的命名方式(下划线分隔)一般和Java属性(驼峰)不同,容易造成混淆
不符合面向对象的原则,对一些第三方框架(需要通过get读取属性等)不兼容,JFinal最新版本目前已经提供从数据库直接按照表结构生成具有getter/setter方法的基础对象
3.7.1.4. Bean和Controller的生成工具
JFinal示例程序中的_JFinalDemoGenerator使用active record的Generator根据数据库表生成相关的BaseBean和Bean,BaseBean为具有getter/setter方法的类,Bean只是继承BaseBean,无其他代码。
相比MyBatis:Java bean比Mytatis简单,只需要继承Jfinal的Model即可实现setter/getter以及和数据库表列的映射。
3.7.2. 查询数据
3.7.2.1. 普通查询
Bean直接提供了一系列的find方法用于查询数据,返回的List或Bean可直接返回给前台
new Bean().dao().find*(sql,params)
3.7.2.2. 分页查询:
Bean直接提供了一系列的paginate方法用于进行分页数据查询,返回的List或Bean可直接返回给前台
new Bean().dao().paginate*(sql,params)
3.7.2.3. 缓存查询:
相比MyBatis:SQL比Mybatis更灵活,和jdbcTemplate相仿,但是比jdbcTemplate多了自动封装返回数据,SQL语句可直接写在代码里面或者使用模板独立于程序,另外JFinal提供封装好的Cache查询接口。
3.7.3. 更新数据
插入数据:bean.save()
修改数据:bean.update()
删除数据:bean.deleteById(id)
相比MyBatis:同”查询数据“。
3.7.4. 通用方式:Db+Record
JFinal提供类似Spring JDBCTemplate的方式,可直接操作数据库,不依赖于具体的Bean,其模式称之为Db+Record
Db:相当于Dao,提供数据访问接口
Record:类似通用的Bean,用于数据承载,数据操作需要通过Db的接口来完成
相比JdbcTemplates:和jdbcTemplate相仿,但是比jdbcTemplate多了自动封装返回数据,提供封装好的Cache查询接口。
3.7.5. 复杂SQL
JFinal利用自带的Template Engine极为简洁的实现了Sql管理功能。
可将复杂SQL独立存储到单独文件中,其使用方式类似于MyBatis,但是更简单。
相比myBatis:和myBatis类似,使用模板语言实现。
3.7.6. 事务(Db.tx)
3.7.6.1. 声明式事务
3.7.6.1.1. 方法声明事务
事务的实现采用拦截器实现,在相关需要事务控制的方法增加”@Before(Tx.class)“即可,另外也提供按照方法名,类名等进行精细控制的事务
3.7.6.1.2. configInterceptor()支持正则表达式和多个Controller&方法控制事务
3.7.6.2. 代码直接处理事务
可添加一段Java代码控制事务:Run()方法里面发生异常,或返回false,事务都会自动回滚
相比spring:基本类似,都是使用AOP的注解和事先配置(JFinal-拦截器;Spring-XML)实现。
3.8. 模板
JFinal在页面端的实现,类似于Freemarker, 据其测试报告,性能比Freemarker&Velocity快很多,没有深入研究。
3.9. EHCachePlugin
提供的EHCache插件,可提供如下缓存机制:
1)CacheInterceptor提供方法级别的拦截器, 对Controller的某个Action进行Cache
2)CacheKit提供工具级别的缓存工具,get/put进行缓存数据的管理
3.10. RedisPlugin
提供RedisPlugin在JFinalConfig注册完毕后即可使用Redis类进行Redis缓存操作,后台使用jedis客户端实现和redis服务器交互
3.11. 任务调度Cron4jPlugin
任务调度使用Cron4J, 你也可实现自己的任务Plugin,例如quartz,JFinal使用Cron4j的原因应该是Cron4J的小巧和不依赖于任何第三方框架
如果使用分布式的任务调度,则需要使用更复杂的任务调度框架来完成,例如Quartz,Elastic-Job
3.12. 数据验证Validator
Validator可对Controler某个Action的数据进行验证,其实现原理就是拦截器,在Controller的方法上面增加@Before注解即可实现
l 首先继承Validator定义自己基于某个Controller的验证代码,例如EmployeeValidator.java
l 使用@Before(EmployeeValidator.class)修饰相关Controller的action方法
4. 业务场景测试
4.1. 开发新功能(前台+后台)-产品管理-单表
1. 根据需求进行分析,建立数据库表goods
2. 规划JFinal程序:
包名:com.demo.base.goods
类:Goods,GoodsControler,GoodsService,GoodsValidator
Web目录:base/goods
3. 使用_JFinalDemoGenerator从数据库生成BaseGoods,Goods
4. 建立相关的Controller和前端页面(jsp或者相关模板)
4.2. 后台接口(获取警报)
1. 规划JFinal程序:
包名:com.demo.ded.api.alert
类:Alert,AlertController
2. 规划JFinal程序:
包名:com.demo.api.alert
类:Alert,AlertControler
对外接口路径:”/alert/get/#”,其中”#“为相关的alert id
3. 从Model继承,建立Alert类
4. 对AlertControler进行编码,增加get方法,使用renderJson()返回数据
4.3. 增加一个字段
1. 修改数据库表结构,增加字段
2. SQL中增加此列名
3. 如果有页面,页面中增加此列的显示
SSM:除了以上工作还需要增加:修改Bean,修改Bean字段映射XML
4.4. 增加一个业务接口
Controller中增加接口即可,如以Service为业务逻辑点,则还需要在Service中增加接口
SSM:需要增加接口道Controller,Service,ServiceImpl,Dao,MyBatisMappingXML
4.5. 修改业务逻辑
直接修改相关Controller或Service层的代码
5. 研究结果
根据不同技术的自身特点,列出研究结果
5.1. 优点
5.1.1. 使用简单,上手快
因为不涉及太多复杂的概念,没有那么多XML的配置项,不管有没有Spring基础,即使是新手,上手也会很快
5.1.2. 后期维护简单
后期增加或修改业务逻辑的时候,代码修改简单
5.1.3. 运行速度快
号称“jfinal 性能是 spring + mybatis 的 4.56 倍”
https://www.oschina.net/news/90815/jfinal-3-3
https://gitee.com/jfinal/jfinal-performance
5.1.4. 扩展性好
采用plugin机制
根据标准接口,扩展相关插件
5.2. 缺点
5.2.1. 插件没有Spring丰富,有些情况需要自己开发插件对接第三方开源插件
5.2.2. JFinal周边生态链框架弱
周边已经形成一定数量的外围框架,但是不如Spring丰富,选择性较少
5.2.3. 缺乏大项目案例,虽然架构上理论可支持,但是缺乏公开的实践
6. 公司可利用范围
6.0.1. 一些小型项目可以使用,尤其是时间比较紧张的时候,不过仍需对其进行一定的包装,例如类似jeecg的代码生成机制
6.0.2. 大型项目目前不宜使用,可在一些小型项目的经验基础上,逐渐推广使用
有一些地方有所误解,这类总结其实是一个相当庞大的话题,时间关系,在此仅作一个极小的补充:
0:最最重要一点就是:一定要有业务层、一定要有业务层、一定要有业务层。就算业务再简单,项目再小,也一定要有业务层。jfinal 官网的 demo 都是有业务层的。
jfinal 作为一个框架存在,本质目标就是将用户尽可能从具体技术中解放出来,尽可能将精力放在业务功能的实现上。 如果连业务层都没有,那可是极大远离了框架存在的目标。
是否存在业务层不是框架可以限定死的,在使用 Spring 的时候,你也可以将业务写在 controller 中,这个是 spring 永远无法强制你不去这样做的。
所以 "jfinal 没有明确的提出Service层" 这个不能成为一个问题,因为没法控制用户是否要将业务写在 controller 这个行为
1:IOC 容器本质是用于对目标对象注入代理类,从而实现 AOP。而 jfinal 没有 IOC 却更简洁实现了 AOP,所以没有 IOC 其实是一个优点,而不是缺点。这个是 jfinal 有意为之的。如无必要,勿增实体这个是设计的基本原则
2:jfinal 建议 Model 是贫血领域模型,也就是 Model 只承载数据,不要写任何业务,所有业务都要放在业务层,无论你的项目有多少都要这样做。
当然,如果开发者将 Model 用成充血领域模型,站在 jfinal 的角度是无法干预到的,就像是开发者在使用 Spring 的时候将 Model/Bean 用成充血领域模型,一样也无法干预。 是否充血与贫血这个完全取决于开发者自己怎么去用
3:最后一个重要问题就是 jfinal 非常适合在大型中使用,我跟我同事用 jfinal 开发的项目规模已经相当大,比起先前的 SSM 版本,20 台机器减少到 12 台,跑了超过三年,跑得又快又稳
记住:简洁的力量是无比强大的