利用日志,可以大大改善代码观察的质量。在本章后面还会了解到,有效地使用日志是生产环境中查找bug的最佳方法。不过,如果能查看JSP是怎么工作的,并单步跟踪每个动作,也很不错,调试Java程序时通常就是这样做的。幸运的是,大多数支持JSP开发的开发环境都支持JSP调试工具。利用调试工具(debugger)可以监视代码的执行。
本章的所有代码都是用NetBeans IDE (3.6版本)创建的。NetBeans是一个免费的Java开源集成开发环境,它支持开发独立的Java应用,以及支持JSP和servlet规范的Web应用。NetBeans包括一个JSP调试工具。不过,这里介绍的概念同样适用于其他支持J2EE的开发环境,例如IntelliJ、JBuilder和WebSphere Application Developer等等,也可以利用适当的插件使用Eclipse。
这一部分包括3个主要概念:
q 断点。
q 单步跟踪代码。
q 观察点。
这是所有调试工具共有的概念。
断点标记的是执行时希望代码暂停或停止的位置。通过暂停代码的执行,可以获得代码的控制权,开始单步跟踪,或者检查系统的状态。在大多数环境中,要设置断点,只需把光标放在某行代码上,并从菜单中选择一项来设置或打开一个断点。例如,对于NetBeans,双击某行代码或者右键点击并从快捷菜单中选择Toggle Breakpoint就能在这行代码上设置一个断点。图3-4显示了NetBeans环境中的一段源代码,其中设置了两个断点。
程序在断点暂停时,它不会向浏览器发送响应。浏览器可能会超时,这会影响所测试程序的行为。这也是Web应用环境中使用调试工具的一个公认的局限性。

图3-4 NetBeans环境中设置有断点的JSP代码
实验:使用调试工具
以下代码示例展示了如何使用调试工具:
<%@page contentType="text/html"%>
<%@page pageEncoding="UTF-8"%>
<%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@taglib prefix="log" uri="http://jakarta.apache.org/taglibs/log-1.0" %>
<html>
<head><title>Debugger Exercise</title></head>
<body>
<%
int dice = 0;
java.util.ArrayList imageNames = new java.util.ArrayList(5);
java.util.Random rnd = new java.util.Random();
for (int i = 0; i < 5; i++)
{
dice = rnd.nextInt(6)+1;
imageNames.add( new String("dice128_"+dice+".gif"));
}
pageContext.setAttribute("dice", imageNames);
%>
<table>
<tbody align="center">
<tr>
<c:forEach var="imageName" items="${dice}" >
<td><img src='${imageName}' /></td>
</c:forEach>
</tr>
</tbody>
</table>
</body>
</html>
给这个程序起名为RollDice.jsp。它掷5次骰子,并在浏览器中以图形方式显示出来。这个scriptlet随机生成骰子的值,并创建一个数组来建立骰子点数与图像文件之间的映射。所选的图像文件名放在一个置于页面上下文的数组列表中。在基于模型-视图-控制器的应用中,可以把这个scriptlet移到控制器中。余下的JSP创建一个表,将适当的图像加载到表中的各个元素(单元格)中。这些图像包括在源代码文件中,可以从本书资源下载。也可以使用自己的图像,只要把图像文件命名为 dice128_1.gif 到dice128_6.gif即可。执行这个JSP时,输出如图3-5所示。

图3-5 运行RollDice.jsp的结果
每次运行JSP时,都会显示一组不同的骰子。在下面这行代码处设置一个断点:
<table>
然后,开始在调试工具中运行这个应用。在Debug 或Run菜单中找到相应的菜单项,然后在调试工具中运行。对于NetBeans,请选择DebugèStart SessionèRun in Debugger,也可以使用Alt+F5达到目的。如果你的环境不会自动打开一个浏览器,还要打开一个浏览器窗口,并指定浏览应用中RollDice.jsp文件的URL。在我的系统上,RollDice.jsp文件的URL是http://localhost:8084/chapt14/RollDice.jsp。
IDE中的容器遇到断点就会暂停执行。浏览器看上去好像没响应了,这是正常的。接着我们来讨论下一个话题,检查变量和设置观察点。
代码一旦暂停,就可以检查系统的状态了。大多数情况下,这意味着查看变量及变量的值。在我们这个例子中,可以看到骰子和imageNames的当前值。JSP生成的servlet中创建的变量也是可见的。要记住,通过EL表达式访问的变量实际上包含在page、request、session或application 上下文中。这些都会在浏览器上显示。图3-6显示了NetBeans的对象浏览器窗口。要看到pageContext中骰子的属性,需要打开pageContext,检查HashMap中键为“dice”的一项,然后打开这一项的值。

图3-6 NetBeans对象浏览器
利用观察点,可以更容易地监视那些对我们来说很重要的变量。观察点(watchpoint)就是我们要求调试工具观察的一个变量。一般地,会在另外一个不同的窗口中跟踪这些变量,这样就能根据需要进行监视和查看。对于NetBeans,浏览感兴趣的表达式时,可以用右键点击使它成为观察点。单步跟踪代码时,如果代码修改了所观察变量的值,观察点也会随之更新。
最后要说明一点:许多调试工具不仅允许检查变量,还允许修改变量的值。这是很有用的,这样就能很快地检查一个假设是否成立。
在断点处暂停后,可以单步跟踪代码,即每次只执行一行代码。大多数调试工具都至少提供了以下单步跟踪代码的功能:
q 跳步跟踪(Step Over):执行下一行代码,然后再次暂停。如果当前行调用了其他方法或函数,不会进入到所调用的方法或函数中。
q 步进跟踪(Step Into):执行下一行代码,如果这行代码中调用了其他函数或方法,则步进到所调用的各个函数或方法中,在该方法(或函数)的第一行暂停。自此可以继续在所进入的方法(或函数)中单步跟踪。
q 步出跟踪(Step Out):继续运行当前函数,直到函数或方法返回。如果步进到一个不感兴趣的函数中时,这个功能就很有用。
q 继续(Continue):继续全速运行程序,直至遇到新断点。
q 终止(Terminate):中止程序,不必完全执行完毕。如果知道存在一个bug而导致程序不可能成功运行完,这个功能通常很有用。
执行单步跟踪命令时,观察点会不断更新,这样我们就能检查变量。利用这些命令,程序员可以每次观察程序执行一行。执行完感兴趣的代码时,程序员可以让程序继续全速执行,而不必再一步一步地跟踪。
启动Tomcat时可以要求支持Web应用的远程调试。如果IDE没有集成的Web开发环境,那么远程调试就很有用。远程调试实际上与在集成环境中调试是一样的,只不过服务器可以在另外一台机器上。要使Tomcat启动时支持在Windows平台上进行远程调试,请完成以下步骤:
① 确保已禁止Tomcat作为服务启动。在Windows平台上,可以使用控制面板上的服务applet 来完成这一点。先找到Apache Tomcat服务(如果没有这个服务,可以跳过这一步),然后双击,会看到属性屏幕。在属性屏幕中,选择Stop,再把启动类型设置为手动(Manual)。这样,就不会在每次机器重启时都启动Tomcat了。
② 打开命令行窗口,进入到Tomcat安装目录下的\bin 目录。Tomcat 5 在Windows平台上的默认安装目录是C:\Program Files\Apache Software Foundation\Tomcat 5.0。
③ 键入以下命令,启动Tomcat:
SET JPDA_TRANSPORT=dt_socket
SET JPDA_ADDRESS=8000
Catalina jpda start
这几行使得Tomcat以调试模式启动,并在8000端口监听调试工具。
④ 要关联远程调试工具,需要为Tomcat实例提供机器名和调试端口。例如,使用NetBeans的话,请选择DebugèStart SessionèAttach,并如图3-7所示填写Attach对话框。

图3-7 NetBeans 远程调试Attach对话框
设置如下:
q Debugger:默认调试工具(JPDA)。
q Connector:SocketAttach(由Socket与其他VM关联)。
q Transport:dt_socket。
q Host:localhost或远程机器的主机名。
q Port:8000。
完成设置后,就可以像在IDE中一样开始调试程序了。







