JFinalConfig
基于JFinal的web项目需要创建一个继承自JFinalConfig类的子类,该类用于对整个web项目进行配置,JFinalConfig子类需要实现六个抽象方法,如下所示:
public class DemoConfig extends JFinalConfig {
public void configConstant(Constants me) {}
public void configRoute(Routes me) {}
public void configEngine(Engine me) {}
public void configPlugin(Plugins me) {}
public void configInterceptor(Interceptors me) {}
public void configHandler(Handlers me) {}
}
1. configConstant()
- 常用配置
public void configConstant(Constants me) {
// 配置开发模式,true 值为开发模式
//而 me.setDevMode(...) 是设置 jfinal 的开发模式,true 为开发模式,false 为生产模式,开发模式用于开发过程,此配置有助于开发工作,例如会在控制台输出 jfinal action report。 false 用于生产环境,性能会最大化。
me.setDevMode(true);
// 配置 aop 代理使用 cglib,否则将使用 jfinal 默认的动态编译代理方案
me.setToCglibProxyFactory();
// 配置依赖注入
me.setInjectDependency(true);
// 配置依赖注入时,是否对被注入类的超类进行注入
me.setInjectSuperClass(false);
// 配置为 slf4j 日志系统,否则默认将使用 log4j
// 还可以通过 me.setLogFactory(...) 配置为自行扩展的日志系统实现类
me.setToSlf4jLogFactory();
// 设置 Json 转换工厂实现类,更多说明见第 12 章
me.setJsonFactory(new MixedJsonFactory());
// 配置视图类型,默认使用 jfinal enjoy 模板引擎
me.setViewType(ViewType.JFINAL_TEMPLATE);
// 配置基础下载路径,默认为 webapp 下的 download
me.setBaseDownloadPath(...);
// 配置基础上传路径,默认为 webapp 下的 upload
me.setBaseUploadPath(...);
// 配置 404、500 页面
me.setError404View("/common/404.html");
me.setError500View("/common/500.html");
}
- 其他配置
public void configConstant(Constants me) {
// 配置 encoding,默认为 UTF8
me.setEncoding("UTF8");
// 配置 json 转换 Date 类型时使用的 data parttern
me.setJsonDatePattern("yyyy-MM-dd HH:mm");
// 配置是否拒绝访问 JSP,是指直接访问 .jsp 文件,与 renderJsp(xxx.jsp) 无关
me.setDenyAccessJsp(true);
// 配置上传文件最大数据量,默认 10M
me.setMaxPostSize(10 * 1024 * 1024);
// 配置验证码缓存 cache,配置成集中共享缓存可以支持分布式与集群
me.setCaptchaCache(...);
// 配置 urlPara 参数分隔字符,默认为 "-"
me.setUrlParaSeparator("-");
}
2. configRoute()
- 常用配置
public void configRoute(Routes me) {
// 如果要将控制器超类中的 public 方法映射为 action, 配置成 true,一般不用配置
me.setMappingSuperClass(false);
// 配置 baseViewPath,可以让 render(...) 参数省去 baseViewPath 这部分前缀,之后render()方法参数可以直接写终点文件,隐去了目录结构。
me.setBaseViewPath("/view");
// 配置作用于该 Routes 对象内配置的所有 Controller 的拦截器
me.addInterceptor(new FrontInterceptor());
// 添加路由,第一个参数是,第一个参数是controllerKey,该字符串唯一对应一个Controller,controllerKey仅能定位到Controller,第二个参数是对应的Controller,第三个参数对应的是该Controller返回的视图的相对路径,当viewpath未指定时显示默认值为controllerKey
me.add("/hello", HelloController.class);
}
Routes.setBaseViewPath(baseViewPath) 方法用于为该 Routes 内部的所有 Controller 设置视图渲染时的基础路径,该基础路径与Routes.add(…, viewPath) 方法传入的viewPath以及 Controller.render(view) 方法传入的 view 参数联合组成最终的视图路径,规则如下:
finalView = baseViewPath + viewPath + view
注意:当Render()方法中的参数view以 “/” 字符打头时表示绝对路径,baseViewPath 与 viewPath 将被忽略。例如“/hello/index.html”
- 路由配置 API
Routes 类中添加路由的方法有两个:
public Routes add(String controllerKey, Class<? extends Controller> controllerClass, String viewPath)
public Routes add(String controllerKey, Class<? extends Controller> controllerClass)
- 极简路由配置
- ActionKey注解
public class UserController extends Controller {
@ActionKey("/login")
public void login() {
render("login.html");
}
}
JFinal在原有的路由规则基础之外害提供了ActionKey注解,可以打破原有规则
假定 UserController 的 controllerKey值为“/user”,在使用了 @ActionKey(“/login”) 注解以后,actionKey 由原来的 “/user/login” 变为了“/login”。该注解还可以让actionKey中使用减号或数字等字符,如“/user/123-456”。
如果 JFinal 默认路由规则不能满足需求,开发者还可以根据需要使用Handler定制更加个性化的路由,大体思路就是在 Handler 中改变第一个参数 String target 的值。
3. configEngine
此方法用来配置Template Engine,以下是代码示例:
public void configEngine(Engine me) {
me.addSharedFunction("/view/common/layout.html");
me.addSharedFunction("/view/common/paginate.html");
me.addSharedFunction("/view/admin/common/layout.html");
}
上面的方法向模板引擎中添加了三个定义了 template function 的模板文件,更详细的介绍详见 Template Engine 那一章节的内容。
4. configPlugin
此方法用来配置JFinal的Plugin,如下代码配置了Druid数据库连接池插件与ActiveRecord数据库访问插件。通过以下的配置,可以在应用中使用ActiveRecord非常方便地操作数据库。
public void configPlugin(Plugins me) {
DruidPlugin dp = new DruidPlugin(jdbcUrl, userName, password);
me.add(dp);
ActiveRecordPlugin arp = new ActiveRecordPlugin(dp);
arp.addMapping("user", User.class);
me.add(arp);
}
5. configInterceptor
- 配置全局拦截器
configInterceptor 方法用来配置全局拦截器,全局拦截器分为两类:控制层、业务层,以下是配置示例:
public void configInterceptor(Interceptors me) {
// 以下两行代码配置作用于控制层的全局拦截器
me.add(new AuthInterceptor());
me.addGlobalActionInterceptor(new AaaInterceptor());
// 以下一行代码配置业务层全局拦截器
me.addGlobalServiceInterceptor(new BbbInterceptor());
}
注意:以上描述中所谓的 "业务层" 本质上是指除了 "控制层" 以外的含义,不一定要求是业务层,也可以是一个除了 controller 以外的任何一个类。
以上方式配置的拦截器可以在方法定义之处通过 @Clear 注解进行移除,具体用法见后续有关 @Clear 注解的章节。
- 拦截器配置层次/粒度
Interceptor 配置粒度分为 Global、Routes、Class、Method 四个层次,其中以上小节配置粒度为全局。Routes、Class 与 Method 级的配置将在后续章节中详细介绍。
6. configHandler(..)
此方法用来配置JFinal的Handler,如下代码配置了名为ResourceHandler的处理器,Handler可以接管所有web请求,并对应用拥有完全的控制权,可以很方便地实现更高层的功能性扩展。
public void configHandler(Handlers me) {
me.add(new ResourceHandler());
}
注意:Handler 是全局共享的,所以要注意其中声明的属性的线程安全问题
7. onStart()、onStop() 回调配置
在
JFinalConfig 继承类
中可以添加 onStart() 与 onStop(),JFinal 会在系统启动完成之后以及系统关闭之前分别回调这两个方法:
// 系统启动完成后回调
public void onStart() {
}
// 系统关闭之前回调
public void onStop() {
}
这两个方法可以很方便地在项目启动后与关闭前让开发者有机会进行额外操作,如在系统启动后创建调度线程或在系统关闭前写回缓存。
8. PropKit 读取配置
PropKit工具类用来读取外部键值对配置文件,PropKit可以极度方便地在系统任意时空使用,配置文件的格式如下:
userName=james
email=no-reply@jfinal.com
devMode=true
如下是 PropKit 代码示例:
PropKit.use("config.txt");
String userName = PropKit.get("userName");
String email = PropKit.get("email");
// Prop 配合用法
Prop p = PropKit.use("config.txt");
Boolean devMode = p.getBoolean("devMode");
如下是在项目中具体的使用示例:
public class AppConfig extends JFinalConfig {
public void configConstant(Constants me) {
// 第一次使用use加载的配置将成为主配置,可以通过PropKit.get(...)直接取值
PropKit.use("a_little_config.txt");
me.setDevMode(PropKit.getBoolean("devMode"));
}
public void configPlugin(Plugins me) {
// 非第一次使用use加载的配置,需要通过每次使用use来指定配置文件名再来取值
String redisHost = PropKit.use("redis_config.txt").get("host");
int redisPort = PropKit.use("redis_config.txt").getInt("port");
RedisPlugin rp = new RedisPlugin("myRedis", redisHost, redisPort);
me.add(rp);
// 非第一次使用 use加载的配置,也可以先得到一个Prop对象,再通过该对象来获取值
Prop p = PropKit.use("db_config.txt");
DruidPlugin dp = new DruidPlugin(p.get("jdbcUrl"), p.get("user")…);
me.add(dp);
}
}
如上代码所示,PropKit可同时加载多个配置文件,第一个被加载的配置文件可以使用PorpKit.get(…)方法直接操作,非第一个被加载的配置文件则需要使用PropKit.use(…).get(…)来操作。
PropKit 的使用并不限于在 YourJFinalConfig 中,可以在项目的任何地方使用。此外PropKit.use(…)方法在加载配置文件内容以后会将数据缓存在内存之中,可以通过PropKit.useless(…)将缓存的内容进行清除。
Controller
Controller是JFinal核心类之一,该类作为MVC模式中的控制器。基于JFinal的Web应用的控制器需要继承该类。Controller是定义Action方法的地点,是组织Action的一种方式,一个Controller可以包含多个Action。Controller是线程安全的。
1. Action
- 简单来说就是定义在Controller中的public修饰的方法就是Action
public class HelloController extends Controller {
public void index() {
renderText("此方法是一个action");
}
public String test() {
return "index.html";
}
}
以上就定义了两个Action,HelloController.index()和HelloController.test().
- @NotAction 注解
如果希望Controller中的一个方法不成为Action可以使用@NotAction注释。@NotAction 注解通常用于引入了 BaseController 的中间 Controller,例如:
public class BaseController extends Controller {
// 不希望成为 action,仅供子类调用,或拦截器中调用
@NotAction
public void getLoginUser() {
}
}
2. Action 参数注入
- get/getPara()获取参数
3. getBean / getModel 系列
具体不太懂,先贴一个代码
// 定义Model,在此为Blog
public class Blog extends Model<Blog> {
}
// 在页面表单中采用modelName.attrName形式为作为表单域的name
<form action="/blog/save" method="post">
<input name="blog.title" type="text">
<input name="blog.content" type="text">
<input value="提交" type="submit">
</form>
public class BlogController extends Controller {
public void save() {
// 页面的modelName正好是Blog类名的首字母小写
Blog blog = getModel(Blog.class);
// 如果表单域的名称为 "otherName.title"可加上一个参数来获取
blog = getModel(Blog.class, "otherName");
}
}
4. set / setAttr 方法
查看jfinal源码就是调用了HttpServletRequest的方法
private HttpServletRequest request;
public Controller setAttr(String name, Object value) {
request.setAttribute(name, value);
return this;
}
3.6中为了进一步简化代码用set()替代了setAttr()。
5. render方法
- render(String view)
controller执行结束之后返回给前端显示的view文件,view参数指向的最终文件是
String template = baseView+viewPath+view
当需要打破baseView和viewPath这两个参数的限制时只要让view参数以“/”打头即可。
render("/other_path/my_path/index.html");
- render(String view) 方法配置模板引擎
render(String view) 将根据 configConstant(Constants me) 中配置的 me.setViewType(ViewType) 方法选择一种模板引进渲染模板文件,例如:
public void configConstant(Constants me) {
me.setViewType(ViewType.JFINAL_TEMPLATE);
}
以上配置将选择 jfinal 内置的 enjoy 模板引擎渲染模板,该配置是默认值,在使用时无需配置。注意该配置仅仅针对 Controller.render(String view) 方法,其它 render 系方法完全不受影响。
- render 系列其它方法
render系列方法将渲染不同类型的视图并返回给客户端。JFinal目前支持的视图类型有:JFinal Template、FreeMarker、JSP、Velocity、JSON、File、Text、Html、QrCode 二维码 等等。除了JFinal支持的视图型以外,还可以通过继承Render抽象类来无限扩展视图类型。
通常情况下使用Controller.render(String)方法来渲染视图,使用Controller.render(String)时的视图类型由JFinalConfig.configConstant(Constants constants)配置中的constants. setViewType(ViewType)来决定,该设置方法支持的ViewType有:JFINAL_TEMPLATE、FreeMarker、JSP、Velocity,不进行配置时的缺省配置为JFINAL_TEMPLATE。
此外,还可以通过 constants.setRenderFactory(RenderFactory)来设置Controller中所有render系列方法所使用的Render实现类。
// 渲染名为test.html的视图,且视图类型为 JFinal Template
renderTemplate(”test.html”);
// 生成二维码
renderQrCode("content");
// 渲染名为test.html的视图,且视图类型为FreeMarker
renderFreeMarker(”test.html”);
// 渲染名为test.html的视图,且视图类型为Velocity
renderVelocity(“test.html”);
// 将所有setAttr(..)设置的变量转换成 json 并渲染
renderJson();
// 以 "users" 为根,仅将 userList 中的数据转换成 json 并渲染
renderJson(“users”, userList);
// 将user对象转换成 json 并渲染
renderJson(user);
// 直接渲染 json 字符串
renderJson("{"age":18}" );
// 仅将setAttr(“user”, user)与setAttr(“blog”, blog)设置的属性转换成json并渲染
renderJson(new String[]{"user", "blog"});
// 渲染名为test.zip的文件,一般用于文件下载
renderFile("test.zip");
// 渲染纯文本内容 "Hello JFinal"
renderText("Hello JFinal");
// 渲染 Html 内容 "Hello Html"
renderHtml("Hello Html");
// 渲染名为 test.html 的文件,且状态为 404
renderError(404 , "test.html");
// 渲染名为 test.html 的文件,且状态为 500
renderError(500 , "test.html");
// 不渲染,即不向客户端返回数据
renderNull();
// 使用自定义的MyRender来渲染
render(new MyRender());
- 定制render()方法
// 定制一个 MyRender
public class MyRender extends Render {
...
}
// 扩展 RenderFactory,用于将 Controller.render(String view)
// 切换到自己定制的 MyRender 上去
public class MyRenderFactory extends RenderFactory {
public Render getRender(String view) {
return new MyRender(view);
}
}
// 配置生效
public void configConstant(Constants me) {
me.setRenderFactory(new MyRenderFactory());
}
6. 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");
baseDownloadPaht可以在configConstant中配置,例如
me.setBaseDownloadPath("files");
此外,还可以将 baseDownloadPath 配置为绝对路径,那么该路径将跳出项目之外,例如:
// linux、mac 系统以字符 "/" 打头是绝对路径
me.setBaseDownloadPath("/var/download");
// windows 系统以盘符打头也是绝对路径
me.setBaseDownloadPath("D:/download");
或者
String file = "D:/my-project/share/files/jfinal-all.zip";
renderFile(new File(file));
为下载的文件指定名字
renderFile("老文件名.txt", "新文件名.txt");
7. renderQrCode二维码的生成
- renderQrCode的用法
// 二维码携带的数据
String data = "weixin://wxpay/bizpayurl?appid=xx&mch_id=xx......";
// 渲染二维码图片,长度与宽度为 200 像素
renderQrCode(data, 200, 200);
此外,renderQrCode还可以指定纠错级别例如
// 最后一个参数 'M' 为纠错级别
renderQrCode(data, 200, 200, 'M');
纠错级别从高到低可以指定为:'H'、'Q'、'M'、'L',其纠错率分别为:30%、25%、15%、7%。 不指定该参数值默认为 'L'。
** 使用renderQrCode功能需要导入zxing的maven依赖 **
<dependency>
<groupId>com.google.zxing</groupId>
<artifactId>javase</artifactId>
<version>3.2.1</version>
</dependency>
8. getFile 文件上传
Controller提供了getFile系列方法支持文件上传,如果需要用到文件上传的功能,需要导入maven依赖:
<dependency>
<groupId>com.jfinal</groupId>
<artifactId>cos</artifactId>
<version>2019.8</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磁盘盘符打头的绝对路径,即可将基础路径指向项目根径之外,方便单机多实例部署。当该路径参数设置为相对路径时,则是以项目根为基础的相对路径。
以上是一些常用的功能,根据JFinal官网的文档总结,其他的还有AOP,ActiveRecord
,Enjoy 模板引擎,EhCachePlugin,RedisPlugin,Cron4jPlugin,Validator,国际化,Json 转换,JFinal架构及扩展
等后面用到整理。
鸽 ~~