JAX-WS(Java API for XML-Based Web Services),是创建web服务的Java编程接口,特别是SOAP服务。是Java XML编程接口之一,是Java SE 和Java EE 平台的一部分。
JAX-WS 2.0 规范是代替JAX-RPC 1.0的下一代Web服务API。JAX-RPC(The Java API for XML Based RPC)是基于XML的RPC(远程过程调用)的Java API。
JAX-WS 2.0 使用JAXB(The Java Architecture for XML Binding)进行XML数据绑定,所谓绑定就是指XML请求和XML响应如何映射成java对象。
https://jcp.org/en/jsr/detail?id=224

https://jakarta.ee/specifications/xml-web-services/2.3/

https://jakarta.ee/specifications/xml-web-services/3.0/

https://jakarta.ee/specifications/xml-web-services/4.0/

Jakarta Web Services Metadata项目是Jakarta XML Web Services 项目的一部分,它用Java为web 服务定义了一个编程模型。
代码库:https://github.com/jakartaee/jws-api

Jakarta Web Services Metadata 3.0 Specification Document (HTML):
https://jakarta.ee/specifications/web-services-metadata/3.0/ws-metadata-spec-3.0
Jakarta Web Services Metadata 3.0 Javadoc:
https://jakarta.ee/specifications/web-services-metadata/3.0/apidocs/jakarta.jws/module-summary.html
https://javaee.github.io/metro-jax-ws/

https://github.com/javaee/metro-jax-ws


主页:
https://eclipse-ee4j.github.io/metro-jax-ws/

代码库:
https://github.com/eclipse-ee4j/metro-jax-ws

XML web服务的Eclipse 实现是一个web服务框架,它为最终用户和中间件开发者开发web服务解决方案提供了工具和基础设施(infrastructure)。
使用XML web服务的Eclipse 实现,客户端和web有一个大的优势:Java编程语言的平台无关性。
Java SE 11或以后版本
Ant 1.10.6或以后版本
在GlassFish v7.x 、Apache Tomcat 10.1.x下测试过
打开主页:https://eclipse-ee4j.github.io/metro-jax-ws/

点击右侧的Download就可以下载独立发布包,例如,下载jaxws-ri-4.0.0.zip。
解压后的目录:

bin子目录下的内容:

docs子目录下的内容:

lib子目录下的内容:

samples子目录下的内容:

设置CATALINA_HOME环境变量,指向Tomcat的安装目录,例如:


在cmd窗口,切换到Jakarta XML Web Services Eclipse实现解压后的目录,例如:D:\jaxws-ri-4.0.0\jaxws-ri

这个目录下有build.xml文件:

运行ant install命令。这个命令其实执行了两个大的操作:
build.xml文件片段:
执行ant install命令的窗口输出信息:

https://eclipse-ee4j.github.io/metro-jax-ws/4.0.0/docs/release-documentation.html#jar-dependency

fromjava这个样例演示了从一个Java service endpoint的实现开始,如何构建、部署、调用一个简单的web服务。其中Java service endpoint的实现使用了Java注释。

fromjava的目录结构:

服务实现类只有一个方法addNumbers,输入两个整数,返回一个整数,抛出AddNumbersException异常:

运行web服务有两种方式:


运行ant clean的作用是删掉${build.home}目录。其中${build.home}目录指向${basedir}/build,就是fromjava/build目录。(其实这个命令不运行也可以,因为下一步运行ant server命令的时候其中含有ant clean的步骤)

运行ant server -Dtomcat=true的作用是构建、部署web服务的WAR包,因为要将web服务运行在tomcat中,所以加了-Dtomcat=true这个属性。这个命令做了四个工作:
<target name="server" depends="setup">
<antcall target="clean"/>
<antcall target="build-server-java"/>
<antcall target="create-war"/>
<antcall target="deploy"/>
</target>
运行输出:


现在fromjava目录下的文件布局(注:照理${env.AS_HOME}这个目录不应该生成的,对我们没有什么用):
D:\JAXWS-RI-4.0.0\JAXWS-RI\SAMPLES\FROMJAVA
│ build.xml
│ Readme.txt
│
├─${env.AS_HOME}
│ └─domains
│ └─domain1
│ └─autodeploy
│ jaxws-fromjava.war
│
├─build
│ ├─classes
│ │ └─fromjava
│ │ └─server
│ │ │ AddNumbersException.class
│ │ │ AddNumbersImpl.class
│ │ │ AddWebservice.class
│ │ │ EndpointStopper$1.class
│ │ │ EndpointStopper.class
│ │ │
│ │ └─jaxws
│ │ AddNumbers.class
│ │ AddNumbers.java
│ │ AddNumbersExceptionBean.class
│ │ AddNumbersExceptionBean.java
│ │ AddNumbersResponse.class
│ │ AddNumbersResponse.java
│ │
│ └─war
│ jaxws-fromjava.war
│
├─etc
│ deploy-targets.xml
│ sun-jaxws.xml
│ web.xml
│
└─src
└─fromjava
├─client
│ AddNumbersClient.java
│
└─server
AddNumbersException.java
AddNumbersImpl.java
AddWebservice.java
EndpointStopper.java
到下查看,war包已经拷贝过来了:

因为生成客户端代码的时候要访问web服务动态生成的wsdl文件,所以要先启动tomcat。


到下查看,war包已经被自动部署了:

部署以后的文件布局:
D:\APACHE-TOMCAT-10.1.13\WEBAPPS\JAXWS-FROMJAVA
├─META-INF
│ MANIFEST.MF
│ war-tracker
│
└─WEB-INF
│ sun-jaxws.xml
│ web.xml
│
└─classes
└─fromjava
└─server
│ AddNumbersException.class
│ AddNumbersImpl.class
│ AddWebservice.class
│ EndpointStopper$1.class
│ EndpointStopper.class
│
└─jaxws
AddNumbers.class
AddNumbers.java
AddNumbersExceptionBean.class
AddNumbersExceptionBean.java
AddNumbersResponse.class
AddNumbersResponse.java
访问http://localhost:8080/jaxws-fromjava/addnumbers?wsdl
可以看到,正常生成了wsdl文件。生成客户端代码的时候,就要访问这个wsdl的地址。

<?xml version='1.0' encoding='UTF-8'?><!-- Published by XML-WS Runtime (https://github.com/eclipse-ee4j/metro-jax-ws). Runtime's version is XML-WS Runtime 4.0.0 git-revision#129f787. --><!-- Generated by XML-WS Runtime (https://github.com/eclipse-ee4j/metro-jax-ws). Runtime's version is XML-WS Runtime 4.0.0 git-revision#129f787. --><definitions xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd" xmlns:wsp="http://www.w3.org/ns/ws-policy" xmlns:wsp1_2="http://schemas.xmlsoap.org/ws/2004/09/policy" xmlns:wsam="http://www.w3.org/2007/05/addressing/metadata" xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/" xmlns:tns="http://server.fromjava/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns="http://schemas.xmlsoap.org/wsdl/" targetNamespace="http://server.fromjava/" name="AddNumbersImplService">
<types>
<xsd:schema>
<xsd:import namespace="http://server.fromjava/" schemaLocation="http://localhost:8080/jaxws-fromjava/addnumbers?xsd=1"/>
</xsd:schema>
</types>
<message name="addNumbers">
<part name="parameters" element="tns:addNumbers"/>
</message>
<message name="addNumbersResponse">
<part name="parameters" element="tns:addNumbersResponse"/>
</message>
<message name="AddNumbersException">
<part name="fault" element="tns:AddNumbersException"/>
</message>
<portType name="AddNumbersImpl">
<operation name="addNumbers">
<input wsam:Action="http://server.fromjava/AddNumbersImpl/addNumbersRequest" message="tns:addNumbers"/>
<output wsam:Action="http://server.fromjava/AddNumbersImpl/addNumbersResponse" message="tns:addNumbersResponse"/>
<fault message="tns:AddNumbersException" name="AddNumbersException" wsam:Action="http://server.fromjava/AddNumbersImpl/addNumbers/Fault/AddNumbersException"/>
</operation>
</portType>
<binding name="AddNumbersImplPortBinding" type="tns:AddNumbersImpl">
<soap:binding transport="http://schemas.xmlsoap.org/soap/http" style="document"/>
<operation name="addNumbers">
<soap:operation soapAction=""/>
<input>
<soap:body use="literal"/>
</input>
<output>
<soap:body use="literal"/>
</output>
<fault name="AddNumbersException">
<soap:fault name="AddNumbersException" use="literal"/>
</fault>
</operation>
</binding>
<service name="AddNumbersImplService">
<port name="AddNumbersImplPort" binding="tns:AddNumbersImplPortBinding">
<soap:address location="http://localhost:8080/jaxws-fromjava/addnumbers"/>
</port>
</service>
</definitions>
运行ant clean:

运行ant client的目的是为了生成客户端代码并编译:
<target name="generate-client" depends="setup">
<wsimport
debug="true"
verbose="${verbose}"
keep="true"
destdir="${build.classes.home}"
package="fromjava.client"
wsdl="http://localhost:8080/jaxws-fromjava/addnumbers?wsdl">
</wsimport>
</target>
<target name="client" depends="generate-client">
<javac
fork="true"
srcdir="${basedir}/src"
destdir="${build.classes.home}"
includes="**/client/**,**/common/**">
<classpath refid="jaxws.classpath"/>
</javac>
</target>

运行后,客户端代码布局:
D:\JAXWS-RI-4.0.0\JAXWS-RI\SAMPLES\FROMJAVA\BUILD
├─classes
│ └─fromjava
│ └─client
│ AddNumbers.class
│ AddNumbers.java
│ AddNumbersClient.class
│ AddNumbersException.class
│ AddNumbersException.java
│ AddNumbersException_Exception.class
│ AddNumbersException_Exception.java
│ AddNumbersImpl.class
│ AddNumbersImpl.java
│ AddNumbersImplService.class
│ AddNumbersImplService.java
│ AddNumbersResponse.class
│ AddNumbersResponse.java
│ ObjectFactory.class
│ ObjectFactory.java
│ package-info.class
│ package-info.java
│
└─war
执行ant run表示运行客户端。
<target name="run">
<java fork="true" classname="fromjava.client.AddNumbersClient">
<classpath>
<path refid="jaxws.classpath"/>
<pathelement location="${build.classes.home}"/>
<pathelement location="${basedir}/etc"/>
</classpath>
</java>
</target>

在运行ant run前,启动Wireshark抓包工具,在环回接口上抓包,目的是看看调用web服务的协议是否用了SOAP协议(用http过滤):


计算10+20的请求:

计算10+20的响应:

计算-10+20的请求:

计算-10+20的响应:

运行ant clean server-j2se生成服务端代码,并且用Endpoint API来部署:
<target name="server-j2se" depends="setup">
<antcall target="clean"/>
<antcall target="build-server-java"/>
<echo message="Starting endpoint... To stop: ant server-j2se-stop "/>
<java fork="true" classname="fromjava.server.AddWebservice">
<classpath>
<path refid="jaxws.classpath"/>
<pathelement location="${build.classes.home}"/>
</classpath>
</java>
</target>

生成的文件布局:
D:\JAXWS-RI-4.0.0\JAXWS-RI\SAMPLES\FROMJAVA\BUILD
├─classes
│ └─fromjava
│ └─server
│ │ AddNumbersException.class
│ │ AddNumbersImpl.class
│ │ AddWebservice.class
│ │ EndpointStopper$1.class
│ │ EndpointStopper.class
│ │
│ └─jaxws
│ AddNumbers.class
│ AddNumbers.java
│ AddNumbersExceptionBean.class
│ AddNumbersExceptionBean.java
│ AddNumbersResponse.class
│ AddNumbersResponse.java
│
└─war
访问http://localhost:8080/jaxws-fromjava/addnumbers?wsdl
可以正常生成wsdl文件:

另外打开一个cmd窗口,运行ant clean client run,目的是在根据endpoint发布的wsdl文件生成客户端代码、编译、并且运行。


运行后,客户端的代码布局:
D:\JAXWS-RI-4.0.0\JAXWS-RI\SAMPLES\FROMJAVA\BUILD
├─classes
│ └─fromjava
│ └─client
│ AddNumbers.class
│ AddNumbers.java
│ AddNumbersClient.class
│ AddNumbersException.class
│ AddNumbersException.java
│ AddNumbersException_Exception.class
│ AddNumbersException_Exception.java
│ AddNumbersImpl.class
│ AddNumbersImpl.java
│ AddNumbersImplService.class
│ AddNumbersImplService.java
│ AddNumbersResponse.class
│ AddNumbersResponse.java
│ ObjectFactory.class
│ ObjectFactory.java
│ package-info.class
│ package-info.java
│
└─war
<target name="server-j2se-stop" depends="setup">
<get src="http://localhost:9090/stop" dest="stop.status"/>
</target>

本样例来自Jakarta XML Web Services Eclipse实现发布包中的restful样例:

这个样例展示了基于REST风格的web服务,利用XML/HTTP绑定。在这种情况下,不需要发布wsdl文件。
服务端实现了一个web服务的Provider:

客户端访问web服务,样例演示了两种访问方式:
方式一:使用Jakarta XML Web Services Dispatch,利用HTTP GET请求方法,以及Jakarta XML Web Services MessageContext 的属性 PATH_INFO 和 QUERY_STRING。
方式二:使用URLConnection。
另外,也可以直接在浏览器中访问,就作为方式三吧。
restful样例的目录结构:
D:\JAXWS-RI-4.0.0\JAXWS-RI\SAMPLES\RESTFUL
│ build.xml
│ Readme.txt
│
├─etc
│ deploy-targets.xml
│ sun-jaxws.xml
│ web.xml
│
└─src
└─restful
├─client
│ AddNumbersClient.java
│ DispatchAddNumbersClient.java
│
└─server
AddNumbersImpl.java
web服务需要两个输入参数,然后求和,将计算结果返回。


运行ant clean server -Dtomcat=true,其中tomcat=true这个系统属性表示将服务端的web服务部署到tomcat上。
运行输出信息:


现在restful目录下的文件布局:
D:\JAXWS-RI-4.0.0\JAXWS-RI\SAMPLES\RESTFUL
│ build.xml
│ Readme.txt
│
├─build
│ ├─classes
│ │ └─restful
│ │ └─server
│ │ AddNumbersImpl.class
│ │
│ └─war
│ jaxws-restful.war
│
├─etc
│ deploy-targets.xml
│ sun-jaxws.xml
│ web.xml
│
└─src
└─restful
├─client
│ AddNumbersClient.java
│ DispatchAddNumbersClient.java
│
└─server
AddNumbersImpl.java
到



到

部署以后的文件布局:
D:\APACHE-TOMCAT-10.1.13\WEBAPPS\JAXWS-RESTFUL
├─META-INF
│ MANIFEST.MF
│ war-tracker
│
└─WEB-INF
│ sun-jaxws.xml
│ web.xml
│
└─classes
└─restful
└─server
AddNumbersImpl.class
直接在浏览器中访问,服务已经正常启动:

用Wireshark在本地环回接口上开启抓http的包,在浏览器中重新访问。

响应报文是HTTP/XML形式的:

运行ant clean client run-dispatch:

客户端运行以后的输出目录布局:
D:\JAXWS-RI-4.0.0\JAXWS-RI\SAMPLES\RESTFUL\BUILD
├─classes
│ └─restful
│ └─client
│ AddNumbersClient.class
│ DispatchAddNumbersClient.class
│
└─war
运行客户端的时候,同步用Wireshark在本地环回接口抓到的http的包:

请求参数放在URL的查询部分::

响应:

请求参数放在URL的路径部分::

响应:

运行ant clean client run-url:

客户端运行以后的输出目录布局:
D:\JAXWS-RI-4.0.0\JAXWS-RI\SAMPLES\RESTFUL\BUILD
├─classes
│ └─restful
│ └─client
│ AddNumbersClient.class
│ DispatchAddNumbersClient.class
│
└─war
用WireShark在本地环回接口上同步抓到的包:

请求参数放在URL的查询部分:

响应:

请求参数放在URL的路径中:

响应:

这种方式不需要安装Jakarta XML Web Services Eclipse实现单独的发布包。
创建一个maven工程,在maven工程的pom.xml文件中增加如下依赖:
jakarta.xml.ws
jakarta.xml.ws-api
4.0.0
com.sun.xml.ws
jaxws-rt
4.0.0
因为将web服务部署servlet容器(例如tomcat)的时候,依赖的包是放在一个目录下面的。所以,要部署的时候,可以先使用maven的dependency插件,将项目依赖从maven仓库中提取出来,拷贝到一个指定的位置(如果使用ant构建的话,方便ant的build文件编写)。
例如,在cmd窗口,切换到maven工程的根目录下面,运行mvn dependency:copy-dependencies -Dmdep.stripVersion=true -DoutputDirectory=lib,指定将依赖包拷贝到项目根目录下面的lib子目录下,拷贝时去掉了依赖包文件名中的版本号:



到<项目根目录>/lib子目录下查看,内容拷贝过来了:
