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() 方法
publicvoid run() {
try {
before();
execute();
after();
} catch (Throwable e) {
onException(e);
} finally {
_finally();
}
}
咱们来看看 before() 方法:
publicstaticvoid 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() 方法:
publicvoid 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 =
="color: rgb(0, 0, 255);">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 方法如下:
publicstaticvoid reload(ClassDefinition definitions) throws UnmodifiableClassException, ClassNotFoundException {
instrumentation.redefineClasses(definitions);
}
读到这里,也就弄清楚了 play! 怎么实现 hot swap 的原理了,还是调用java.lang.instrument目录下的类和方法来实现的 hot swap。不存在魔法,play! 还是选择了替换 classloader,只不过这个替换动作发生在处理 http request 的时候,于是开发人员用起来就是“刷新浏览器就可以看见效果了”。
文章出处:飞诺网(www.firnow.com):http://dev.firnow.com/course/3_program/java/javajs/20100630/279343.html
分享到:
相关推荐
Hotswap功能是Play的一个亮点,它允许开发者在运行时修改代码并立即看到效果,无需重新启动服务器。这一特性显著提高了开发效率,减少了部署过程中的麻烦。文档中"play! framework hot swap 浅析"深入探讨了这一...
USB的设计使得设备的即插即用(Plug-and-Play)和热插拔(Hot Swap)成为可能,极大地简化了用户和开发者的工作。随着技术的发展,USB标准也在不断进化,例如USB 3.0、USB 3.1、USB 4等,带来了更高的传输速度和额外...
PCI总线由数据线、地址线和控制线组成,能够支持多种数据宽度,如32位和64位,并且提供了即插即用(Plug and Play)和热插拔(Hot Swap)功能。PCI接口标准定义了多个设备类,包括网络适配器、声卡、图形卡等,使得...
通过简单地重新加载名称空间,您的代码将被注入到游戏中,而不受诸如HotSwap之类的工具所带来的限制。 此外,REPL允许您在运行时读取和修改游戏状态,从而可以快速进行实验和诊断问题。 Clojure还带来了函数式编程...
2. **Instant Run的替换:**Android Studio 3.0中移除了Instant Run功能,并引入了更快的热重载(Hot Swap)。这个新特性允许开发者在运行应用时快速看到代码变更的效果,而无需完全重新部署应用,大大提高了调试...
8. **热插拔(Hot Swap)**:尽管不是所有PCI设备都支持,但一些高级版本的PCI标准(如PCI-X和PCI Express)提供了热插拔功能,允许在系统运行时安全地插入或移除设备。 9. **兼容性**:PCI总线协议设计时考虑了向...
PCI插槽设计为即插即用(Plug and Play)和热插拔(Hot Swap),便于用户安装和更换硬件。该协议定义了信号规范、电气特性、机械规格以及软件接口,确保不同厂商的设备能兼容并协同工作。 二、PCI协议的工作原理 1....
其次,即插即用(Plug-and-Play)和热插拔(Hot Swap)功能是现代计算机的重要特性。即插即用允许系统自动识别并配置插入的设备,而无需手动设置,简化了用户的操作。热插拔则进一步提升了系统的灵活性,允许在系统...
PCI总线提供了较高的数据传输速率,同时具备即插即用(Plug and Play)和热插拔(Hot Swap)功能,极大地简化了系统集成和维护。 PCI总线的架构主要包括以下关键概念: 1. **总线主控器**:通常集成在主板的北桥...
PCI总线还支持即插即用(Plug and Play)和热插拔(Hot Swap)功能,方便用户添加或更换硬件设备。 PCI卡细节: PCI卡是基于PCI标准的扩展卡,它通过一个物理接口连接到主板上的PCI插槽。PCI卡通常包含一个控制芯片...
3. **PCI总线特点**:32位或64位数据宽度,支持多种工作频率,具备即插即用(Plug and Play)和热插拔(Hot Swap)功能,可提供高带宽数据传输和低延迟。 4. **PCI总线定义**:PCI是Peripheral Component ...
13. **Android Studio IDE**:分享关于Android Studio的使用技巧,如热重载(Hot Swap)、调试技巧、插件推荐等。 14. **App发布与部署**:包括签名应用、打包APK、发布到Google Play商店的流程,以及第三方市场的...
4. 插拔即用(Plug and Play)与热插拔(Hot Swap):USB设备无需安装额外驱动程序即可被识别和使用,即插即用功能简化了用户操作。同时,USB支持在系统运行时插入或拔出设备,实现了热插拔。 5. 功率供应:USB接口...
3. **热修复与Instant Run**:了解如何使用Instant Run快速部署和调试应用,以及使用Hot Swap进行代码修改。 四、UI设计与动画 1. **Material Design**:遵循Material Design指南,创建符合现代审美的用户界面。 2...
PCI总线的含义是Peripheral Component Interconnect,PCI总线的主要特点包括插槽Hot-Swap、 Plug-and-Play和可扩展性强等。中断的概念是指CPU暂停当前任务,响应外部事件,执行相应的中断处理程序,然后返回到原来的...
<END><br>45,PlayAVI.zip PlayAVI.ocx is an activeX control which allows you to play an AVI file on a form. A sample application is provided with the control. <END><br>46,FldrView.zip The ...