Play框架的Router机制和jregex包
最近对Play框架兴趣甚浓,对其有兴趣的功能都想一探究竟,先研究了下它的Router机制。概要地说,主要使用routes文件和正则表达式来完成URL到函数的映射,不过这里正则表达式功能比较强可以解析出{id}这样的参数,因此Play使用了第三方jregex包而不是JDK的java.util.regex包来执行正则表达式的工作。
Play的routes文件很清楚简单,每行一个Route,最重要的参数包含请求方式、请求URL和响应的函数,就像下面这一行:
GET /customer/detail/{id} CustomerController.detail |
Router机制机制具体包含解析和路由2个阶段。路由代码主要在play.mvc.Router和其静态内部类Route,解析Request代码主要在play.mvc.ActionInvoker中。
1.route文件的解析和路由表的建立
- Play在启动初始化时,通过application.conf配置文件获取http.path参数,ctxPath = http.path;(这个就是相对路径啦)
- 调用Router.detectChanges(ctxPath);//若路由表没有建立或者routes文件被更新,解析文件
- 针对route文件的每一行(所以路由的顺序按照routes文件行的顺序为优先级),根据正则表达式获取method、path、action值,可能还有headers和params值
- 新建一条Route记录,并作一些解析计算(比如找出path中带有的参数信息)
以上面提到那条示例的route为例,method = GET,path = /customer/detail/{id},action = CustomerController.detail,在新建Route记录作解析计算时,会先将path利用正则表示式转化为/customer/detail/{<[^/]+>id}的形式,然后拿出所有的{id}形式的变量信息,再然后继续通过正则表达式再转化为
/customer/detail/({id}[^/]+) |
这样的形式,这也是path最后的pattern,用来去匹配请求的URL。这个形式的正则表达式,java.util.regex包无法解析,而jregex包是可以处理的,并且group的时候可以根据变量名来取变量值,很方便。
2.URL的路由
当一个Http请求过来,就遍历路由表,当一条Route记录可以匹配当前的path,就可以获得在routes文件中配置的action值,就比如上面的CustomerController.detail,并通过正则表达式获得路径中的参数(id=?)。然后解析action值得到Controller类名和Action方法名(action值若不以“controllers.”开头的话会被自动加上),通过Play框架中的ApplicationClassloader获取到Controller类的Class类,然后通过此Class类反射获取到具体Action的Method,将这些解析的结果都赋给play.mvc.Http的静态内部类Request中,URL路由就完成了。
值得优化
这样看来,其实这自定义URL的路由机制还是蛮简单的,如果去除了动态参数绑定、静态文件路由特判等一些特殊功能的话,那真的是很简单很容易实现了。而且感觉Play的路由代码还有很多可以值得优化:
- 不带动态参数的路由路径(就是纯字符串匹配)可以通过HashMap来匹配,而不用每条都去执行一遍正则表达式
- 每条路由对应的Controller类和调用的Action Method应该在路由表建立的时候就应该被解析完毕并存储在路由表中,而不是每一个http请求都重新解析一遍
jregex包
看Play源代码发现了这个包,官方网址:http://jregex.sourceforge.net/。网站上很好地比较了它与java.util.regex的优劣,用变量名去Group即网站上介绍的named groups,而且据它自己介绍性能比较高。觉得当一个应用大量用到正则表达式的话,jregex是比较推荐的。
JRegex
vs. java.util.regex
- (+) JRegex is a Free Software
- (+) JRegex is portable (runs under any version of java)
- (+) JRegex supports more features (named groups, Perl5.6’s conditional statements and more)
- (-) java.util.regex implements 2’d level of Unicode support (JRegex is level 1)
- (-) JRegex is not a ‘standard’ library