提问

#楼主# 2016-5-16

跳转到指定楼层
我的第一个 Java 项目包括接管由我所在公司的一个部门编写的一个门户,该部门现在已经关闭了。当我凝视着运行门户代码的服务器终端时,我看到一行接一行的调试代码涌现在在屏幕上。进一步的研究之后,我发现 JavaServer Pages ( JSP )和 servlet 中到处都是 System.out.println (“ [Debug Statement] ”)。由于我对我接管的语言和代码有些陌生,我坚持自己为终端编写调试语句,因为我不知道调试 Java 代码的其他方法。
  然而,在漫长而乏味的调试过程中,我不断失败。我发现在将代码提交给 QA 之前很难移除所有的调试语句。同样,调试涉及到重新编译、重新部署的代码和在执行代码时观察终端也非常困难。为了查找代码中断的点,我时常在方法中的每一行代码后加入 println 语句,在这种情况下,过程常常为找到的每一个故障重复。
  最后,在我发现 Java 平台调试架构( Java Platform Debugger Architecture , JPDA )的时候,我摆脱了调试梦魇。 JPDA 是一套组成构建调试应用程序框架的 API 。幸运的是,我们大多数人不需要自己考虑创建调试应用程序的事,因为这些是与主要的 IDE 捆绑在一起提供的。调试在这些新的 IDE 中是一个相当简单和轻松的过程。
  您必须执行几个步骤才可以使您的开发环境成为您可以进行调试的环境。如果您正在使用 Sun 的 Java Virtual Machine ( JVM )进行调试,您必须在命令行中指定启动 J2EE 容器的时间,其中, JVM 已经以调试模式启动。为了执行该操作,只需在 java 命令的后边添加 -Xdebug 参数。我们随后将研究一个命令行调试参数的示例。为了加载 Java Debug Wire Protocol ( JDWP ) 的 JPDA 参数实现,需要使用具有随后指定的 JPDA 选项的 -Xrunjdwp 。该参数加载进行中的调试资料库,而其子选项提供了调试服务器如何与调试客户端交互的细节。我们将研究一份 JPDA 子选项列表,该列表可以帮助设置正确的调试环境。
  首先,指定传输选项。 transport 用于在调试程序和 VM 使用的进程之间通讯。 Win32 平台的 VM 提供了很多其他平台使用的共享内存传输和套接字传输。共享的内存传输(仅在 Win32 平台中受支持)要求调试应用程序和目标 VM 存放在相同的机器中。套接字传输使用标准的 TCP/IP 套接字来与调试信息通信。调试客户端和调试服务器可以位于使用套接字传输的相同或者不同的机器中。对于远程调试 Enterprise JavaBeans ( EJB )和 servlet 来说,我们关注于使用套接字传输,因为它受所有平台支持:
  1. transport=dt_socket
复制代码

  我们正在启动的 VM 需要作为调试服务器执行。如前所述,调试服务器是运行以调试模式编译的应用程序的 JVM (以后讲述),并具有允许客户端连接和 监听 应用程序的开放套接字。为了使 VM 成为一台服务器,需要提供服务器选项,并将其值设置为 yes :
  1. server=yes
复制代码

  接下来,我们需要指出调试服务器将要使用的端口号,同时也是端口客户端用来连接服务器的端口号。只有知道正在运行服务器的端口号的客户端才可以建立连接,因为不存在标准的调试端口,也就是 HTTP 服务器。任何未使用的端口都适用。在我们的情况中,我们使用了端口 4000 :
  1. address=4000
复制代码

避免混乱
  您可以提供一个选项,仅在调试客户端建立连接之后启动 VM ( suspend=y )。我的 J2EE 容器具有一种服务,它可以在容器登录之后立即启动,而不用添加 suspend=y 到参数列表。我发现在我的那项服务启动之前,启动客户端并连接到 VM 有些混乱 。使用该选项,我就可以启动 VM 。当它暂停的时候,我打开 IDE ,设置中断点,然后建立调试连接。一旦连接建立,容器继续登录,而我可以一步步启动服务。
  onthrow 选项推迟初始化 JDWP 资料库,直到引发指定类的异常。例如,如果需要在引发异常(如 ServletException )时执行一些操作,您需要包含如下选项:
onthrow=
javax.servlet.ServletException
  同样, JDWP 资料库初始化也可以推迟,直到引发异常但没有捕获时,该操作可以通过提供 onuncaught=y 选项指定。
  启动选项指出 VM 应当基于调试事件来启动应用程序,该事件与 onthrow 或者 onuncaught 选项一起提供:
  1. launch=/usr/home/mydir/debugapp
复制代码

  让我们研究一些我经常使用的命令行调试参数的示例。在第一个示例中,我们将指导 VM 使用套接字传输。调用的 VM 是服务器,它应当监听端口 4000 。我们还指出 VM 应当在调试服务器建立连接后才可以暂停:
  1. -Xrunjdwp:transport=
  2. dt_socket,server=y,address=4000,
  3. suspend=n
复制代码

  第二套参数类似于前一个示例,不同的是其传输现在是一种共享内存传输(仅限于 Windows ):
  1. -X runjdwp:transport=
  2. dt_shmem,server=y,address=4000,
  3. suspend=n
复制代码

  第三套参数会导致 VM 通过端口 4000 上的套接字连接附加到正在运行中的调试服务器中,这将要求 VM 以调试模式运行在端口 4000 上的 SomeHost 中:
  1. -X runjdwp:transport=
  2. dt_socket,server=n,address=
  3. SomeHost:4000, suspend=n
复制代码

  第四套参数将导致 VM 在开始的时候暂停。 VM 将等待建立与调试客户端的连接,然后再继续加载。 VM 使用端口 4000 来监听客户端,并使用套接字连接:
  1. -X runjdwp:transport=
  2. dt_socket,server=y,address=
  3. 4000, suspend=y
复制代码

  使用第五套参数,当引发 javax.management.InstanceNotFoundException 的时候, VM 将暂停,并启动 c:debug.bat 。 debug.bat 脚本可以启动调试应用程序,该应用程序可以连接服务器,并开始调试会话:
  1. -Xrunjdwp:transport=
  2. dt_socket,address=4000,server=
  3. y,suspend=y,onthrow=
  4. javax.management.
  5. InstanceNotFoundException,
  6.    launch=c:debug.bat
复制代码

  最后一套参数将在引发异常但没有捕获的时候执行 debug.bat 脚本:
  1. -Xrunjdwp:transport=
  2. dt_socket,address=4000,server=
  3. y,suspend=n,onuncaught=
  4. y,launch=c:debug.bat
复制代码

  我们现在可以使用一些现有的通用 IDE 来说明如何调试 J2EE 。我们在示例中使用的 J2EE 容器是 JBoss 服务器;然而,任何标准的 J2EE 容器都是可用的。 JBoss 服务器从 bin 目录中的 run.bat 文件启动。 BAT 文件可以接受调试命令行自变量,并将它们发送给 JVM 。作为一名开发人员,我发现我很少需要启动开发环境(除了调试模式),因此我将 run.bat 复制到一个名叫 debug.bat 的新文件中,并且在该文件中添加了调试参数。以下是 JBoss debug.bat 文件的内容:

  1. rem Read all command line
  2. rem arguments
  3. set ARGS=
  4. :loop
  5. if [%1] == [] goto endloop
  6. set ARGS=%ARGS% %1
  7. shift
  8. goto loop
  9. :endloop
  10. set JAVA_OPTS=
  11. -Dprogram.name=run.bat
  12. set JAVAC_JAR=
  13. %JAVA_HOME%lib        ools.jar
  14. set RUNJAR=.un.jar
  15. set JBOSS_CLASSPATH=
  16. %JBOSS_CLASSPATH%;%JAVAC_JAR%;
  17. %RUNJAR%
  18. rem Set the debug options here
  19. set DEBUG_OPTS =
  20. -Xdebug -Xnoagent
  21. -Djava.compiler=
  22. NONE -Xrunjdwp:transport=
  23. dt_socket,address=
  24. 4000,server=y,suspend=n
  25. Java %JAVA_OPTS% %DEBUG_OPTS%
  26. -classpath "%JBOSS_CLASSPATH%"
  27. org.jboss.Main %ARGS%
复制代码

调试信息
  在将 IDE 的调试程序连接到服务器之前,将应用程序放在服务器上。确保使用打开的调试信息对应用程序进行编译。这样做可以在已经编译的类文件中提供行编号方式。如果您使用 Ant 来构建项目,请确保 javac 任务包含:
debug="on"
否则, IDE 应当包含在编译期间打开调试的选项。
  我提供了 从简单的 DocBook 文件生成 PDF E-Books 的 J2EE 应用程序 。该应用程序由会话 bean 、 JSP 文件和 servlet 组成。会话 bean 可以列举存储在服务器中的 DocBook 文档和 XSL 样式表文件。 JSP 使用会话 bean 来显示书籍和 XSL 文件的列表,以下拉列表的方式显示。用户可以选择他们要将哪些文件转换成 PDF ,并指定要将哪些样式表用于转换。作为最后一个步骤, servlet 将 DocBook 文件转换为 PDF ,并将 PDF 显示在用户的 Web 浏览器中。 Apache 的 FOP 资料库用于转换文档(参阅 资源 )。
  如果您想使用我构建的应用程序,请编辑 Ebook.properties 文件,并提供到您的机器中某个位置的有效路径,该位置中存放 DocBook 和 XSL 文件。示例 DocBook 和 XSL 文件还可以下载。请将这些文件存放在您指定的位置中。
  我已经提供了一份 Ant build.xml 文件来构建应用程序和将类封装到企业归档文件( EAR )中。将 EAR 文件部署到 JBoss 的目录中,并启动具有以下参数的容器:
-Xrunjdwp:transport=
dt_socket,server=y,address=4000,
suspend=n
  现在,我们已经准备好在调试模式中连接代码
Intelli-J IDEA 调试
  为了在Intelli-J中连接调试服务器,只需点击工具条中的Debug按钮,打开调试配置界面即可。然后选择Remote标记,来查看哪些调试配置可用。最初,没有配置可以使用。点击屏幕左上方的+按钮来添加新的远程调试配置。输入主机名称或者IP地址和要连接到的端口(参见图1)。



图 1. 远程 Intelli-J 调试
  图中有一个 Intelli-J 中的连接到本地机器的远程调试配置。
  确保服务器启动时具有已经为 VM 进行设置的调试选项,然后点击 Debug 按钮。您应当连接到 VM ,并且您的调试屏幕中将出现在 Intelli-J 屏幕的底部。您可以通过点击 Edit 窗口右边空白区域来轻松的在 IDE 中设置中断点。在中断的地方会出现一个红点。如果圆点中有一个 X ,则您指定的中断点无效,或者服务器中的代码在编译时没有打开调试。如果圆点中有一个校验标记,则说明您已经成功的连接到调试服务器,和已经选择了有效的中断点位置。您可以进行调试了。如果您使用的是本文提供的 示例代码 ,则请在 EJB 的 getDocBooks() 方法中设置中断点。将 Web 浏览器指向 http://localhost:8080/DocBookToPDF/DocBook.jsp 。您的浏览器应当显示为悬挂状态,并且 Intelli-J 在设置中断点的行中应当有一个蓝条。恭喜,您正在调试 EJB ,您的 IDE 已经为进入代码准备就绪(参见 图 2)。



图 2. Intelli-J 中断点
  Intelli-J 中断点在其设置的地方以蓝色突出显示。图中,中断点已经设置,可以进行下一步骤。
  们正在调试的方法查找在 DocBookToPDF.properties 文件中指定的目录,并返回所有以 XML 为结尾的文件。该方法假设每一个文档都是 DocBook 文件。在我们继续之前,确保目录具有有效的 DocBook 和 XSL 文件,以将文档转换为 PDF 。
  完成进入代码之后,注意 JSP 在下拉列表中显示 DocBook 和 XSL 文件。下一步骤是调试 DocBookToPDFServlet servlet 。将中断点放在 Edit 窗口左边, processRequest() 方法中的某个位置。在 Web 浏览器中选择 DocBook 文件和有效的 XSL 文件,然后点击 Web 页面中的 Generate Book 按钮。 servlet 中的中断点将呈现为蓝色, servlet 代码可以进入。
NetBeans/Forte 调试
  在 NetBeans 中创建一个项目,该项目包含您需要调试的代码。使用打开的调试选项编译它,并像在前面 Intelli-J 部分中讨论的那样,将应用程序部署到服务器中。我提供的 Ant build XML 文件演示了如何以调试模式编译代码。
  为了开始调试,请选择 Debug | Start Session | Attach 菜单选项。这时,将出现一个对话框,要求填写连接信息。指定 JPDA 为调试程序, SocketAttach 为连接程序, localhost 为主机,端口 4000 为要连接到的器。点击 OK 按钮,您应当附加到 J2EE 容器的 VM (参见 图 3 )中。 Debug 窗口将成为活动窗口,您现在可以使用相同的方式来设置中断点,就像使用 Intelli-J 那样。在 EJB 的 getDocBooks() 方法中设置中断点,并将 Web 浏览器指向 http://localhost:8080/DocBookToPDF/DocBook.jsp 。 Web 页面应当显示为悬挂状态,而 NetBeans 中的中断点将被突出显示(参见 图 4 )。您可以在 IDE 中点击调试工具栏中的 Step Over 和 Step Into 按钮来进入 NetBeans 中的代码。您还可以在界面左边框架中设置观察。



图 3. 远程 NetBeans 调试
  图中是 NetBeans 中本地机器的远程调试配置


图 4. NetBeans 中断点
  NetBeans 中断点在活动状态时以绿色突出显示。图中是已经设置的中断点,已经准备好进行调试。
  为了调试 servlet ,请在 processRequest() 方法中设置中断点。当 JSP 在 Web 浏览器中完成加载,并在机器中显示 DocBooks 和 XSL 文件的列表之后,选择 DocBook 文件和 XSL 文件,然后点击 Web 页面中的 Generate Book 按钮。在 NetBeans 中,中断点将变为突出显示状态,您可以开始调试 servlet 代码了。
Eclipse
  为了在Eclipse中调试,需要在Java透视图中创建一个项目。如果您正在使用DocBook-to-PDF代码示例,则使用Ant build.xml编译代码,以编译打开调试的类。突出显示Java应用程序,并选择Run | Debug菜单项。此时,将出现一个对话框。突出显示Remote Java Application,然后点击对话框左手边底部的New按钮。对话框项目的名称应当为在Java透视图中突出显示的项目的名称。对话框中连接部分的主机字段应当是您需要连接到的服务器,端口字段中应当具有容器的VM正在使用的调试端口(参见图5)


图 5. 远程 Eclipse 调试
  图中是 Eclipse 中本地机器的远程调试配置。
  接下来,点击Debug按钮。Eclipse将转到Debug透视图。为了调试代码,请返回到Java透视图中,并在编辑器中打开EJB文件或者servlet。双击EJB的getDocBooks()方法的左手边的窗格来设置一个中断点。有效的中断点具有一个蓝色的圆球,并且圆球中有一个校验标记。如果中断点中没有校验标记,双击该中断点,然后尝试不同的点。
  接下来,将Web浏览器指向相同的JSP和servlet文件,就像前面NetBeans和Intelli-J中所描述的那样。当设置中断点时,中断点所在的行将被突出显示,您可以像使用其他IDE那样来调试代码(参见图6)。



图 6. Eclipse 中断点
  Eclipse 中有效的中断点以蓝色圆球和校验标记指示。该图说明了如何在 Eclipse IDE 中调试代码。当前的调试位置以绿色突出显示
  使用具有流行 IDE 的 JPDA 资料库应当会缩短开发时间,并且允许您创建包含极少缺陷的更优秀的代码。如果您仍在代码中添加 System.out.println() 语句,以发现程序失败或者中断的位置,那么请住手!您是在浪费时间和金钱。 Java IDE 已经推出很长时间了,它可以提供功能强大的调试工具。您仅需要对启动 VM 的方法做一些小小的改动,就可以轻松地调试 Java 应用程序了。
原文出处

[table]
作者简介
Brian Irwin是STSN 的软件工程师, STSN 是一家提供高速 Internet 接入服务的公司。
转播转播
回复

使用道具

成为第一个回答人

B Color Link Quote Code Smilies
*滑动验证: