本博客会引导你配置Eclipse的Tomcat的热代码替换(也叫做hotswap debugging)
- 什么是“热代码替换”
- 什么是“JPDA”、“JDI”
- 基于JPDA的Tomcat的配置
- “JPDA”的局限性——Catch
- 改进方案1:DCEVM
- 改进方案2:JRebel
什么是“热代码替换”
“热代码替换”(Hot Code Replace,以下简称HCR)就是当你正在调试web程序的时候,jvm允许让你修改的Java代码立刻生效,而不用重启程序,HCR是Java Platform Debugger Architecture(JPDA)的一部分(它包含很多东西,比如我们的远程调试也是),HCR在现代JVM中都有支持。
看下如下的代码:
public class Sample {
public static void main(String[] args) {
String foo = "unchangeable";
foo += blah();
System.out.println(foo);
}
public static String blah() {
String bar = "bar";
bar += "blah";
return bar;
}
}
如果你正在Eclipse中调试这段代码,你可以修改它,在线的,而不用重启程序,比如,在blah方法的第二行打一个断点,然后调试程序,程序会在bar +=那行挂起,然后修改把字符串“blah”改为“quz”,保存文件,程序会继续运行,当前行会跳到blah方法的第一行,并且会以新的代码运行。
什么是“JPDA”、“JDI”
简单来说,Java Platform Debugger Architecture(JPDA)就是Java提供的一套用于开发Java调试工具的规范,任何的JDK实现都需要实现这个规范。
JDI是这套调试工具提供的API接口,JDI的API在com.sun.jdi包下,相当于是JDI的接口规范了。除了JDK自带的实现外,我在HotSpot的SA中也发现了一个实现。他俩的实现分别是在com.sun.tools.jdi包下和sun.jvm.hotspot.jdi包下
正是因为有了他们,你才可以像上面那么玩儿。
“JPDA”的局限性——catch
你在基于Tomcat运行的web程序中也可以这么玩儿,但是会有一些问题:
“Hot Code Replace Failed”
Some code changes cannot be hot swapped into a running virtual machine, such as changing method names or introducing errors into running code. The current target virtual machine was unable to replace It is safe to continue ...
然后给你4个按钮:continue、details、terminate、restart
这个提示应该很眼熟吧,这个叫做Catch。然后刚刚修改的代码也没有生效,连行号都不对了。
什么是Catch?
当我们在使用HCR的时候还是有很多限制的:
- HCR只能替换方法里面的代码
- 不能修改类签名(类名、继承类、实现接口)、方法签名(方法名和参数列表)、成员变量
- 不能新增类
- 特殊的方法调用不能修改,例如main方法,通过反射调用的方法(这些统称为“stack frames”)
如果你修改了,那么上面的错误提示就出来了。
基于Tomcat的Web项目配置
配置基于Tomcat的web项目
项目配置,我们通常可以按照如下的步骤将web项目部署到Eclipse的Tomcat中:
- 下载EE版的Eclipse
- 在设置中配置Tomcat。打开设置,找到Servers,然后
- 创建Dynamic Web Project
- 在Servers视图中new Server
- 右击创建好的Tomcat,选择Add And Remove...,将项目部署到Tomcat中
- 以Debug模式启动Tomcat
禁用Auto Reloading
发布到Tomcat中的项目,默认都是开启Auto Reloading的,为了更好地使用JPDA功能,请按照如下步骤禁用Tomcat的Auto Reloading
- 双击Servers View中的Tomcat。
- Services View应该是默认就有的,万一没有或者被关闭了,你可以通过如下方式打开它:菜单:window→show view→services。
- 切换到Modules,选中要修改的project,然后点击edit,去掉“Auto Reloading Enabled”前面的勾。
为什么禁用Auto Reloading
Auto Reloading是一种tomcat用来实现不使用JPDA情况下支持java类热替换的。在这种模式下,Tomcat使用java中的classloaders来卸载并重新加载class,当它重新加的时候,tomcat会尝试重新初始化你的系统,重新启动那些在web.xml中标记为load-on-startup的servlet。
结果是,当你的项目里面有很多的启动代码时,这么做并不会给你节省时间,例如:如果你的启动代码需要初始化Hibernate的数据库缓存,Spring的依赖注入配置等等,你将会花费更长的时间,甚至比重启tomcat还要长。
更加坑爹的是,被自动加载的程序有时候会变得很奇怪,而且非常容易出现PermGen的内存溢出,这些都是因为频繁的卸载、重新加载类引起的。当出现这个错误的时候,重启tomcat通常可以解决这个问题。如果你即使只花费了5分钟来解决重新加载的问题,你也被坑了,因为本来你指望通过reload来做到比重启tomcat更省时间的,结果并没有。
通过禁用这个“Auto Reloading”来改为使用JPDA的代码热替换,你获得了更为可靠的代码热替换。
禁用“Auto Reloading”但是启用“Auto Publishing”
根据上面说的,你应该知道如何禁用“Auto Reloading”了吧,虽然“Auto Reloading”会导致JPDA工作不正常,但是tomcat的配置页的“Overview”Tab页有另一个设置叫做“Automatically publish when resources change”,它默认是手气的,你可以点击按钮展开它;当你禁用了“Auto Reloading”以后,这个“Auto Publishing”请确保已经打开。
为了了解两者的区别,我们需要先了解一下Eclipse的WTP是如何工作的。当你在Eclipse了里面创建了一个“Server”以后,Eclipse会在你的workspace中创建一个虚拟的Tomcat目录,这个目录里面完整包含了:conf、logs、temp、webapps、works这些目录,当你配置了这个服务,其实你就告诉了Eclipse去哪儿找tomcat的运行目录,但并不会使用webapps目录里面任何你的配置文件或者数据,在Eclipse启动tomcat的时候,它将虚拟目录的位置通过启动启动参数的方式传递给了jvm。
“发布(Publishing)”意思就是说将你全部的代码复制到这个虚拟目录中,包括:JSP、jar包,配置文件,动态生成的配置等等。
如果你禁用了这个发布功能,那么你就需要右击Server View里面你的Tomcat,然后点击“Publish”菜单了,而且每次你保存代码都要这么来一遍,更加好的是,如果你开启了“Auto Publishing”,Tomcat可以自动就支持JSP的自动重新加载(可以不用JPDA就可以支持)。
找到Tomcat虚拟目录tmp0
很多时候,看下tomcat虚拟目录的里面都有什么东西对我们代码开发是非常有帮助的。通过如下方式我们可以找到tomcat虚拟目录:通过双击Servers View中的Tomcat,在弹出的页面中找到Service Locations,我们就可以看到了,通常,虚拟目录在:.metadata/.plugins/org.eclipse.wst.server.core/tmp0,最后一个0是会变的,多个Tomcat的时候会从0开始往上涨。
这个.metadata目录在你的workspace中(你可以通过如下方式找到workspace的位置:菜单:FileSwitch Workspace默认的那个就是你当前的workspace),或者,启动Eclipse的时候让你选择的workspace就是你当前的workspace。
在这个虚拟目录中,你可以看到所有Eclipse自动创建的目录,看看conf目录下的server.xml,检查一下work目录下jsp自动生成的java文件,webapps里面肯定是空的,因为Eclipse使用的是wtpwebapps目录。
改进方案:
上面说了,JPDA有很多的局限性,但是国外牛人的能力是强大的,我们可以通过如下的方式来让我们的代码开发更加高效!
DCEVM
这个是github上的一个开源项目,是国外的一个牛人基于OpenJDK7的源码重新编写了其中代码热替换部分实现的jvm,通过将我们默认的Hotspot JVM替换为此JVM,就可以自动实现类名修改、方法名修改等一些JPDA不支持的代码热替换了。
JRebel
JavaRebel 是一个代码热替换系统,它要比HCR好一点 (可能好很多),也能弥补DCEVM对很多web框架不支持的不足。
有了JavaRebel,你可以在不重启Tomcat地情况下增加、删除方法和类,而且多个project同时运行时不会串,更好的是,它支持对很多框架都加入了支持(Spring、Struts、Jboss等等),比如,你正在基于Spring做代码开发,新增了一个Controller类或者增加了一个方法(有@RequestMapping),通常情况下,你需要重启Tomcat来让其生效,但是如果你的Eclipse安装了JavaRebel,那么当你保存的时候,Controller的信息就全部重新加载了,直接在浏览器就可以访问对应的URL!
唯一的缺憾就是,它收费,每个开发人员每年需要支付149美元,当然了,在天朝,嘿嘿(*^__^*) 。