下面是服务器端的类HelloWorld.java:
package org.young.ws;
import javax.jws.WebMethod;
import javax.jws.WebParam;
import javax.jws.WebResult;
import javax.jws.WebService;
import javax.jws.soap.SOAPBinding;
@WebService(targetNamespace = "http://localhost")
@SOAPBinding(style = SOAPBinding.Style.RPC)
publicclass HelloWorld {
@WebMethod(action="toSayHello",operationName="toSayHello",exclude=false)
@WebResult(name="returnWord")//自定义该方法返回值在WSDL中相关的描述
public String sayHello(@WebParam(name="userName")String userName) {
return"Hello:" + userName;
}
@WebMethod
publicint getExp(int i, int j) {
return i / j;
}
}
这是服务器端普通的业务类,通过@WebService、@WebMethod等注释描述来生成WSDL文件。
STEP 2,执行wsgen命令
本 例中到HelloWorld类所在的目录中新建一个命名为wsdl的文件夹,运行:wsgen -cp ./bin -r ./wsdl -s ./src -d ./bin -wsdl org.young.ws.HelloWorld。执行后会在wsdl文件夹中生成HelloWorld的wsdl描述文件,src文件夹中生成 依赖类,如异常说明类,bin中生成依赖类的class文件
STEP 3,发布Web Service Bean ---JDK6内置的http server
启动服务类StartService.java:
package org.young.ws;
import javax.xml.ws.Endpoint;
publicclass StartService {
publicstaticvoid main(String[] args) {
Endpoint.publish("http://localhost:8080/webservice/hws", new HelloWorld());
}
}
此类很简单,能过Endpoint类的publish()方法发布实例发布地址为:
http://localhost:8080/webservice/hws,必需明确指明http协议,主机IP 地址及端口号,在IE上输入http://localhost:8080/webservice/hws?wsdl返回以下内容说明发布成功
<?xml version="1.0" encoding="UTF-8" ?>
<definitions xmlns="http://schemas.xmlsoap.org/wsdl/"
xmlns:tns="http://localhost"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"
targetNamespace="http://localhost"
name="HelloWorldService">
<types />
<message name="toSayHello">
<part name="userName" type="xsd:string" />
</message>
<message name="toSayHelloResponse">
<part name="returnWord" type="xsd:string" />
</message>
<message name="getExp">
<part name="arg0" type="xsd:int" />
<part name="arg1" type="xsd:int" />
</message>
<message name="getExpResponse">
<part name="return" type="xsd:int" />
</message>
<portType name="HelloWorld">
<operation name="toSayHello" parameterOrder="userName">
<input message="tns:toSayHello" />
<output message="tns:toSayHelloResponse" />
</operation>
<operation name="getExp" parameterOrder="arg0 arg1">
<input message="tns:getExp" />
<output message="tns:getExpResponse" />
</operation>
</portType>
<binding name="HelloWorldPortBinding" type="tns:HelloWorld">
<soap:binding style="rpc"
transport="http://schemas.xmlsoap.org/soap/http" />
<operation name="toSayHello">
<soap:operation soapAction="toSayHello" />
<input>
<soap:body use="literal"
namespace="http://localhost" />
</input>
<output>
<soap:body use="literal"
namespace="http://localhost" />
</output>
</operation>
<operation name="getExp">
<soap:operation soapAction="" />
<input>
<soap:body use="literal"
namespace="http://localhost" />
</input>
<output>
<soap:body use="literal"
namespace="http://localhost" />
</output>
</operation>
</binding>
<service name="HelloWorldService">
<port name="HelloWorldPort"
binding="tns:HelloWorldPortBinding">
<soap:address
location="http://localhost:8080/webservice/hws" />
</port>
</service>
</definitions>
增加一个Servlet,用于启动时注册服务
package org.young.ws.servlet;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.xml.ws.Endpoint;
import org.young.ws.HelloWorld;
public class WebServiceStarter extends HttpServlet {
public WebServiceStarter() {
super();
}
public void destroy() {
super.destroy();
}
public void init() throws ServletException {
System.out.println("准备启动服务");
Endpoint.publish("http://localhost:8080/webservice/hws", new HelloWorld());
System.out.println("服务启动完毕");
}
}
web.xml
<?xml version="1.0" encoding="ISO-8859-1"?>
<web-app xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
version="3.0"
metadata-complete="true">
<description>
WebService Examples.
</description>
<display-name>WebService Examples</display-name>
<!-- Define servlets that are included in the example application -->
<servlet>
<servlet-name>WebServiceStarter</servlet-name>
<servlet-class>org.young.ws.servlet.WebServiceStarter</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
</web-app>
STEP 4,生成客户端执行类
在cmd命令中执行 wsimport -d ./bin -s ./src - p org.young.ws.client
http://10.168.189.182:8080/webservice/hws?wsdl 后在在src目录下生成客户端调用的两个类:
org.young.ws.client.HelloWorld.java 根据wsdl描述生成的客户端执行类
org.young.ws.client.HelloWorldServices.java 通过此类负责解悉wsdl初始化客户端HelloWorld实例
在bin目录下生成对应的类文件。
注意:执行wsimport命令时STEP 3的服务必需启动,否则无法生成
STEP 5,客户端调用
客户端调用过程ClientRun.java:
package org.young.ws.client;
import org.young.ws.*;
publicclass ClientTest {
publicstaticvoid main(String[] args) {
HelloWorldService hws = new HelloWorldService();
HelloWorld hw = hws.getHelloWorldPort();
System.out.println(hw.getExp(9, 3));
System.out.println(hw.toSayHello("kevin"));
}
}
启动STEP 3中的服务,运行Client后,制控台输出:
3
Hello:kevin
注意:经测试,用Endpoint.publish("http://localhost:8080/webservice/hws", new HelloWorld())方式在Tomcat6中发布,不会存在端口号与路径冲突。
JKD6 中定义的Web Service注释
1. @WebService 标注要暴露为Web Services的类或接口 ,用于申修饰类或接口,包含属性
targetNamespace 定义命名空间,默认为”http://”+”包名倒排”
name Web Service 的名称,默认为类名,例如:
<definitions targetNamespace="http://localhost/"
name="HelloWorldService">
portName Web Service 的端口名称
serviceName Web Service 的服务名称,例如
<service name="HelloWorldService">
<port name="HelloWorldPort"
binding="tns:HelloWorldPortBinding">
...
</port>
</service>
wsdlLocation描述服务的预定义 WSDL 的位置
endpointInterface定义服务抽象 Web Service 协定的服务端点接口的完整名称
2. @SOAPBinding 定义Web Service 在SOAP中的消息协议,用于申修饰类或接口,包含属性
style 定义消息的编码类型
user 定义消息的格式化类型
3. @WebMethod 定义Web Service运作的方法,包含属性
action 操作的活动
operationName与此方法匹配的 wsdl:operation 的名称
exclude 标注此方法是否被暴露,默认为false
4. @WebResult 定义返回值,返回值类型不能为接口类或抽象类,而且必须有个不带参的构造函数,包含属性
name返回值的名称
partName表示此返回值的 wsdl:part 的名称
targetNamespace返回值的 XML 名称空间
header如果为 true,则结果是从消息头而不是消息正文获取的
5. @WebParam 定义方法的参数,参数类型不能为接口类或抽象类,而且必须有个不带参的构造函数,包含属性
name参数名称
partName表示此参数的 wsdl:part 的名称
targetNamespace参数的 XML 名称空间
header如果为 true,则结果是从消息头而不是消息正文获取的
mode参数的流向(IN、OUT 或 INOUT 之一)
wsgen与wsimport命令说明
wsgen 命令的主要功能是用来生成合适的JAX-WS。它读取Web Service的终端类文件,在我们的例子中就是 org.young.ws.HelloWorld,同时生成所有用于发布Web Service所依赖的源代码文件和经过编译过的二进制类文件,通常Web Service Bean中用到的异常类会另外生成一个描述Bean。它还能生成WSDL和符合规范的HelloWorld类Web Service。wsgen从资源文件生成一个完整的操作列表并验证是合法的。如果Web Service Bean中的主法有申明抛出异常,这一步是必需的,否则服务器无法绑定该对像。
命令参数说明:
-cp 定义classpath
-r 生成 bean的wsdl文件的存放目录
-s 生成发布Web Service的源代码文件的存放目录(如果方法有抛出异常,则会生成该异常的描述类源文件)
-d 生成发布Web Service的编译过的二进制类文件的存放目录(该异常的描述类的class文件)
wsimport命令的主要功能是根据wsdl文件生成客户端存根及框架,负责与Web Service 服务器通信,并在将其封装成实例,客户端可以直接使用,就像使用本地实例一样。
命令参数说明:
-d 生成客户端执行类的class文件的存放目录
-s 生成客户端执行类的源文件的存放目录
-p 定义生成类的包名
三、 附录:WSDL说明
1、WSDL 文档结构
WSDL 文档是利用这些主要的元素来描述某个 web service 的:
元素
定义
<portType>
web service 执行的操作
<message>
web service 使用的消息
<types>
web service 使用的数据类型
<binding>
web service 使用的通信协议
一个 WSDL 文档的主要结构是类似这样的:
<definitions>
<types>
definition of types........
</types>
<message>
definition of a message....
</message>
<portType>
definition of a port.......
</portType>
<binding>
definition of a binding....
</binding>
</definitions>
WSDL 文档可包含其它的元素,比如 extension 元素,以及一个 service 元素,此元素可把若干个 web services 的定义组合在一个单一的 WSDL 文档中。
WSDL 端口
<portType> 元素是最重要的 WSDL 元素。
它可描述一个 web service、可被执行的操作,以及相关的消息。
可以把 <portType> 元素比作传统编程语言中的一个函数库(或一个模块、或一个类)。
WSDL 消息
<message> 元素定义一个操作的数据元素。
每个消息均由一个或多个部件组成。可以把这些部件比作传统编程语言中一个函数调用的参数。
WSDL types
<types> 元素定义 web service 使用的数据类型。
为了最大程度的平台中立性,WSDL 使用 XML Schema 语法来定义数据类型。
WSDL Bindings
<binding> 元素为每个端口定义消息格式和协议细节。
WSDL 实例
这是某个 WSDL 文档的简化的片段:
<message name="getTermRequest">
<part name="term" type="xs:string"/>
</message>
<message name="getTermResponse">
<part name="value" type="xs:string"/>
</message>
<portType name="glossaryTerms">
<operation name="getTerm">
<input message="getTermRequest"/>
<output message="getTermResponse"/>
</operation>
</portType>
在这个例子中,<portType> 元素把 "glossaryTerms" 定义为某个端口的名称,把 "getTerm" 定义为某个操作的名称。
操作 "getTerm" 拥有一个名为 "getTermRequest" 的输入消息,以及一个名为 "getTermResponse" 的输出消息。
<message> 元素可定义每个消息的部件,以及相关联的数据类型。
对比传统的编程,glossaryTerms 是一个函数库,而 "getTerm" 是带有输入参数 "getTermRequest" 和返回参数 getTermResponse 的一个函数。
2、WSDL 端口
<portType> 元素是最重要的 WSDL 元素。
它可描述一个 web service、可被执行的操作,以及相关的消息。
端口定义了指向某个 web service 的连接点。可以把它元素比作传统编程语言中的一个函数库(或一个模块、或一个类),而把每个操作比作传统编程语言中的一个函数。
操作类型
请求-响应是最普通的操作类型,不过 WSDL 定义了四种类型:
类型
定义
One-way
此操作可接受消息,但不会返回响应。
Request-response
此操走可接受一个请求并会返回一个响应
Solicit-response
此操作可发送一个请求,并会等待一个响应。
Notification
此造作可发送一条消息,但不会等待响应。
One-Way 操作
一个 one-way 操作的例子:
<message name="newTermValues">
<part name="term" type="xs:string"/>
<part name="value" type="xs:string"/>
</message>
<portType name="glossaryTerms">
<operation name="setTerm">
<input name="newTerm" message="newTermValues"/>
</operation>
</portType >
在这个例子中,端口 "glossaryTerms" 定义了一个名为 "setTerm" 的 one-way 操作。
这个 "setTerm" 操作可接受新术语表项目消息的输入,这些消息使用一条名为 "newTermValues" 的消息,此消息带有输入参数 "term" 和 "value"。不过,没有为这个操作定义任何输出。
Request-Response 操作
一个 request-response 操作的例子:
<message name="getTermRequest">
<part name="term" type="xs:string"/>
</message>
<message name="getTermResponse">
<part name="value" type="xs:string"/>
</message>
<portType name="glossaryTerms">
<operation name="getTerm">
<input message="getTermRequest"/>
<output message="getTermResponse"/>
</operation>
</portType>
在这个例子中,端口 "glossaryTerms" 定义了一个名为 "getTerm" 的 request-response 操作。
"getTerm" 操作会请求一个名为 "getTermRequest" 的输入消息,此消息带有一个名为 "term" 的参数,并将返回一个名为 "getTermResponse" 的输出消息,此消息带有一个名为 "value" 的参数。
WSDL 绑定可为 web service 定义消息格式和协议细节。
绑定到 SOAP
一个 请求 - 响应 操作的例子:
<message name="getTermRequest">
<part name="term" type="xs:string"/>
</message>
<message name="getTermResponse">
<part name="value" type="xs:string"/>
</message>
<portType name="glossaryTerms">
<operation name="getTerm">
<input message="getTermRequest"/>
<output message="getTermResponse"/>
</operation>
</portType>
<binding type="glossaryTerms" name="b1">
<soap:binding style="document"
transport="http://schemas.xmlsoap.org/soap/http" />
<operation>
<soap:operation
soapAction="http://example.com/getTerm"/>
<input>
<soap:body use="literal"/>
</input>
<output>
<soap:body use="literal"/>
</output>
</operation>
</binding>
binding 元素有两个属性 - name 属性和 type 属性。
name 属性定义 binding 的名称,而 type 属性指向用于 binding 的端口,在这个例子中是 "glossaryTerms" 端口。
soap:binding 元素有两个属性 - style 属性和 transport 属性。
style 属性可取值 "rpc" 或 "document"。在这个例子中我们使用 document。transport 属性定义了要使用的 SOAP 协议。在这个例子中我们使用 HTTP。
operation 元素定义了每个端口提供的操作符。
对于每个操作,相应的 SOAP 行为都需要被定义。同时您必须如何对输入和输出进行编码。在这个例子中我们使用了 "literal"。