Jfinal——实践出真知
什么是Jfinal?
JFinal 是基于 Java 语言的极速 WEB + ORM 框架,其核心设计目标是开发迅速、代码量少、学习简单、功能强大、轻量级、易扩展、Restful。在拥有Java语言所有优势的同时再拥有ruby、python、php等动态语言的开发效率!
简单来说,Jfinal是一套基于微内核的快速开发框架。给开发者提供更高效、易用的工具框架。
Jfinal的特点:
- MVC架构,设计精巧,使用简单
- 遵循COC原则,零配置,无xml
- 独创Db + Record模式,灵活便利
- ActiveRecord支持,使数据库开发极致快速
- 极简、高性能Template Engine,十分钟内掌握基本用法
- 自动加载修改后的java文件,开发过程中无需重启web server
- AOP支持,拦截器配置灵活,功能强大
- Plugin体系结构,扩展性强
- 多视图支持,支持FreeMarker、JSP、Velocity
- 强大的Validator后端校验功能
- 功能齐全,拥有struts2的绝大部分功能
- 体积小仅580K
为什么要写这篇博客:
写这篇博客的目的呢,其实很简单。
其一:官方的文档不具连贯性,都是某某API的用法和介绍,小白的话读了文档可能还是两脸懵逼😂(其实笔者就是这个小白);俗话说纸上得来终觉浅嘛,所以自己有思路以后希望写一篇以 用户注册功能 为需求的Jfinal的搭建、入门、和简单的开发流程的一个教程;希望可以帮到像我一样的孩纸。
其二:于自己来说是一个总结,可以用作为一个初步掌握Jfinal的视角去重新的审视自己的学习过程。
其三:装个X嘛🤪
开始自己的Demo:
好了,前面废话了那么多。对Jfinal有了初步的了解后,下面咱们开干吧:(开发环境为:IDEA For Mac,项目管理工具使用Maven)
1. 首先,使用maven创建一个web项目,如图:
在pom.xml中添加jar包依赖,其中jfinal相关的两个是必须的,junit作为单元测试,最后两个用来连接数据库
<dependencies> <!--单元测试Junit--> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>3.8.1</version> <scope>test</scope> </dependency> <!-- Jfinal相关(jfinal、jetty-server) --> <dependency> <groupId>com.jfinal</groupId> <artifactId>jfinal</artifactId> <version>3.3</version> </dependency> <dependency> <groupId>com.jfinal</groupId> <artifactId>jetty-server</artifactId> <version>8.1.8</version> </dependency> <!--阿里的数据库连接池--> <dependency> <groupId>com.alibaba</groupId> <artifactId>druid</artifactId> <version>1.1.6</version> </dependency> <!-- mysql的连接驱动 --> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>5.1.18</version> </dependency> </dependencies>
配置完成后,我们还需要更改下项目的目录结构然后 再来使用jfinal。
- 将WEB-INF下的除web.xml以外的文件删除
- 在src/main下创建一个java文件夹
- 将java设为code source
- 可选(有需要的可以更改编译的输出目录,idea默认在target/classes下)
- 将webapp改名为WebRoot
- 在src/main/java下建立自己的package
更改完成后,如图:
2.项目创建完成,接下来我们来引入Jfinal。
首先,在package下建一个jfinal_config子包,然后创建一个类AppConfig来继承JFinalConfig类来完成初步设置,如下:
public class AppConfig extends JFinalConfig { @Override public void configConstant(Constants constants) { } @Override public void configRoute(Routes routes) { } @Override public void configPlugin(Plugins plugins) { } @Override public void configEngine(Engine engine) { } @Override public void configInterceptor(Interceptors interceptors) { } @Override public void configHandler(Handlers handlers) { } }
继承JFinalConfig类需要重写这七个方法,具体方法意义,自行参考文档,贴图简示
设置开发模式
设置路由
设置插件
3. 然后,我们在package下再新建一个子包controller,在controller包下新建RegisterController类继承Controller,如下:
public class RegisterController extends Controller { /** * index方法为 未指定具体方法层面的接口时 默认访问的方法 * */ public void index(){ //返回一个视图 render("register.jsp"); } /** * 注册表单提交接口 */ public void addUser(){ } }
写完controller,我们需要配置路由使得用户可以通过url来映射到我们对应的controller,在此之前我们先要在WEB-INF下新建一个文件夹views作为前端视图的根目录。
然后在views下新建一个register.jsp来展示我们的注册页. 完成后 如下:
这里的jsp名称,要与我们controller的index方法 返回的视图名称相一致。
以上的操作完成后,我们去AppConfig中配置一下路由映射
具体的路由用法,大家参考官方文档,本篇主要阐述宏观的思路。
3. 接下来,在web.xml中配置下我们的自定义的Config。
4. 然后,我们在AppConfig中添加main方法来启动项目、测试 :
打开浏览器,输入localhost:8088测试(😅 不会前端,所以现学了一下,做的不好看,轻喷)
5. ORM层配置
- 在package下创建子包model
- 在model下创建数据库映射实体类
- 在resources文件夹下新建db.properties,用来保存相关数据库连接参数
- 在AppConfig的configConstant()方法内 加载配置文件
- 在AppConfig的configPlugin()方法内配置数据库插件(Druid、mysql-connect ...)
具体操作因业务/情况而定,我的配置如下:
这是model层的User类,对应数据库的user表
这是 db.properties
jdbcUrl=jdbc:mysql://localhost:3306/JfinalDemoDB?characterEncoding=utf8&zeroDateTimeBehavior=convertToNull username=root password=admin showSql=true
在configConstant()方法中默认读取db.properties文件
然后在configPlugin()方法中 使用数据库相关组件:
@Override public void configPlugin(Plugins plugins) { //通过读取db.properties来初始化druid插件 DruidPlugin druidPlugin = new DruidPlugin(getProperty("jdbcUrl"),getProperty("username"), getProperty("password")); //添加该插件 plugins.add(druidPlugin); //将该插件作为使用的 持久层框架 ActiveRecordPlugin activeRecordPlugin = new ActiveRecordPlugin(druidPlugin); activeRecordPlugin.addMapping("user",User.class).setShowSql(Boolean.parseBoolean(getProperty( "showSql" ))); plugins.add(activeRecordPlugin); }
在package下新建一个test子包,测试orm层配置是否生效。
测试发现,报空指针异常,那么通过检查,应该是orm层插件在junit环境下不能生效,所以我们采用手动配置数据库插件方式来实现 junit 的orm层的单元测试。
在package下新建一个utils子包,然后在utils中新建一个类ManualOrmMap 来配置相关参数。(其实就是把 AppConfig的configPlugin()中的配置单独拿出来)
************这是个大坑*********** 代码如下:
public class ManualOrmMap { protected static DruidPlugin dp; protected static ActiveRecordPlugin arp = null; @BeforeAll static void initAll(){ if(dp==null){ PropKit.use("db.properties"); dp = new DruidPlugin(PropKit.get("jdbcUrl"),PropKit.get("username"), PropKit.get("password")); dp.start(); } if (arp==null) { arp = new ActiveRecordPlugin(dp); // 打印sql语句 //arp.setShowSql(true); // 数据库映射 arp.addMapping("user",User.class).setShowSql(Boolean.parseBoolean(PropKit.get( "showSql" ))); arp.start(); } //System.out.println("Begin..."); } @AfterAll static void tearDownAll() { //System.out.println("...End"); } }
让测试类继承自ManualOrmMap类,再次测试:
问题解决。
6. 既然orm层的配置没有问题了,下面我们来实现具体的业务逻辑
首先,在utils包下新建一个Result类,用来作为前后端的数据交互格式,如下:
然后在package下新建子包service,然后新建类RegisterService。(Service层具体的处理业务流程)
public class RegisterService { //User dao private User userDao = new User().dao(); //对应RegisterController的addUser方法 public Result addUser(String username, String password, String email){ Result result = new Result(); if(username != null && !username.equals("") && password != null && !password.equals("") && email != null && !email.equals("")){ // List<User> checkUser = userDao.find("select * from user where username = ?",username); System.out.println(" >>>>> " + checkUser); if(checkUser != null && !checkUser.isEmpty()){ System.out.println("账号已存在"); result.setStatus("1"); result.setMessage("该账号已存在"); return result; } //执行保存 System.out.println("开始保存用户信息"); User user = new User(); user.set("username",username); user.set("password",password); user.set("email",email); user.save(); result.setStatus("0"); result.setMessage("创建账户成功"); result.setData(user); return result; }else { result.setStatus("1"); result.setMessage("表单未完整填写"); return result; } } }
OK,上述操作完成后,我们来使用junit测试一下,同样测试类需要继承ManualOrmMap类,测试结果如下:
那么。service层测试完毕后,我们开始最后的整合。
7. 整合controller完成项目
为了防止同学们存在疑惑,下面贴一下,前端代码(写的比较乱,将就看):
<html> <head> <title>Jfinal Demo</title> <meta charset="utf-8"> <meta name="viewport" content="width=device-width, initial-scale=1"> <link rel="stylesheet" href="http://cdn.static.runoob.com/libs/bootstrap/3.3.7/css/bootstrap.min.css"> <script src="http://cdn.static.runoob.com/libs/jquery/2.1.1/jquery.min.js"></script> <script src="http://cdn.static.runoob.com/libs/bootstrap/3.3.7/js/bootstrap.min.js"></script> <style> body{ background: linear-gradient(45deg,#BBDEFB,#F0F4C3); } </style> <script> $(document).ready(function(){ $("#sub").click(function(){ //测试 console.log($("#register").serialize()); $.ajax({url:"http://localhost:8088/addUser", data:$("#register").serialize(), success:function(result){ if(result.status == "0"){ $('#myModal').modal({ keyboard: true }); $(".modal-body").text(result.message); }else { $('#myModal').modal({ keyboard: true }); $(".modal-body").text(result.message); } }}); }); }); </script> </head> <body> <br> <h2 style="text-align: center;color: #B39DD8;">Welcome To Jfinal For OceanLi,Please Register</h2> <br> <div class="container" style="margin: 0 auto;width: 60%"> <div class="jumbotron" style="background-color: #FAFAFA;opacity: 0.6"> <div > <form id="register" role="form"> <div class="form-group"> <label for="register" style="text-align: center">名称</label> <input type="text" class="form-control" name="username" placeholder="请输入名称"> </div> <div class="form-group"> <label for="register" style="text-align: center">密码</label> <input type="password" class="form-control" name="password" placeholder="请输入密码"> </div> <div class="form-group"> <label for="register" style="text-align: center">邮箱</label> <input type="email" class="form-control" name="email" placeholder="请输入邮箱"> </div> <div style="margin: 0 auto;text-align: center"> <button id="sub" type="button" class="btn btn-success btn-block" style="width: 60%;margin: 0 auto">提交注册</button> </div> </form> </div> </div> </div> <!-- 模态框(Modal) --> <div class="modal fade" id="myModal" tabindex="-1" role="dialog" aria-labelledby="myModalLabel" aria-hidden="true"> <div class="modal-dialog"> <div class="modal-content"> <div class="modal-header"> <button type="button" class="close" data-dismiss="modal" aria-hidden="true">× </button> <h4 class="modal-title" id="myModalLabel"> 注册结果: </h4> </div> <div id="dialog_content" class="modal-body"> 注册结果data.message </div> <div class="modal-footer"> <button type="button" class="btn btn-default" data-dismiss="modal">关闭 </button> </div> </div><!-- /.modal-content --> </div><!-- /.modal-dialog --> </div><!-- /.modal --> </body> </html>
8. 最后,我们来启动服务器,最终测试一下:
测试前,user表中的数据为空
start:
我们再来检查一下数据库:
完成。
测试下重复提交的情况:
数据库并没有插入重复数据
9.最后的最后,贴给大家数据库的sql脚本。
SET NAMES utf8mb4; SET FOREIGN_KEY_CHECKS = 0; -- ---------------------------- -- Table structure for `user` -- ---------------------------- DROP TABLE IF EXISTS `user`; CREATE TABLE `user` ( `id` int(11) NOT NULL AUTO_INCREMENT, `username` varchar(255) DEFAULT NULL, `password` varchar(255) DEFAULT NULL, `email` varchar(255) DEFAULT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB AUTO_INCREMENT=36 DEFAULT CHARSET=utf8; SET FOREIGN_KEY_CHECKS = 1;