1. 概述
Java API for XML Web Service(JAX-WS)是用于创建和使用SOAP(简单对象访问协议) Web服务的标准化API。
在本文中,我们将创建一个SOAP Web服务并使用JAX-WS连接到它。
2. SOAP
SOAP是一种通过网络发送消息的XML规范,SOAP消息独立于任何操作系统,并且可以使用多种通信协议,包括HTTP和SMTP。
SOAP是XML密集型的,因此最好与工具/框架一起使用。JAX-WS是一个简化SOAP使用的框架,它是标准Java的一部分。
3. 自上而下与自下而上
构建SOAP Web服务有两种方法,我们可以采用自上而下的方法,也可以采用自下而上的方法。
在自上而下(契约优先)方法中,创建WSDL文档,并从WSDL生成必要的Java类。在自下而上(契约最后)方法中,编写Java类,并从Java类生成WSDL。
编写WSDL文件可能非常困难,具体取决于你的Web服务的复杂程度,这使得自下而上的方法成为一种更简单的选择。另一方面,由于你的WSDL是从Java类生成的,因此代码中的任何更改都可能导致WSDL发生变化,自上而下的方法并非如此。
在本文中,我们将介绍这两种方法。
4. Web服务定义语言(WSDL)
WSDL是可用服务的契约定义,它是输入/输出消息以及如何调用Web服务的规范,它与语言无关,并以XML定义。
让我们看一下WSDL文档的主要元素。
4.1 定义
definitions元素是所有WSDL文档的根元素,它定义服务的名称、命名空间等:
<definitions xmlns="http://schemas.xmlsoap.org/wsdl/"
xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"
xmlns:tns="http://jaxws.tuyucheng.com/"
xmlns:wsam="http://www.w3.org/2007/05/addressing/metadata"
xmlns:wsp="http://www.w3.org/ns/ws-policy"
xmlns:wsp1_2="http://schemas.xmlsoap.org/ws/2004/09/policy"
xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
targetNamespace="http://jaxws.tuyucheng.com/"
name="EmployeeService">
...
</definitions>
4.2 类型
types元素定义Web服务使用的数据类型,WSDL使用XSD(XML模式定义)作为类型系统,有助于实现互操作性:
<definitions ...>
...
<types>
<xsd:schema>
<xsd:import namespace="http://jaxws.tuyucheng.com/"
schemaLocation = "http://localhost:8080/employeeservice?xsd=1" />
</xsd:schema>
</types>
...
</definitions>
4.3 消息
message元素提供了正在传输的数据的抽象定义,每个message元素描述服务方法的输入或输出以及可能出现的异常:
<definitions ...>
...
<message name="getEmployee">
<part name="parameters" element="tns:getEmployee" />
</message>
<message name="getEmployeeResponse">
<part name="parameters" element="tns:getEmployeeResponse" />
</message>
<message name="EmployeeNotFound">
<part name="fault" element="tns:EmployeeNotFound" />
</message>
...
</definitions>
4.4 操作和端口类型
portType元素描述了可以执行的每个操作以及所涉及的所有message元素。例如,getEmployee操作指定了请求的输入、输出以及Web服务操作可能引发的故障异常:
<definitions ...>
...
<portType name="EmployeeService">
<operation name="getEmployee">
<input
wsam:Action="http://jaxws.tuyucheng.com/EmployeeService/getEmployeeRequest"
message="tns:getEmployee" />
<output
wsam:Action="http://jaxws.tuyucheng.com/EmployeeService/getEmployeeResponse"
message="tns:getEmployeeResponse" />
<fault message="tns:EmployeeNotFound" name="EmployeeNotFound"
wsam:Action="http://jaxws.tuyucheng.com/EmployeeService/getEmployee/Fault/EmployeeNotFound" />
</operation>
....
</portType>
...
</definitions>
4.5 绑定
binding元素为每个portType提供协议和数据格式的详细信息:
<definitions ...>
...
<binding name="EmployeeServiceImplPortBinding" type="tns:EmployeeService">
<soap:binding transport="http://schemas.xmlsoap.org/soap/http" style="document" />
<operation name="getEmployee">
<soap:operation soapAction="" />
<input>
<soap:body use="literal" />
</input>
<output>
<soap:body use="literal" />
</output>
<fault name="EmployeeNotFound">
<soap:fault name="EmployeeNotFound" use="literal" />
</fault>
</operation>
...
</binding>
...
</definitions>
4.6 服务和端口
service元素定义了web服务支持的端口,service中的port元素定义了服务的名称、绑定和地址:
<definitions ...>
...
<service name="EmployeeService">
<port name="EmployeeServiceImplPort" binding="tns:EmployeeServiceImplPortBinding">
<soap:address location="http://localhost:8080/employeeservice" />
</port>
</service>
...
</definitions>
5. 自上而下(契约优先)方法
让我们从自上而下的方法开始,创建一个WSDL文件employeeservicetopdown.wsdl,为了简单起见,它只有一个方法:
<?xml version="1.0" encoding="UTF-8"?>
<definitions
xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"
xmlns:tns="http://topdown.server.jaxws.tuyucheng.com/"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns="http://schemas.xmlsoap.org/wsdl/"
targetNamespace="http://topdown.server.jaxws.tuyucheng.com/"
qname="EmployeeServiceTopDown">
<types>
<xsd:schema
targetNamespace="http://topdown.server.jaxws.tuyucheng.com/">
<xsd:element name="countEmployeesResponse" type="xsd:int"/>
</xsd:schema>
</types>
<message name="countEmployees">
</message>
<message name="countEmployeesResponse">
<part name="parameters" element="tns:countEmployeesResponse"/>
</message>
<portType name="EmployeeServiceTopDown">
<operation name="countEmployees">
<input message="tns:countEmployees"/>
<output message="tns:countEmployeesResponse"/>
</operation>
</portType>
<binding name="EmployeeServiceTopDownSOAP"
type="tns:EmployeeServiceTopDown">
<soap:binding transport="http://schemas.xmlsoap.org/soap/http"
style="document"/>
<operation name="countEmployees">
<soap:operation
soapAction="http://topdown.server.jaxws.tuyucheng.com/
EmployeeServiceTopDown/countEmployees"/>
<input>
<soap:body use="literal"/>
</input>
<output>
<soap:body use="literal"/>
</output>
</operation>
</binding>
<service name="EmployeeServiceTopDown">
<port name="EmployeeServiceTopDownSOAP"
binding="tns:EmployeeServiceTopDownSOAP">
<soap:address
location="http://localhost:8080/employeeservicetopdown"/>
</port>
</service>
</definitions>
5.1 从WSDL生成Web服务源文件
有多种方法可以从WSDL文档生成Web服务源文件。
一种方法是使用wsimport工具,它是JDK的一部分(位于$JAVA_HOME/bin)。
从命令提示符中:
wsimport -s . -p cn.tuyucheng.taketoday.jaxws.server.topdown employeeservicetopdown.wsdl
使用的命令行选项:-p指定目标包,-s指定放置生成的源文件的位置。
对于更高版本的JDK,我们可以使用MojoHaus的jaxws-maven-plugin,如此处所述。
另外,org.jvnet.jaxb2的maven-jaxb2-plugin也可以派上用场,详情请参阅在Spring中调用SOAP Web服务。
生成的文件:
- EmployeeServiceTopDown.java:是包含方法定义的服务端点接口(SEI)
- ObjectFactory.java:包含工厂方法,用于以编程方式创建模式派生类的实例
- EmployeeServiceTopDown_Service.java:是JAX-WS客户端可以使用的服务提供者类
5.2 Web服务端点接口
wsimport工具已生成Web服务端点接口EmployeeServiceTopDown,它声明了以下Web服务方法:
@WebService(
name = "EmployeeServiceTopDown",
targetNamespace = "http://topdown.server.jaxws.tuyucheng.com/")
@SOAPBinding(parameterStyle = SOAPBinding.ParameterStyle.BARE)
@XmlSeeAlso({
ObjectFactory.class
})
public interface EmployeeServiceTopDown {
@WebMethod(
action = "http://topdown.server.jaxws.tuyucheng.com/"
+ "EmployeeServiceTopDown/countEmployees")
@WebResult(
name = "countEmployeesResponse",
targetNamespace = "http://topdown.server.jaxws.tuyucheng.com/",
partName = "parameters")
public int countEmployees();
}
5.3 Web服务实现
wsimport工具已创建Web服务的结构,我们必须创建Web服务的实现:
@WebService(
name = "EmployeeServiceTopDown",
endpointInterface = "cn.tuyucheng.taketoday.jaxws.server.topdown.EmployeeServiceTopDown",
targetNamespace = "http://topdown.server.jaxws.tuyucheng.com/")
public class EmployeeServiceTopDownImpl
implements EmployeeServiceTopDown {
@Inject
private EmployeeRepository employeeRepositoryImpl;
@WebMethod
public int countEmployees() {
return employeeRepositoryImpl.count();
}
}
6. 自下而上(契约最后)方法
在自下而上的方法中,我们必须创建端点接口和实现类。发布Web服务时,将从这些类生成WSDL。
让我们创建一个Web服务,对Employee数据执行简单的CRUD操作。
6.1 模型类
Employee模型类:
public class Employee {
private int id;
private String firstName;
// standard getters and setters
}
6.2 Web服务端点接口
声明Web服务方法的Web服务端点接口:
@WebService
public interface EmployeeService {
@WebMethod
Employee getEmployee(int id);
@WebMethod
Employee updateEmployee(int id, String name);
@WebMethod
boolean deleteEmployee(int id);
@WebMethod
Employee addEmployee(int id, String name);
// ...
}
此接口定义了Web服务的抽象契约,使用的注解:
- @WebService表示这是一个Web服务接口
- @WebMethod用于定制Web服务操作
- @WebResult用于自定义表示返回值的XML元素名称
6.3 Web服务实现
Web服务端点接口的实现类:
@WebService(endpointInterface = "cn.tuyucheng.taketoday.jaxws.EmployeeService")
public class EmployeeServiceImpl implements EmployeeService {
@Inject
private EmployeeRepository employeeRepositoryImpl;
@WebMethod
public Employee getEmployee(int id) {
return employeeRepositoryImpl.getEmployee(id);
}
@WebMethod
public Employee updateEmployee(int id, String name) {
return employeeRepositoryImpl.updateEmployee(id, name);
}
@WebMethod
public boolean deleteEmployee(int id) {
return employeeRepositoryImpl.deleteEmployee(id);
}
@WebMethod
public Employee addEmployee(int id, String name) {
return employeeRepositoryImpl.addEmployee(id, name);
}
// ...
}
7. 发布Web服务端点
要发布Web服务(自上而下和自下而上),我们需要将Web服务实现的地址和实例传递给javax.xml.ws.Endpoint类的publish()方法:
public class EmployeeServicePublisher {
public static void main(String[] args) {
Endpoint.publish(
"http://localhost:8080/employeeservicetopdown",
new EmployeeServiceTopDownImpl());
Endpoint.publish("http://localhost:8080/employeeservice",
new EmployeeServiceImpl());
}
}
现在我们可以运行EmployeeServicePublisher来启动Web服务,要利用CDI功能,可以将Web服务作为WAR文件部署到WildFly或GlassFish等应用服务器。
8. 远程Web服务客户端
现在让我们创建一个JAX-WS客户端来远程连接到EmployeeService Web服务。
8.1 生成客户端工件
为了生成JAX-WS客户端工件,我们可以再次使用wsimport工具:
wsimport -keep -p cn.tuyucheng.taketoday.jaxws.client http://localhost:8080/employeeservice?wsdl
生成的EmployeeService_Service类封装了使用URL和QName获取服务器端口的逻辑。
8.2 连接到Web服务
Web服务客户端使用生成的EmployeeService_Service连接到服务器并远程进行Web服务调用:
public class EmployeeServiceClient {
public static void main(String[] args) throws Exception {
URL url = new URL("http://localhost:8080/employeeservice?wsdl");
EmployeeService_Service employeeService_Service = new EmployeeService_Service(url);
EmployeeService employeeServiceProxy = employeeService_Service.getEmployeeServiceImplPort();
List<Employee> allEmployees = employeeServiceProxy.getAllEmployees();
}
}
9. 总结
本文简要介绍了使用JAX-WS的SOAP Web服务。
我们采用了自下而上和自上而下的方法来使用JAX-WS API创建SOAP Web服务,还编写了一个可以远程连接到服务器并进行Web服务调用的JAX-WS客户端。
Post Directory
