Playframework热情交流群里的邮件讨论
Freewind@play23月17日 10:21
昨天有感于play源代码里的一些强大功能和精巧实现,提出了以下问题,这些问题在群里和几位朋友简单讨论过,但我觉得整理一下会有用。
play的一些打破常规的做法,让我非常欣赏和赞叹。希望大家不要仅满足于使用play,在有余力的情况下,多看看play的源代码,对自己java能力的进一步提高非常有帮助。当你能读懂甚至能独立实现一个类似play的框架时,就是真牛人了:)
有一些问题我也不知道答案,我有时间也会去读,共勉。
问题列表:
- play的hot reload是怎么实现的
- 有的库不能直接在play中使用,必须写一个插件以解决classloader的问题,为什么
- action中向view中传参数时,可以render(a,b,c),就直接过去了,不用专门传参数名"“a”",““b””,““c”",这是怎么实现的
- javabean不用写getter/setter,自动生成,是怎么实现的
- 其它类中调用javabean中的public field,但是最终将被转为getter/setter,怎么实现的
- play中的template用的是groovy的gsp,但增加了自己的东西,比如tag等,是怎么实现的
- User类继承Model后,可以使用findXxx这样的静态方法,这个方法是父类提供的,却能返回正确的子类类型,是怎么实现的
- Action中直接调用另一个类,不用写return,但后面的代码就不再执行了,这是怎么实现的
- play的session是基于cookie的,是怎么实现的
- play中可以支持大量客户端的ajax应用,在action里,当接收到一个请求但没有取得需要的数据时,会暂停它的执行让出线程,等到有数据时再回到线程,发给客户端,是怎么实现的
- public static void action(String name, String password),play能根据name和password的名字,到request中取对应的数据,是怎么实现的?默认情况下,java程序编译后,参数名都会变,比如变成action(String a,String b)
- play的request,response,cookie等类,都是自己定义的,为什么却可以打包为war,部署到tomcat下?那边传过来的可是j2ee里的类
green@使用中3月17日 11:07
很好的问题。谈谈自己的一些认识:
- play的hot reload是怎么实现的
Play 会尝试使用Java instrument来做类的热替换,不过这个过程成功率不高,因为限制很大,包括类方法的签名不能改变 如果java instrument替换失效,play会重启,这样会重新装载application class loader,以便获得最新的应用类字节码 参见 play.classloading.ApplicationClassloader.detectChanges()
- 有的库不能直接在play中使用,必须写一个插件以解决classloader的问题,为什么?
能举例吗?
- action中向view中传参数时,可以render(a,b,c),就直接过去了,不用专门传参数名"“a””,““b””,““c”",这是怎么实现的?
play使用本地变量名增强器对应用类进行增强,这个过程会获得参数名字信息并保存到线程变量(ThreadLocal)当中。 同时控制器的render方法会尝试获得线程变量中的变量名字,将其放入renderArgs 参见 play.classloading.enhancers.LocalvariablesNamesEnhancer 和 play.mvc.Controller.render()
- javabean不用写getter/setter,自动生成,是怎么实现的
参见 play.classloading.enhancers.PropertiesEnhancer 另外不是不用写getter/setter,而是当你没有提供getter/setter方法的时候,play帮你提供一个默认的
- 其它类中调用javabean中的public field,但是最终将被转为getter/setter,怎么实现的
参见 play.classloading.enhancers.PropertiesEnhancer
- play中的template用的是groovy的gsp,但增加了自己的东西,比如tag等,是怎么实现的?
tag的实现有两种,一种是定义tag模板(/play/framework/templates),另一种是定义Java类/play/framework/src/play/templates/(FastTags.java|GroovyInlineTags.java))。 调用机制在play.templates.GroovyTemplateCompiler.startTag()可以找到
- User类继承Model后,可以使用findXxx这样的静态方法,这个方法是父类提供的,却能返回正确的子类类型,是怎么实现的?
这是通过Java Generic机制实现的,如下代码所示:
:::java
public static `<T extends Model>` T findById(Object id) {
throw new UnsupportedOperationException(
""Embedded entity does not support this method"");
}
其实Java编译器无法判断具体返回类型,只能说是Model的子类。这个机制也有缺陷,导致下面代码编译出错:
:::java
return (id == null) ? null : MyModel.findById(id);
你只能用一下代码替换:
:::java
if (null == id) return null;
else return MyModel.findById(id);
另外在Model的各种find方法中很多都会抛出UnsupportedOperationException异常,之所以运行的时候没有抛出异常,也是因为通过增强器进行了代码注入。
- Action中直接调用另一个类,不用写return,但后面的代码就不再执行了,这是怎么实现的?
Controller的各种render方法最后都会抛出play.mvc.results.Result类型的异常
- play的session是基于cookie的,是怎么实现的?
很简单,在回应请求的时候play的session对象会被写入cookie,而读取请求的时候会从cookie生成session对象
- play中可以支持大量客户端的ajax应用,在action里,当接收到一个请求但没有取得需要的数据时,会暂停它的执行让出线程,等到有数据时再回到线程,发给客户端,是怎么实现的?
通过await()调用实现的。具体过程比较复杂,如果愿意探究,需要仔细研究Controller和ActionInvoker代码
- public static void action(String name, String password),play能根据name和password的名字,到request中取对应的数据,是怎么实现的?默认情况下,java程序编译后,参数名都会变,比如变成action(String a,String b)
与问题3同理,不过使用LocalVariablesNamesTracer的换成了ActionInvoker,这个过程不仅仅是变量名传递,同时还有很复杂的变量类型绑定。具体参见ActionInvoker.getActionMethodArgs的代码
- play的request,response,cookie等类,都是自己定义的,为什么却可以打包为war,部署到tomcat下?那边传过来的可是j2ee里的类
不熟悉这方面的,大概是play.server.ServletWrapper做转换工作,参见play.server.ServletWrapper.service方法。
Freewind@play23月17日 11:27
- 比如使用Ebean,如果不写一个插件来处理classloader的问题,在play中无法使用。
Play在dev模式下,编译java源代码后,生成的字节码是放在内存中的,而不是以.class文件形式保存在硬盘上。这给其它一些库带来了麻烦。 比如在Ebean的config中,需要传入Model类,Ebean会设法导入对应的.class文件。此时必须写一个插件,让Ebean能从play的classloader中找那些类。不然找不到会报错。