OOPAA

Focusing on OO, Patterns, Architecture, and Agile
posts - 29, comments - 75, trackbacks - 0, articles - 0
  BlogJava :: 首页 :: 新随笔 :: 联系 :: 聚合  :: 管理

play! framework hot swap 浅析

Posted on 2008-12-30 17:06 mingj 阅读(4494) 评论(12)  编辑  收藏 所属分类: framework 框架
play! 最大的卖点就在于 hot swap,正如它自己宣称的:
reach your maximum productivity。play! 允许开发人员修改java文件,保存,然后刷新浏览器,立马可以看到效果。不需要编译,也不需要重启服务器。
Java 要想实现动态更新 class 文件,不外乎两种手段:替换 classloader、替换 JVM。因为替换 JVM 引起的开销更大,需要维护 JVM 的堆、栈等运行信息,所以 hot swap 通常是选择替换 classloader。比如 grails 里面就是选择替换 classloader,它会自己维护一个线程,定期轮询源文件是否发生修改,以替换原来的 classloader。那么 play! 宣称的 hot swap 又是怎么实现的呢?
让我们来看看play! 的内部流程:
1. play! 使用了 Apache Mina 作为底层的 http server,然后使用了自己关于 Mina IoHandler 接口的实现—— HttpHandler
2. 当浏览器发起一个 request:
2.1 Mina Server 生成一个 Mina Request,转发给 HttpHandler 的 messageReceived 方法
2.2 play! 解析 Mina Request 和 Mina Session,包装成自己的 Request 对象

Request request = parseRequest(minaRequest, session);

2.3 play! 检测 Route 文件修改情况,根据 Route 配置信息将 Route/Action 的信息赋给 Request 对象

Router.detectChanges();
Router.route(request);

2.4 play! 根据当前配置的开发模式来采用不同的策略调用 Action 来理 Request

if (Play.mode == Play.Mode.DEV) {
Invoker.invokeInThread(
new MinaInvocation(session, minaRequest, minaResponse, request, response));
}
else {
Invoker.invoke(
new MinaInvocation(session, minaRequest, minaResponse, request, response));
}

2.5 如果 play! 当前是 DEV 模式,invokeInThread方法会让 invocation 对象代理 run() 方法

public void run() {
try {
before();
execute();
after();
}
catch (Throwable e) {
onException(e);
}
finally {
_finally();
}
}

咱们来看看 before() 方法:
public static void before() {
Thread.currentThread().setContextClassLoader(Play.classloader);
if(!Play.id.equals("test")) {
Play.detectChanges();
if (!Play.started) {
Play.start();
}
}
//
}

在 Play 类的 detectChanges() 方法里面,有这么一句:
classloader.detectChanges();

哈哈,play! 修改源文件后,刷新浏览器即见效的奥秘就在这里了。再进去看看 play! 自定义 classloader 的 detectChanges() 方法:

public void detectChanges() {
// Now check for file modification
List<ApplicationClass> modifieds = new ArrayList<ApplicationClass>();
for (ApplicationClass applicationClass : Play.classes.all()) {
if (applicationClass.timestamp < applicationClass.javaFile.lastModified()) {
applicationClass.refresh();
modifieds.add(applicationClass);
}
}
List
<ClassDefinition> newDefinitions = new ArrayList<ClassDefinition>();
Map
<Class, Integer> annotationsHashes = new HashMap<Class, Integer>();
for (ApplicationClass applicationClass : modifieds) {
annotationsHashes.put(applicationClass.javaClass, computeAnnotationsHash(applicationClass.javaClass));
if (applicationClass.compile() == null) {
Play.classes.classes.remove(applicationClass.name);
}
else {
applicationClass.enhance();
BytecodeCache.cacheBytecode(applicationClass.enhancedByteCode, applicationClass.name, applicationClass.javaSource);
newDefinitions.add(
new ClassDefinition(applicationClass.javaClass, applicationClass.enhancedByteCode));
}
}
try {
HotswapAgent.reload(newDefinitions.toArray(
new ClassDefinition[newDefinitions.size()]));
}
catch (ClassNotFoundException e) {
throw new UnexpectedException(e);
}
catch (UnmodifiableClassException e) {
throw new UnexpectedException(e);
}
// Check new annotations
for (Class clazz : annotationsHashes.keySet()) {
if (annotationsHashes.get(clazz) != computeAnnotationsHash(clazz)) {
throw new RuntimeException("Annotations change !");
}
}
// Now check if there is new classes or removed classes
int hash = computePathHash();
if (hash != this.pathHash) {
// Remove class for deleted files !!
for (ApplicationClass applicationClass : Play.classes.all()) {
if (!applicationClass.javaFile.exists()) {
Play.classes.classes.remove(applicationClass.name);
}
if(applicationClass.name.contains("$")) {
Play.classes.classes.remove(applicationClass.name);
}
}
throw new RuntimeException("Path has changed");
}
}

HotswapAgent类的 reload 方法如下:
public static void reload(ClassDefinition definitions) throws UnmodifiableClassException, ClassNotFoundException {
instrumentation.redefineClasses(definitions);
}

读到这里,也就弄清楚了 play! 怎么实现 hot swap 的原理了,还是调用java.lang.instrument目录下的类和方法来实现的 hot swap。不存在魔法,play! 还是选择了替换 classloader,只不过这个替换动作发生在处理 http request 的时候,于是开发人员用起来就是“刷新浏览器就可以看见效果了”。



评论

# re: play! framework hot swap 浅析  回复  更多评论   

2008-12-30 19:09 by 绵阳人才网
very good! 绵阳 的 人才 我 wite...

# re: play! framework hot swap 浅析  回复  更多评论   

2008-12-30 19:25 by 太阳里的雪
play! 框架的controller和model分包,及view下的html文件目录组织怎么搞?

# re: play! framework hot swap 浅析  回复  更多评论   

2008-12-30 20:16 by 太阳里的雪
用了一下,某些方面比Grails还要强~~

# re: play! framework hot swap 浅析  回复  更多评论   

2008-12-30 20:28 by mingj
@太阳里的雪
你说文件目录结构怎么搞是什么意思?
是说怎么放置相应的文件么?

你可以看看/resources目录下的application-skel文件夹
这下面就是新建application的skeleton

# re: play! framework hot swap 浅析  回复  更多评论   

2008-12-30 21:22 by 太阳里的雪
就是我要把controller和model要分包,好象包名必须以controllers和models开头才行。

另外一个问题就是怎么跟spring结合。

# re: play! framework hot swap 浅析  回复  更多评论   

2008-12-30 22:55 by shinewang
@太阳里的雪
最新的stable4里面有个SpringPlugin

# re: play! framework hot swap 浅析  回复  更多评论   

2008-12-30 23:50 by mingj
@太阳里的雪
是的,这是play! 要求的convention
被写死在Play类里面了

# re: play! framework hot swap 浅析  回复  更多评论   

2008-12-31 02:03 by 太阳里的雪
今天试用了一下,play!模版处理能力目前实在是太差啦~~功能太少~~

# re: play! framework hot swap 浅析  回复  更多评论   

2008-12-31 10:46 by mingj
@太阳里的雪
现阶段的play! 的确只能算是个玩具
一些编程理念和普遍接受的理念大相径庭

稍后我还会推出其他的博文来分析play!种种之痛的
敬请期待:)

# re: play! framework hot swap 浅析  回复  更多评论   

2008-12-31 19:59 by 梦想在这里起飞
写得不错啊,看看我这个
开源的报表ireport项目web应用
http://ireport.cubebi.com


# re: play! framework hot swap 浅析  回复  更多评论   

2009-01-05 12:44 by ajf
感兴趣的朋友可以俺整地看看这个
ajf agile java framework

http://www.blogjava.net/ajf/archive/2009/01/03/249618.html

# re: play! framework hot swap 浅析  回复  更多评论   

2011-10-28 15:02 by 孙健
@太阳里的雪
可是扩展及其方便 相比较JSP。

只有注册用户登录后才能发表评论。


网站导航: