Controller(下)
renderFile 文件下载
- renderFile 基本用法
- renderFile 方法使用一个 baseDownloadPath 参数为基础路径去寻找文件。以标准的 maven 项目为例,该参数默认值指向目录:src/main/webapp/download,示例如下:
// 最终下载文件为:src/main/webapp/download/file.zip renderFile("file.zip"); // 最终下载文件为:src/main/webapp/download/abc/def/file.zip renderFile("abc/deb/file.zip"); ```
- renderFile 方法使用一个 baseDownloadPath 参数为基础路径去寻找文件。以标准的 maven 项目为例,该参数默认值指向目录:src/main/webapp/download,示例如下:
- 配置 baseDownloadPath
- baseDownloadPath 可以在 configConstant(Constants me) 中自由配置,方法如下:
me.setBaseDownloadPath("files");
- 以标准的 maven 项目为例,以上配置的 baseDonwnloadPath 值将指向目录 src/main/webapp/files。
- 还可以将 baseDownloadPath 配置为绝对路径,那么该路径将跳出项目之外
// linux、mac 系统以字符 "/" 打头是绝对路径 me.setBaseDownloadPath("/var/download"); // windows 系统以盘符打头也是绝对路径 me.setBaseDownloadPath("D:/download");
- baseDownloadPath 可以在 configConstant(Constants me) 中自由配置,方法如下:
- renderFile(File file)
- renderFile(File file) 方法直接使用 File 参数去获取下载文件,可脱离 baseDownloadPath 的束缚,指向任意地点的文件.例如:
String file = "D:/my-project/share/files/jfinal-all.zip"; renderFile(new File(file));
- 为下载文件重新命名
renderFile("老文件名.txt", "新文件名.txt");
- renderFile(File file) 方法直接使用 File 参数去获取下载文件,可脱离 baseDownloadPath 的束缚,指向任意地点的文件.例如:
renderQrCode 二维码生成
- 实例如下
// 二维码携带的数据 String data = "weixin://wxpay/bizpayurl?appid=xx&mch_id=xx......"; // 渲染二维码图片,长度与宽度为 200 像素, M为二维码纠错级别 renderQrCode(data, 200, 200, 'M');
- 纠错参数可以在二维码图片被遮挡或者被损坏一部分时仍然可以正确读取其中的内容。
- 纠错级别从高到低可以指定为:‘H’、‘Q’、‘M’、‘L’,其纠错率分别为:30%、25%、15%、7%。 不指定该参数值默认为 ‘L’。
- maven 依赖
<dependency> <groupId>com.google.zxing</groupId> <artifactId>javase</artifactId> <version>3.2.1</version> </dependency>
session 操作
- 通过 setSessionAttr(key, value) 可以向 session 中存放数据,getSessionAttr(key) 可以从 session 中读取数据。还可以通过 getSession()得到 session 对象从而使用全面的session API。
getfile
- Controller提供了getFile系列方法支持文件上传。
- maven依赖
<dependency> <groupId>com.jfinal</groupId> <artifactId>cos</artifactId> <version>2017.5</version> </dependency>
- 如果客户端请求为multipart request(form表单使用了enctype=“multipart/form-data”),那么必须先调用getFile系列方法才能使getPara系列方法正常工作,因为multipart request需要通过getFile系列方法解析请求体中的数据,包括参数。同样的道理在Interceptor、Validator中也需要先调用getFile。
- 文件默认上传至项目根路径下的upload子路径之下,该路径称为文件上传基础路径。可以在 JFinalConfig.configConstant(Constants me)方法中通过me.setBaseUploadPath(baseUploadPath) 设置文件上传基础路径,该路径参数接受以”/”打头或者以windows磁盘盘符打头的绝对路径,即可将基础路径指向项目根径之外。当该路径参数设置为相对路径时,则是以项目根为基础的相对路径。
keep 系方法
- keepPara
- 不带参的 keepPara() 方法将保持住所有表单域的内容
- 以keepPara()保持住的参数返回页面时,无论是什么类型都将转换成 String 类型,所以,如果表单域的类型必须要保持住的话可以使用如下的方式
// 指定 keep 后的类型为 Date keepPara(Date.class, "createAt"); // 指定 keep 后的类型为 Integer keepPara(Integer.class, "age");
- keepPara 一般用在 Validator 或者拦截器之中
- keepModel 与 keepBean
- keepModel 可以将以 modelName 前缀的表单域保持住内容与类型,例如:
<input name="blog.title" value="#(blog.title ??)"/> <input name="blog.content" value="#(blog.content ??)" />
- 如上所示,表单域是以前缀为 blog 的 model,提交到后端是通过 getModel 来接收数据,如果提交的数据不完整或者有错误可以使用 keepModel 保持住内容返回给页面,让用户继续填写。
AOP
Interceptor
-
Interceptor 可以对方法进行拦截,并提供机会在方法的前后添加切面代码,实现 AOP 的核心目标。Interceptor 接口仅仅定义了一个方法 public void intercept(Invocation inv)。示例:
public class DemoInterceptor implements Interceptor { public void intercept(Invocation inv) { System.out.println("Before method invoking"); inv.invoke(); System.out.println("After method invoking"); } }
以上代码中的 DemoInterceptor 将拦截目标方法,并且在目标方法调用前后向控制台输出文本。inv.invoke() 这一行代码是对目标方法的调用,在这一行代码的前后插入切面代码可以很方便地实现AOP。
-
注意:必须调用 inv.invoke() 方法,才能将当前调用传递到后续的 Interceptor 与 Action。如果刻意不去调用剩下的拦截器与 action,这种情况仍然需要使用 inv.getController().render()。
- 全局共享,注意线程安全问题
Before
- Before注解用来对拦截器进行配置,该注解可配置Class、Method级别的拦截器,以下是代码示例
// 配置一个Class级别的拦截器,她将拦截本类中的所有方法
@Before(AaaInter.class)
public class BlogController extends Controller {
// 配置多个Method级别的拦截器,仅拦截本方法
@Before({BbbInter.class, CccInter.class})
public void index() {
}
// 未配置Method级别拦截器,但会被Class级别拦截器AaaInter所拦截
public void show() {
}
}
- 如上代码所示,Before可以将拦截器配置为Class级别与Method级别,前者将拦截本类中所有方法,后者仅拦截本方法。此外Before可以同时配置多个拦截器,只需用在大括号内用逗号将多个拦截器进行分隔即可。
- 除了 Class 与 Method 级别的拦截器以外,JFinal 还支持全局拦截器以及 Routes 拦截器,全局拦截器分为控制层全局拦截器与业务层全局拦截器,前者拦截控制层所有 Action 方法,后者拦截业务层所有方法。
- 全局拦截器需要在 YourJFinalConfig 进行配置,以下是配置示例
public class YourJFinalConfig extends JFinalConfig { public void configInterceptor(Interceptors me) { // 添加控制层全局拦截器 me.addGlobalActionInterceptor(new GlobalActionInterceptor()); // 添加业务层全局拦截器 me.addGlobalServiceInterceptor(new GlobalServiceInterceptor()); } }
Clear
- 拦截器从上到下依次分为Global、Routes、Class、Method四个层次,Clear用于清除自身所处层次以上层的拦截器。
- clear不带参数时清除所有拦截器,带参时清除参数指定的拦截器
- 在某些应用场景之下,需要移除Global或Class拦截器。例如某个后台管理系统,配置了一个全局的权限拦截器,但是其登录action就必须清除掉她,否则无法完成登录操作,以下是代码示例:
Clear注解带有参数时,能清除指定的拦截器,以下是一个更加全面的示例: @Before(AAA.class) public class UserController extends Controller { @Clear @Before(BBB.class) public void login() { // Global、Class级别的拦截器将被清除,但本方法上声明的BBB不受影响 } @Clear({AAA.class, CCC.class})// 清除指定的拦截器AAA与CCC @Before(CCC.class) public void show() { // 虽然Clear注解中指定清除CCC,但她无法被清除,因为清除操作只针对于本层以上的各层 } } // 上面的清除都用在了 method 上,还可以将其用于 class 之上 @Clear(AAA.class) public class UserController { public void index() { ... } }
Inject 依赖注入
- 使用 @Inject 注解可以向 Controller 以及 Interceptor 中注入依赖对象,使用注入功能需要如下配置
public void configConstant(Constants me) { // 开启对 jfinal web 项目组件 Controller、Interceptor、Validator 的注入 me.setInjectDependency(true); // 开启对超类的注入。不开启时可以在超类中通过 Aop.get(...) 进行注入 me.setInjectSuperClass(true); }
- 如果需要创建的对象并不是 Controller 的属性,也不是 Interceptor 的属性,还可以使用 Aop.get(…) 方法进行依赖对象的创建以及注入,例如:
public class MyKit { static Service service = Aop.get(Service.class); public void doIt() { service.justDoIt(); } }
Aop 工具
- APO
- get(…)
Aop.get(…) 可以在任意时空创建对象并且对其进行依赖注入,例如:Service service = Aop.get(Service.class);
- inject(…)
Aop.inject(…) 可以在任意时空对目标对象进行注入,该方法相对于 Aop.get(…) 方法少一个对象创建功能:Service service = new Service(...); Aop.inject(service);
- get(…)
- addSingletonObject(…)
由于 Aop 创建对象时不支持为构造方法传递参数,所以还需提供 addSingletonObject(…) 添加单例对象:
// Service 类的构造方法中传入了两个参数
Service service = new Service(paraAaa, paraBbb);
AopManager.me().addSingletonObject(service);
// 上面代码添加完成以后,可以在任何地方通过下面的方式获取单例对象:
// 获取时使用单例对象
service = Aop.get(Service.class);
// 注入时也可以使用前面配置的单例对象
@Inject
Service service;
// 在添加为单例对象之前还可以先为其注入依赖对象:
Service service = new Service(paraAaa, paraBbb);
// 这里是对 Service 进行依赖注入
Aop.inject(service);
// 为单例注入依赖以后,再添加为单例供后续使用
AopManager.me().addSingletonObject(service);
- Routes 级别拦截器
- Routes级别拦截器是指在Routes中添加的拦截器,如下是示例:
/** * 后端路由 */ public class AdminRoutes extends Routes { public void config() { // 此处配置 Routes 级别的拦截器,可配置多个 addInterceptor(new AdminAuthInterceptor()); add("/admin", IndexAdminController.class, "/index"); add("/admin/project", ProjectAdminController.class, "/project"); add("/admin/share", ShareAdminController.class, "/share"); } }
- 在上例中,AdminAuthInterceptor 将拦截IndexAdminController、ProjectAdminController、ShareAdminController 中所有的 action 方法。
- Routes 级别拦截器将在 Class 级别拦截器之前被调用。
- Routes级别拦截器是指在Routes中添加的拦截器,如下是示例:
ActiveRecordPlugin
ActiveRecordPlugin
- ActiveRecord是作为JFinal的Plugin而存在的,所以使用时需要在JFinalConfig中配置ActiveRecordPlugin。配置示例:
public class DemoConfig extends JFinalConfig { public void configPlugin(Plugins me) { DruidPlugin dp = new DruidPlugin("jdbc:mysql://localhost/db_name", "userName", "password"); me.add(dp); ActiveRecordPlugin arp = new ActiveRecordPlugin(dp); me.add(arp); arp.addMapping("user", User.class); arp.addMapping("article", "article_id", Article.class); } }
- ActiveReceord中定义了addMapping(String tableName, Class<? extends Model> modelClass>)方法,该方法建立了数据库表名到Model的映射关系。
- 代码中arp.addMapping(“user”, User.class),表的主键名为默认为 “id”,如果主键名称为 “user_id” 则需要手动指定,如:arp.addMapping(“user”, “user_id”, User.class)。
Model
- Model是ActiveRecord中最重要的组件之一,它充当MVC模式中的Model部分。以下是Model定义示例代码
public class User extends Model<User> { public static final User dao = new User().dao(); }
- Model的一些常见用法
// 创建name属性为James,age属性为25的User对象并添加到数据库 new User().set("name", "James").set("age", 25).save(); // 删除id值为25的User User.dao.deleteById(25); // 查询id值为25的User将其name属性改为James并更新到数据库 User.dao.findById(25).set("name", "James").update(); // 查询id值为25的user, 且仅仅取name与age两个字段的值 User user = User.dao.findByIdLoadColumns(25, "name, age"); // 获取user的name属性 String userName = user.getStr("name"); // 获取user的age属性 Integer userAge = user.getInt("age"); // 查询所有年龄大于18岁的user List<User> users = User.dao.find("select * from user where age>18"); // 分页查询年龄大于18的user,当前页号为1,每页10个user Page<User> userPage = User.dao.paginate(1, 10, "select *", "from user where age > ?", 18);
以上内容来自官方文档JFinal开发教程,个人仅做简单整理。