1、常用配置

此方法用来配置访问路由,如下代码配置了将 “/hello” 映射到HelloController这个控制器,通过以下的配置,http://localhost/hello 将访问 HelloController.index() 方法,而http://localhost/hello/methodName 将访问到 HelloController.methodName() 方法。

  1. public void configRoute(Routes me) {
  2. // 如果要将控制器超类中的 public 方法映射为 action 配置成 true,一般不用配置
  3. me.setMappingSuperClass(false);
  4. // 配置 baseViewPath,可以让 render(...) 参数省去 baseViewPath 这部分前缀
  5. me.setBaseViewPath("/view");
  6. // 配置作用于该 Routes 对象内配置的所有 Controller 的拦截器
  7. me.addInterceptor(new FrontInterceptor());
  8. // 路由扫描,jfinal 4.9.03 新增功能。参数 "com.xxx." 表示扫描被限定的包名,
  9. // 扫描仅会在该包以及该包的子包下进行
  10. me.scan("com.xxx.");
  11. // 手工添加路由。注意:使用了路由扫描就不要再使用手工添加路由,两者选其一
  12. me.add("/hello", HelloController.class);
  13. }

Routes.setBaseViewPath(baseViewPath) 方法用于为该 Routes 内部的所有 Controller 设置视图渲染时的基础路径,该基础路径与Routes.add(…, viewPath) 方法传入的viewPath以及 Controller.render(view) 方法传入的 view 参数联合组成最终的视图路径,规则如下:

finalView = baseViewPath + viewPath + view

注意:当view以 “/” 字符打头时表示绝对路径,baseViewPath 与 viewPath 将被忽略。

2、路由扫描(jfinal 4.9.03 新增功能)

jfinal 4.9.03 新增了路由扫描功能,扫描功能需要在 Controller 声明之处使用 @Path 注解,例如:

  1. @Path("/project")
  2. public class ProjectController extends Controller {
  3. ...
  4. }
  5. // 以下用法为 controller 配置了 viewPath
  6. @Path(value="/", viewPath="/index")
  7. public class IndexController extends Controller {
  8. ...
  9. }

@Path 注解可以配置 controllerPath 与 viewPath 两个参数,当 viewPath 省略时默认与第一个参数值相同。上例中的两个 Controller,第一个只配置了 controllerPath,第二个还配置了 viewPath。

快速掌握:对于已经熟悉了 jfinal 手工注册路由的同学来说,@Path 注解的两个参数完全等价于 Routes.add(controllerPath, controllerClass, viewPath) 方法中的第一第三个参数。

使用了 @Path 注解以后,通过如下简单配置即可开启路由扫描功能:

  1. public void configRoute(Routes me) {
  2. me.scan("com.xxx.");
  3. }

通过 scan(…) 方法即开启了路由扫描功能,以上代码中的参数 “com.xxx.” 表示扫描被限定在 “com.xxx.” 包以及子包下面,其它地方不被扫描。

如果需要对路由进行分类拆分,并且需要分类进行 baseViewPath 配置,以及分类进行 Routes 级别的拦截器配置,可以参考 jfinal 俱乐部项目中使用的如下配置:

  1. public void configRoute(Routes me) {
  2. /**
  3. * 扫描后台路由
  4. */
  5. me.add(new Routes() {
  6. public void config() {
  7. // 添加后台管理拦截器,将拦截在此方法中注册的所有 Controller
  8. this.addInterceptor(new AdminAuthInterceptor());
  9. this.addInterceptor(new PjaxInterceptor());
  10. this.setBaseViewPath("/_view/_admin");
  11. // 如果被扫描的包在 jar 文件之中,需要添加如下配置:
  12. // undertow.hotSwapClassPrefix = com.jfinal.club._admin.
  13. this.scan("com.jfinal.club._admin.");
  14. }
  15. });
  16. /**
  17. * 扫描前台路由
  18. *
  19. * 注意:
  20. * 1:scan(...) 方法要添加 skip 参数,跳过后台路由,否则后台路由会被扫描到,
  21. * 造成 baseViewPath 以及 routes 级别的拦截器配置错误
  22. *
  23. * 2: 由于 scan(...) 内部避免了重复扫描同一个类,所以需要将扫描前台路由代码
  24. * 放在扫描后台路由之前才能验证没有 skip 参数造成的后果
  25. */
  26. me.add(new Routes() {
  27. public void config() {
  28. this.setBaseViewPath("/_view");
  29. // 如果被扫描的包在 jar 文件之中,需要添加如下配置:
  30. // undertow.hotSwapClassPrefix = com.jfinal.club.
  31. this.scan("com.jfinal.club.", className -> {
  32. // className 为当前正扫描的类名,返回 true 时表示跳过当前类不扫描
  33. return className.startsWith("com.jfinal.club._admin.");
  34. });
  35. }
  36. });
  37. }

以上配置分别创建了两个拆分的 Routes,并分别对其配置了 setBaseViewPath,后台路由还配置了 Routes 级别的拦截器,最后分别对 scan(…) 扫描方法限定了扫描范围。

以上配置的关键是第一个 scan(“com.jfinal.club._admin.”) 的参数包含了 “._admin.” 这个包路径,所以扫描范围被限定为只扫描后台管理部分的路由。而第二个 scan(“com.jfinal.club.”, skip) 的第一个参数虽然会同时扫描到前台与后台管理的路由,但是第二个参数 skip 跳过了后台控管理那部分的路由。

常见问题:当被扫描路由在 jar 包之中,如果路由扫描不成功,需要将 scan(basePackage) 中的 basePackage 参数配置在 undertow.txt 中(未使用 undertow 不必关心该问题),需要配置的变量为 undertow.hotSwapClassPrefix,例如:

  1. undertow.hotSwapClassPrefix = com.jfinal.blog.admin.

该情况只可能出现在开发时,部署环境无需关心。

3、手工配置路由

Routes 类中添加路由的方法有两个:

  1. public Routes add(String controllerPath, Class<? extends Controller> controllerClass, String viewPath)
  2. public Routes add(String controllerPath, Class<? extends Controller> controllerClass)

第一个参数 controllerPath 是指访问某个 Controller 所需要的一个字符串,从 jfinal 4.9.03 版本开始,多个 controller 可以配置相同的 controllerPath。

第二个参数 controllerClass 是该 controllerPath 所对应到的 Controller 。

第三个参数viewPath是指该Controller返回的视图的相对路径(该参数具体细节将在Controller相关章节中给出)。当viewPath未指定时默认值为controllerPath。

4、极简路由规则

JFinal 仅有四种路由,路由规则如下表:

8.png

变更:从 jfinal 4.9.03 版本开始,controllerKey 改名为 controllerPath,多个 controller 可以共享同一个 controllerPath。

从表中可以看出,JFinal 访问一个确切的 Action(Action定义见3.2节) 需要使用 controllerPath 与 method 来精确定位,当 method 省略时默认值为 index。

urlPara是为了能在url中携带参数值,urlPara可以在一次请求中同时携带多个值,JFinal默认使用减号“-”来分隔多个值(可通过constants. setUrlParaSeparator(String)设置分隔符),在 Controller 中可以通过 getPara(int index) 分别取出这些值。controllerPath、method、urlPara 这三部分必须使用正斜杠“/”分隔。

注意,controllerPath 自身也可以包含正斜杠“/”,如“/admin/article”,这样实质上实现了struts2的namespace功能。

JFinal在以上路由规则之外还提供了ActionKey注解,可以打破原有规则,以下是代码示例:

  1. public class UserController extends Controller {
  2. @ActionKey("/login")
  3. public void login() {
  4. render("login.html");
  5. }
  6. }

假定 UserController 的 controllerPath 值为“/user”,在使用了 @ActionKey(“/login”) 注解以后,actionKey 由原来的 “/user/login” 变为了“/login”。该注解还可以让actionKey中使用减号或数字等字符,如“/user/123-456”。

如果 JFinal 默认路由规则不能满足需求,开发者还可以根据需要使用Handler定制更加个性化的路由,大体思路就是在 Handler 中改变第一个参数 String target 的值。

5、路由拆分、模块化

JFinal路由还可以进行拆分配置,这对大规模团队开发十分有用,以下是代码示例:

  1. public class FrontRoutes extends Routes {
  2. public void config() {
  3. setBaseViewPath("/view/front");
  4. add("/", IndexController.class);
  5. add("/blog", BlogController.class);
  6. }
  7. }
  1. public class AdminRoutes extends Routes {
  2. public void config() {
  3. setBaseViewPath("/view/admin");
  4. addInterceptor(new AdminInterceptor());
  5. add("/admin", AdminController.class);
  6. add("/admin/user", UserController.class);
  7. }
  8. }
  1. public class MyJFinalConfig extends JFinalConfig {
  2. public void configRoute(Routes me) {
  3. me.add(new FrontRoutes()); // 前台路由
  4. me.add(new AdminRoutes()); // 后台路由
  5. }
  6. public void configConstant(Constants me) {}
  7. public void configEngine(Engine me) {}
  8. public void configPlugin(Plugins me) {}
  9. public void configInterceptor(Interceptors me) {}
  10. public void configHandler(Handlers me) {}
  11. }

如上三段代码,FrontRoutes 类中配置了系统前台路由,AdminRoutes 配置了系统后台路由,MyJFinalConfig.configRoute(…)方法将拆分后的这两个路由合并起来。使用这种拆分配置不仅可以让MyJFinalConfig文件更简洁,而且有利于大规模团队开发,避免多人同时修改MyJFinalConfig时的版本冲突。

FrontRoutes 与 AdminRoutes 中分别使用 setBaseViewPath(…) 设置了各自 Controller.render(view) 时使用的 baseViewPath。

AdminRoutes 还通过 addInterceptor(new AdminInterceptor()) 添加了 Routes 级别的拦截器,该拦截器将拦截 AdminRoutes 中添加的所有 Controller,会在 class 拦截器之前被调用。这种用法可以避免在后台管理这样的模块中的所有 class 上使用@Before(AdminInterceptor.class),减少代码冗余。

6、控制器父类中的action映射

jfinal 3.6 新增了如下配置方法:

  1. public void configRoute(Routes me) {
  2. me.setMappingSuperClass(false);
  3. }

该方法用于配置是否要将控制器父类中的 public 方法映射成 action。默认配置为 false,也即父类中的所有方法都不会成为 action。

注意:由于该配置是 3.6 版才引入,所以老版本 jfinal 项目升级时,如果控制器父类中存在 action 的需要开启这个配置为 true。因为 MsgController 中的 index() 需要被映射成 action 才能正常分发微信服务端的消息。

引入该配置本质是一个性能优化。可以加快项目启动速度。如果 Routes 被拆分成了多个子 Routes,建议在需要该配置的子 Routes 中进行配置,因为该配置可以在子 Routes 内独立生效,其它没有该配置的 Routes 仍然可以使用到该性能优化,