用于创建客户端的 WSDL 的 URL 已在?jax-ws-catalog.xml?中映射到 WSDL 的本地副本。映射到本地副本有几个好处。首先,客户端无需 WSDL 的远程副本即可运行。其次,客户端运行速度将会加快,因为其不需要对远程 WSDL 文件进行解析。最后,更加易于移植。

开发客户端
您可以通过多种方法来实现 Web 服务客户端。Web 服务的 WSDL 文件限制了您可以向该 Web 服务发送的信息类型,以及您从该 Web 服务接收的信息类型。但是,WSDL 文件不限制您对其所需信息进行传递的方式,以及用户界面所包含的内容。您接下来要构建的客户端实现包含一个 Web 页和一个 Servlet,前者可使用户输入要检查的文本,后者可将这些文本传递至 Web 服务,然后构建一个包含结果的报告。
编码 Web 页
Web 页将包含一个文本区域和一个按钮,前者可供用户输入文本,后者用于将该文本发送至 Web 服务。根据您选择作为目标服务器的服务器版本不同,IDE 生成了?index.html?或index.jsp?作为应用程序的索引页。
- 在 "Projects"(项目)窗口中,展开?SpellCheckService?项目的 "Web Pages"(Web 页)节点,然后双击索引页(index.html?或?index.jsp)以在源代码编辑器中打开文件。
- 将以下代码复制并粘贴到索引页的?<body>?标记中:
<body>
<form name="Test" method="post" action="SpellCheckServlet">
<p>Enter the text you want to check:</p>
<p>
<p><textarea rows="7" name="TextArea1" cols="40" ID="Textarea1"></textarea></p>
<p>
<input type="submit" value="Spell Check" name="spellcheckbutton">
</form>
</body>
上面列出的代码指定当单击提交按钮时,textarea?的内容将被发送至名为?SpellCheckServlet?的 Servlet。
创建 Servlet 并对其进行编码
在此部分,您将创建一个与 Web 服务交互的 Servlet。但是,执行交互的代码将由 IDE 提供。因此,您只需处理业务逻辑,即,准备要发送的文本以及对结果进行处理。
- 在 "Projects"(项目)窗口中,右键单击?SpellCheckService?项目节点,选择 "New"(新建)> "Other"(其他),然后选择 "Web" > "Servlet"。单击 "Next"(下一步)以打开 "New Servlet"(新建 Servlet)向导。
- 将 Servlet 命名为?SpellCheckServlet,然后在 "Package"(包)下拉列表中键入?clientservlet。单击 "Next"(下一步)。

- 在 "Configure Servlet Deployment"(配置 Servlet 部署)面板中,注意此 Servlet 的 URL 映射是?/SpellCheckServlet。接受默认值,然后单击 "Finish"(完成)。该 Servlet 将在源代码编辑器中打开。?

- 将光标置于源代码编辑器中的?SpellCheckServlet.java?的?processRequest?方法主体内,并在该方法的顶部添加一些新行。
- 右键单击上一步所创建的空白区域,然后选择 "Insert Code"(插入代码)> "Call Web Service Operation"(调用 Web 服务操作)。在 "Select Operation to Invoke"(选择要调用的操作)对话框中,单击?checkSoap.CheckTextBodyV2?操作,如下所示:?

单击 "OK"(确定)。
注:您也可以将此操作节点直接从 "Projects"(项目)窗口拖放至编辑器中,而不调用以上所示的对话框。
在?SpellCheckServlet?类的末尾,将会看到一个用于调用 SpellCheckerV2 服务并返回?com.cdyne.ws.DocumentSummary?对象的私有方法。
private DocumentSummary checkTextBodyV2(java.lang.String bodyText) {
com.cdyne.ws.CheckSoap port = service.getCheckSoap();
return port.checkTextBodyV2(bodyText);
}
只需使用此方法即可调用 Web 服务上的操作。此外,还需在类的顶部声明以下代码行(粗体显示):
public class SpellCheckServlet extends HttpServlet {
@WebServiceRef(wsdlLocation = "http://wsf.cdyne.com/SpellChecker/check.asmx?WSDL")
private Check service;
- 将?processRequest()?方法的?try?块替换为以下代码。以下代码行中的注释说明了每行代码的用途。
try (PrintWriter out = response.getWriter()) {
//Get the TextArea from the web page
String TextArea1 = request.getParameter("TextArea1");
//Initialize WS operation arguments
java.lang.String bodyText = TextArea1;
//Process result
com.cdyne.ws.DocumentSummary doc = checkTextBodyV2(bodyText);
String allcontent = doc.getBody();
//From the retrieved document summary,//identify the number of wrongly spelled words:
int no_of_mistakes = doc.getMisspelledWordCount();
//From the retrieved document summary,//identify the array of wrongly spelled words:
List allwrongwords = doc.getMisspelledWord();
out.println("<html>");
out.println("<head>");
//Display the report's name as a title in the browser's titlebar:
out.println("<title>Spell Checker Report</title>");
out.println("</head>");
out.println("<body>");
//Display the report's name as a header within the body of the report:
out.println("<h2><font color='red'>Spell Checker Report</font></h2>");
//Display all the content (correct as well as incorrectly spelled) between quotation marks:
out.println("<hr><b>Your text:</b> "" + allcontent + """ + "<p>");
//For every array of wrong words (one array per wrong word),//identify the wrong word,the number of suggestions,and
//the array of suggestions. Then display the wrong word and the number of suggestions and
//then,for the array of suggestions belonging to the current wrong word,display each
//suggestion:
for (int i = 0; i < allwrongwords.size(); i++) {
String onewrongword = ((Words) allwrongwords.get(i)).getWord();
int onewordsuggestioncount = ((Words) allwrongwords.get(i)).getSuggestionCount();
List allsuggestions = ((Words) allwrongwords.get(i)).getSuggestions();
out.println("<hr><p><b>Wrong word:</b><font color='red'> " + onewrongword + "</font>");
out.println("<p><b>" + onewordsuggestioncount + " suggestions:</b><br>");
for (int k = 0; k < allsuggestions.size(); k++) {
String onesuggestion = (String) allsuggestions.get(k);
out.println(onesuggestion);
}
}
//Display a line after each array of wrong words:
out.println("<hr>");
//Summarize by providing the number of errors and display them:
out.println("<font color='red'><b>Summary:</b> " + no_of_mistakes + " mistakes (");
for (int i = 0; i < allwrongwords.size(); i++) {
String onewrongword = ((Words) allwrongwords.get(i)).getWord();
out.println(onewrongword);
}
out.println(").");
out.println("</font>");
out.println("</body>");
out.println("</html>");
}
- 您会看到许多错误线和警告图标,指示未找到的类。要在粘贴代码之后修复导入,请按 Ctrl-Shift-I 组合键(在 Mac 上按 ?-Shift-I 组合键),或在任意位置单击鼠标右键,然后在打开的上下文菜单中选择 "Fix Imports"(修复导入)。(您可以选择要导入的 List 类。此处将接受默认的 java.util.List。)以下是已导入类的完整列表:
import com.cdyne.ws.Check;
import com.cdyne.ws.Words;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.List;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.xml.ws.WebServiceRef;
注:如果看到“找不到?com.cdyne.*?类”的警告内容,请不要担心。当构建项目后,IDE 在解析 WSDL 文件并查找类时,此问题会得到解决。
请注意,尚未处理上面列出的代码中存在的错误。有关详细信息,请参见应用所学知识。
部署客户端
IDE 使用 Ant 构建脚本来构建和运行应用程序。此构建脚本是由 IDE 基于您在创建项目时所输入的选项来构建的。您可以在项目的 "Project Properties"(项目属性)对话框(在 "Projects"(项目)窗口中右键单击项目节点,然后选择 "Properties"(属性))中调整这些选项。
- 右键单击项目节点,然后选择 "Run"(运行)。稍后,应用程序将部署并显示上一部分所编码的 Web 页。
- 输入一些文本,确保其中的某些文本存在拼写错误:?

- 单击 "Spell Check",然后查看结果:?

异步 Web 服务客户端
默认情况下,NetBeans IDE 创建的 JAX-WS 客户端是同步的。同步客户端会调用对服务的请求,然后在等待响应时挂起其处理。但是,在某些情况下,您希望客户端继续一些其他处理而不是等待响应。例如,在某些情况下,服务可能需要大量时间来处理请求。继续处理而不等待服务响应的 Web 服务客户端称为“异步”。
异步客户端会发出服务请求,然后继续其处理而不等待响应。服务会处理客户端请求,然后在一段时间后返回响应,而客户端则在这段时间内检索响应并继续其处理。
异步客户端会通过“轮询”或“回调”方法使用 Web 服务。在“轮询”方法中,将调用一个 Web 服务方法并反复请求结果。“轮询”是一种阻止操作,因为它会阻止调用线程,所以这就是不在 GUI 应用程序中使用它的原因。在“回调”方法中,您在 Web 服务方法调用期间传递回调处理程序。当结果有效时,将调用该处理程序的?handleResponse()?方法。这种方法适用于 GUI 应用程序,因为您不必等待响应。例如,从 GUI 事件处理程序发出调用并立即返回控制权,这样可以使用户界面随时保持响应。轮询方法的缺点是,即使在捕获响应后使用响应,也必须对其进行轮询来查明已将其捕获。
在 NetBeans IDE 中,通过勾选 Web 服务引用的编辑 Web 服务属性 GUI 中的框,将异步客户端的支持添加到 Web 服务客户端应用程序中。除了具有轮询 Web 服务或传递回调处理程序并等待结果的方法外,开发该客户端的所有其他方面都与同步客户端相同。
此部分的其余内容详述了如何创建 Swing 图形界面并将异步 JAX-WS 客户端嵌入其中。
创建 Swing 窗体
在此部分,您将设计 Swing 应用程序。如果不愿意自己设计 Swing GUI,可以下载预先设计的 JFrame,然后转至创建异步客户端中的此部分内容。
Swing 客户端会获取您键入的文本,将其发送至服务,然后返回错误数和所有错误词语的列表。该客户端还会向您显示每个错误词语和替换该词语的建议,一次只显示一个错误词语。

创建 Swing 客户端:
- 创建新的 Java 应用程序项目。将其命名为?AsynchSpellCheckClient。不要为该项目创建?Main?类。
- 在 "Projects"(项目)视图中,右键单击?AsynchSpellCheckClient?项目节点并选择 "New"(新建)> "JFrame Form..."(JFrame 窗体...)。
- 将该窗体命名为?MainForm,然后将其放置在包?org.me.forms?中。
- 创建 JFrame 后,打开项目属性。在 "Run"(运行)类别中,将?MainForm?设置为主类。?

- 在编辑器中,打开?MainForm.java?的 "Design"(设计)视图。在组件面板中,将三个滚动窗格拖放至?MainForm?中。定位滚动窗格并调整其大小。这些窗格将包含要进行检查所键入的文本、所有错误词语以及对某个错误词语提出的建议的文本字段。
- 将五个文本字段拖放至?MainForm?中。将其中的三个拖放至三个滚动窗格中。按如下方式对其进行修改:
tfYourText |
Y |
Y |
tfNumberMistakes |
N |
N |
tfWrongWords |
tfWrongWord1 |
tfSuggestions1 |
N |
- 将进度栏拖放至?MainForm?中。将该变量命名为?pbProgress。
- 将两个按钮拖放至?MainForm?中。将第一个按钮命名为?btCheck,并将其文本更改为 "Check Text" 或 "Check Spelling"。将第二个按钮命名为?btNextWrongWord,将其文本更改为 "Next Wrong Word",然后禁用该按钮。
- 将一些标签拖放至?MainForm?中,为应用程序提供一个标题并描述文本字段。
将 JFrame 的外观按您喜欢的方式进行排列,然后进行保存。接下来将添加 Web 服务客户端功能。
启用异步客户端
如创建客户端中所述,添加 Web 服务引用。然后编辑 Web 服务属性以启用异步客户端。
AsynchSpellCheckClient?项目节点,然后选择 "New"(新建)> "Other"(其他)。在新建文件向导中,选择 "Web Services"(Web 服务)> "Web Service Client"(Web 服务客户端)。在“Web 服务客户端”向导中,指定 Web 服务的 URL: http://wsf.cdyne.com/SpellChecker/check.asmx?wsdl。接受所有默认值,然后单击 "Finish"(完成)。这与创建客户端中所述步骤 2 以后的过程相同。
- 展开 "Web Service References"(Web 服务引用)节点,然后右键单击?check?服务。上下文菜单打开。

- 从上下文菜单中选择 "Edit Web Service Attributes"(编辑 Web 服务属性)。“Web 服务属性”对话框打开。
- 选择 "WSDL Customization"(WSDL 定制”)签。
- 展开 "Port Type Operations"(端口类型操作)节点。展开第一个?CheckTextBodyV2?节点并选择 "Enable Asynchronous Client"(启用异步客户端)。?

- 单击 "OK"(确定)。该对话框关闭,并出现一条警告,指出更改 Web 服务属性将会刷新客户端节点。

- 单击 "OK"(确定)。该警告信息关闭,并刷新客户端节点。如果展开 "Web Service References"(Web 服务引用)中的?check?节点,则会看到现在已具有?CheckTextBody?操作的 "Polling"(轮询)和 "Callback"(回调)版本。

现已为您的应用程序启用了 SpellCheck 服务的异步 Web 服务客户端。
添加异步客户端代码
现在,您已经有了异步 Web 服务操作,可将其中一个异步操作添加到?MainForm.java?中。
添加异步客户端代码:
- 在?MainForm?中,切换到 "Source"(源)视图,然后将以下方法添加到最后一个右花括号的前面。?
public void callAsyncCallback(String text){
}
- 在 "Projects"(项目)窗口中,展开?AsynchSpellCheckClient?的 "Web Service References"(Web 服务引用)节点,并找到?checkSoap.CheckTextBodyV2 [Asynch Callback]?操作。
- 将?CheckTextBodyV2 [Asynch Callback]?操作拖至空的?callAsyncCallback?方法主体中。IDE 会生成以下?try?块。将此生成的代码与为同步客户端生成的代码进行比较。
try { // Call Web Service Operation(async. callback)
com.cdyne.ws.Check service = new com.cdyne.ws.Check();
com.cdyne.ws.CheckSoap port = service.getCheckSoap();
// TODO initialize WS operation arguments here
java.lang.String bodyText = "";
javax.xml.ws.AsyncHandler<com.cdyne.ws.CheckTextBodyV2Response> asyncHandler =
new javax.xml.ws.AsyncHandler<com.cdyne.ws.CheckTextBodyV2Response>() {
public void handleResponse(javax.xml.ws.Response<com.cdyne.ws.CheckTextBodyV2Response> response) {
try {
// TODO process asynchronous response here
System.out.println("Result = "+ response.get());
} catch(Exception ex) {
// TODO handle exception
}
}
};
java.util.concurrent.Future<? extends java.lang.Object> result = port.checkTextBodyV2Async(bodyText,asyncHandler);
while(!result.isDone()) {
// do something
Thread.sleep(100);
}
} catch (Exception ex) {
// TODO handle custom exceptions here
}
在此代码与 Web 服务调用中,您会看到来自 SpellCheck 服务的响应是通过?AsynchHandler?对象进行处理的。同时,Future?对象会查看是否已返回了结果,然后使线程休眠直至完全返回了结果。
- 切换回 "Design"(设计)视图。双击 "Check Spelling" 按钮。IDE 会自动将 "ActionListener" 添加到按钮中,然后切换到 "Source"(源)视图中,光标同时出现在空的btCheckActionPerformed?方法中。
- 将以下代码添加至?btCheckActionPerformed?方法主体。此代码会获取您在?tfYourText?字段中键入的文本,使进度栏显示 "waiting for server" 消息,并禁用?btCheck?按钮,以及调用异步回调方法。
private void btCheckActionPerformed(java.awt.event.ActionEvent evt) {
String text = tfYourText.getText();
pbProgress.setIndeterminate(true);
pbProgress.setString("waiting for server");
btCheck.setEnabled(false);
callAsyncCallback(text);
}
- 在?MainForm?类的开头,实例化名为?nextWord?的私有?ActionListener?字段。此?ActionListener?字段是为 Next Wrong Word 按钮提供的,使用该按钮可以前进到错误词语列表中的下一个错误词语,并显示该词语以及更正它的建议。您可以在此处创建私有字段,这样就可以在定义了?ActionListener?的情况下取消对其的注册。否则,每次检查新文本时,都将添加一个额外的监听程序并最终出现多个监听程序多次调用?actionPerformed()?的情况。这样,该应用程序将无法正常运行。
public class MainForm extends javax.swing.JFrame {
private ActionListener nextWord;
...
- 将整个?callAsyncCallback?方法替换为以下代码。请注意,最外层的?try?块已删除。此代码块是多余的,因为已在方法内添加了更多特定的?try?块。并且在代码注释中介绍了对代码所做的其他更改。?
public void callAsyncCallback(String text) {
com.cdyne.ws.Check service = new com.cdyne.ws.Check();
com.cdyne.ws.CheckSoap port = service.getCheckSoap();
// initialize WS operation arguments here
java.lang.String bodyText = text;
javax.xml.ws.AsyncHandler<com.cdyne.ws.CheckTextBodyV2Response> asyncHandler = new javax.xml.ws.AsyncHandler<com.cdyne.ws.CheckTextBodyV2Response>() {
public void handleResponse(final javax.xml.ws.Response<com.cdyne.ws.CheckTextBodyV2Response> response) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
try {
// Create a DocumentSummary object containing the response.
// Note that getDocumentSummary() is called from the Response object
// unlike the synchronous client,where it is called directly from
// com.cdyne.ws.CheckTextBody
com.cdyne.ws.DocumentSummary doc = response.get().getDocumentSummary();
//From the retrieved DocumentSummary,//identify and display the number of wrongly spelled words:
final int no_of_mistakes = doc.getMisspelledWordCount();
String number_of_mistakes = Integer.toString(no_of_mistakes);
tfNumberMistakes.setText(number_of_mistakes);
// Check to see if there are any mistakes
if (no_of_mistakes > 0) {
//From the retrieved document summary,//identify the array of wrongly spelled words,if any:
final List<com.cdyne.ws.Words> allwrongwords = doc.getMisspelledWord();
//Get the first wrong word
String firstwrongword = allwrongwords.get(0).getWord();
//Build a string of all wrong words separated by commas,then display this in tfWrongWords
StringBuilder wrongwordsbuilder = new StringBuilder(firstwrongword);
for (int i = 1; i < allwrongwords.size(); i++) {
String onewrongword = allwrongwords.get(i).getWord();
wrongwordsbuilder.append(",");
wrongwordsbuilder.append(onewrongword);
}
String wrongwords = wrongwordsbuilder.toString();
tfWrongWords.setText(wrongwords);
//Display the first wrong word
tfWrongWord1.setText(firstwrongword);
//See how many suggestions there are for the wrong word
int onewordsuggestioncount = allwrongwords.get(0).getSuggestionCount();
//Check to see if there are any suggestions.
if (onewordsuggestioncount > 0) {
//Make a list of all suggestions for correcting the first wrong word,and build them into a String.
//Display the string of concactenated suggestions in the tfSuggestions1 text field
List<String> allsuggestions = ((com.cdyne.ws.Words) allwrongwords.get(0)).getSuggestions();
String firstsuggestion = allsuggestions.get(0);
StringBuilder suggestionbuilder = new StringBuilder(firstsuggestion);
for (int i = 1; i < onewordsuggestioncount; i++) {
String onesuggestion = allsuggestions.get(i);
suggestionbuilder.append(",");
suggestionbuilder.append(onesuggestion);
}
String onewordsuggestions = suggestionbuilder.toString();
tfSuggestions1.setText(onewordsuggestions);
} else {
// No suggestions for this mistake
tfSuggestions1.setText("No suggestions");
}
btNextWrongWord.setEnabled(true);
// See if the ActionListener for getting the next wrong word and suggestions
// has already been defined. Unregister it if it has,so only one action listener
// will be registered at one time.
if (nextWord != null) {
btNextWrongWord.removeActionListener(nextWord);
}
// Define the ActionListener (already instantiated as a private field)
nextWord = new ActionListener() {
//Initialize a variable to track the index of the allwrongwords list
int wordnumber = 1;
public void actionPerformed(ActionEvent e) {
if (wordnumber < no_of_mistakes) {
// get wrong word in index position wordnumber in allwrongwords
String onewrongword = allwrongwords.get(wordnumber).getWord();
//next part is same as code for first wrong word
tfWrongWord1.setText(onewrongword);
int onewordsuggestioncount = allwrongwords.get(wordnumber).getSuggestionCount();
if (onewordsuggestioncount > 0) {
List<String> allsuggestions = allwrongwords.get(wordnumber).getSuggestions();
String firstsuggestion = allsuggestions.get(0);
StringBuilder suggestionbuilder = new StringBuilder(firstsuggestion);
for (int j = 1; j < onewordsuggestioncount; j++) {
String onesuggestion = allsuggestions.get(j);
suggestionbuilder.append(",");
suggestionbuilder.append(onesuggestion);
}
String onewordsuggestions = suggestionbuilder.toString();
tfSuggestions1.setText(onewordsuggestions);
} else {
tfSuggestions1.setText("No suggestions");
}
// increase i by 1
wordnumber++;
} else {
// No more wrong words! Disable next word button
// Enable Check button
btNextWrongWord.setEnabled(false);
btCheck.setEnabled(true);
}
}
};
// Register the ActionListener
btNextWrongWord.addActionListener(nextWord);
} else {
// The text has no mistakes
// Enable Check button
tfWrongWords.setText("No wrong words");
tfSuggestions1.setText("No suggestions");
tfWrongWord1.setText("--");
btCheck.setEnabled(true);
}
} catch (Exception ex) {
ex.printStackTrace();
}
// Clear the progress bar
pbProgress.setIndeterminate(false);
pbProgress.setString("");
}
});
}
};
java.util.concurrent.Future result = port.checkTextBodyV2Async(bodyText,asyncHandler);
while (!result.isDone()) {
try {
//Display a message that the application is waiting for a response from the server
tfWrongWords.setText("Waiting...");
Thread.sleep(100);
} catch (InterruptedException ex) {
Logger.getLogger(MainForm.class.getName()).log(Level.SEVERE,null,ex);
}
}
}
- 按 Ctrl-Shift-I 组合键(在 Mac 上按 ?-Shift-I 组合键)并修复导入。这样会添加以下 import 语句:
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.swing.SwingUtilities;
现在,您可以构建并运行应用程序了!遗憾的是,您不可能在获取服务器响应的很长延迟时间里看到所发生的变化,因为服务的处理速度相当快。
应用所学知识
现在,您已在 IDE 中创建了第一个 Web 服务客户端,接下来该您大显身手,对应用程序进行全方位扩展了。下面建议了两项可着手执行的任务。
- 在 Servlet 中添加错误处理代码。
- 重写客户端,以使用户可与从 Web 服务返回的数据进行交互。
原文地址:
https://netbeans.org/kb/docs/websvc/client_zh_CN.html