JAS-WS
的全称为 Java API for XML Services
,代指Java程序设计语言中用于创建 Web Services
的API。Java程序员通过该API可快速开发基于XML的 Web Services
客户端及服务端。
Web Services
是应用程序组件之一,可被其他应用程序使用, XML
是 Web Services
的基础, Web Services
包含以下三大要素。
SOAP WSDL UUDI
常用的 Web Services
框架有 Apache Axis1
、 Apache Axis2
、 Apache CXF
,而 Apache Axis1
已经逐渐被淘汰所以本文不会讨论,重点关注 Apache Axis2
及 Apache CXF
。
在IDEA中新建 Axis2Demo
项目后右键选择 添加框架的支持 并选中 Web Application
。
从Apache Axis2官网处下载 war包 进行部署,将 axis2.war
解压后把 WEB-INF
和 axis2-web
目录复制到项目的 web
目录下(如下图所示)并启动Tomcat Server。
访问 http://localhost:8080/Axis2Demo_war_exploded/axis2-web/index.jsp
出现下图的页面表示部署成功。
在 Axis1
中的全局配置和 Servcies
的配置均在 server-config.wsdd
中进行配置,而 Axis2
则将全局配置单独存放于 WEB-INF/conf/axis2.xml
中, services
的配置文件则位于 servcies
。
新建一个 HelloService
类并编译为 HelloService.class
复制至 WEB-INF/pojo
目录下并重启服务。
// 不能声明package public class HelloService { public HelloService(){} public String sayHello() { return "hello"; } public String sayHelloToPerson(String name) { if (name == null) { name = "nobody"; } return "hello, " + name; } }
重启服务后再次访问 http://localhost:8080/Axis2Demo_war_exploded/services/HelloService?wsdl
即可发现新发布的服务,点击 HelloService
即可查看Axis自动为该服务生成的WSDL,其描述了如何调用服务的方法及返回内容:
使用 SoapUI
客户端调用 HelloService
服务方法:
而之所以 WEB-INF/pojo
目录下的 .class
文件会自动发布为服务是因为在 axis2.xml
配置文件中的 deployer
标签中所配置的该选项。
<deployer extension=".class" directory="pojo" class="org.apache.axis2.deployment.POJODeployer"/> <!-- 如果需要在其他目录自动发布服务只需添加新的标签即可 --> <deployer extension=".class" directory="services" class="org.apache.axis2.deployment.POJODeployer">
上述的方式发布服务需要将编译后的类放置在某个具体的目录中,且不能包含 package
,而使用 *.aar
的方式则可以解决此问题。首先在Project的根目录下新建 META-INF/services.xml
,文件内容可以参考官方示例 version.aar
。
<?xml version="1.0" encoding="UTF-8"?> <service name="HelloService"> <description>一个简单的WebService</description> <!-- 服务全类名 --> <parameter name="ServiceClass">com.ws.test.services.HelloService</parameter> <operation name="send"> <!-- 信息接收器, 无返回值用:RPCInOnlyMessageReceiver--> <messageReceiver mep="http://www.w3.org/ns/wsdl/in-only" class="org.apache.axis2.rpc.receivers.RPCInOnlyMessageReceiver" /> </operation> <operation name="add"> <!-- 信息接收器, 有返回值用:RPCMessageReceiver--> <messageReceiver mep="http://www.w3.org/2004/08/wsdl/in-out" class="org.apache.axis2.rpc.receivers.RPCMessageReceiver" /> </operation> </service>
最终结构为如下所示,在项目根目录中执行 jar cvf HelloService.aar . 进行打包。
将打包后的文件复制至 WEB-INF/services
目录下,即可在服务列表中看到新注册的服务,或者在 Axis
后台中也可以上传包部署(因此如果应用程序的Axis后台可访问且为默认凭据即可部署恶意Service获取权限)。
调用 Web Service
可通过代码的方式实现也可以通过WSDL构造SOAP协议调用方法,最简便的方法则是使用SoapUI,其会根据 Web Service
的WSDL生成对应方法的SOAP协议请求。
// 代码实现Web Service调用 import javax.xml.namespace.QName; import org.apache.axis2.addressing.EndpointReference; import org.apache.axis2.client.Options; import org.apache.axis2.rpc.client.RPCServiceClient; public class WebServiceClient { public static void main(String[] args) throws Exception { RPCServiceClient serviceClient = new RPCServiceClient(); Options options = serviceClient.getOptions(); EndpointReference targetEPR = new EndpointReference("http://192.168.0.105:8080/Axis2Demo_war_exploded/services/HelloService"); options.setTo(targetEPR); Object[] entryArgs = new Object[]{4, 2}; QName qName = new QName("http://ws.apache.org/axis2", "add"); Object result = serviceClient.invokeBlocking(qName, entryArgs, new Class[]{int.class})[0]; qName = new QName("http://ws.apache.org/axis2", "send"); serviceClient.invokeRobust(qName, new Object[]{"hello world!"}); } }
Soap UI
Apache CXF是一个开源的、全功能的,容易使用的Web服务框架。CXF是两个项目的结合:由IONA技术公司开发的Celtix和由Codehaus主持的团队开发的XFire。
CXF支持的特性非常广泛,但特性主要在以下一些方面:
支持的Web服务标准包括:
SOAP
WS-Addressing
WS-Policy
WS-ReliableMessaging
WS-Security
WS-SecurityPolicy
WS-SecureConversation
JAS-WS API,用于Web服务开发
WSDL优先支持工具
Java优先支持
JAX-RS(JSR 311 1.0)API,用于RESTful Web服务开发
…
:arrow_up:内容摘自Wiki百科。
使用 Maven
构建项目,POM文件内容如下:
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>org.example</groupId> <artifactId>CXFDemo</artifactId> <version>1.0-SNAPSHOT</version> <dependencies> <dependency> <groupId>org.apache.cxf</groupId> <artifactId>cxf-rt-frontend-jaxws</artifactId> <version>3.4.0</version> </dependency> <dependency> <groupId>org.apache.cxf</groupId> <artifactId>cxf-rt-transports-http</artifactId> <version>3.4.0</version> </dependency> <dependency> <groupId>org.apache.cxf</groupId> <artifactId>cxf-rt-transports-http-jetty</artifactId> <version>3.4.0</version> </dependency> <dependency> <groupId>org.apache.cxf</groupId> <artifactId>cxf-rt-transports-http-jetty</artifactId> <version>3.4.0</version> </dependency> </dependencies> </project>
编写一个服务接口,定义 sayHi
方法:
package org.example.services; import javax.jws.WebService; // 声明这是一个Ws服务接口 @WebService public interface HelloWorld { // 定义服务方法 String sayHi(String name); }
编写一个服务接口的实现类:
package org.example.services; import javax.jws.WebService; @WebService(endpointInterface = "org.example.services.HelloWorld", serviceName = "HelloWorld") public class HelloWorldImpl implements HelloWorld { public String sayHi(String name) { return "hi, " + name; } }
再编写一个发布服务的主类 Main
:
import org.apache.cxf.jaxws.JaxWsServerFactoryBean; import org.example.services.HelloWorld; import org.example.services.HelloWorldImpl; public class Main { public static void main(String[] args) { HelloWorldImpl implementor = new HelloWorldImpl(); JaxWsServerFactoryBean svrFactory = new JaxWsServerFactoryBean(); svrFactory.setServiceClass(HelloWorld.class); svrFactory.setAddress("http://localhost:9000/helloworld"); svrFactory.setServiceBean(implementor); svrFactory.create(); } }
运行后即可访问 http://localhost:9000/helloworld?wsdl
查看该服务,从WSDL的描述文件中也能看出 HelloWorld
服务提供了 sayHi
方法,且该方法需要一个字符串参数,返回值也为字符串。
使用 SoapUI
调用该方法示例:
通过代码也可以实现客户端调用 Web Service
:
import org.example.services.HelloWorld; import javax.xml.namespace.QName; import javax.xml.ws.Service; import javax.xml.ws.soap.SOAPBinding; public class WsClient { private static final QName SERVICE_NAME = new QName("http://services.example.org/", "HelloWorld"); private static final QName PORT_NAME = new QName("http://services.example.org/", "HelloWorldPort"); public static void main(String[] args) { Service service = Service.create(SERVICE_NAME); String endpointAddress = "http://localhost:9000/helloworld"; service.addPort(PORT_NAME, SOAPBinding.SOAP11HTTP_BINDING, endpointAddress); HelloWorld helloWorld = service.getPort(HelloWorld.class); System.out.println(helloWorld.sayHi("SearchNull")); } }
在Spring框架中可以集合 Apache CXF
框架发布 Web Service
服务,通过 Maven
或其他方式将CXF所需的Jar包导入项目中,编写 Web Service
接口及实现类即可。
package org.example.webservices; import javax.jws.WebMethod; import javax.jws.WebParam; import javax.jws.WebResult; import javax.jws.WebService; /** * @WebService注解标记一个webservice接口 * * @param targetNamespace 指定命名空间,默认使用包名的反转. * @param serviceName 对外发布的服务名称 * @param portName wsdl:portName,默认为服务名 + Port */ @WebService( targetNamespace = "http://services.example.org/", serviceName = "HelloService", portName = "HelloServicePort" ) public interface IHelloService { /** * @WebResult注解指定从返回值至WSDL或XML元素的映射,将此注解应用于客户端或服务端接口(SEI)方法之上。 * * @param name 当返回值列示在 WSDL 文件中并且在连接上的消息中找到该返回值时,指定该返回值的名称。对于 RPC 绑定,这是用于表示返回值的 * @param targetNamespace 指定返回值的 XML 命名空间。仅当操作类型为 RPC 或者操作是文档类型并且参数类型为 BARE 时才使用此参数。(字符串) * * @WebMethod注解表示作为 Web Service 的方法,将此注解应用于服务端接口(SEI)的方法上。 * * @param operationName 指定与此方法相匹配的 wsdl:operation 的名称,默认值为Java方法的名称 * * @WebParam注解用于指定方法参数即Web Service方法的消息部件和XML元素的映射 * @param name 参数的名称,如果操作是RPC(远程过程调用)类型并且未指定portName属性,则表示参数的wsdl:port属性的名称 */ @WebResult(name = "helloRequest", targetNamespace = "http://services.example.org/") @WebMethod(operationName="sayHello") public String hello(@WebParam(name = "msg")String name); }
Web Service
接口的实现类
package org.example.webservices.impl; import javax.jws.WebService; import javax.jws.soap.SOAPBinding; import javax.jws.soap.SOAPBinding.Style; import org.springframework.stereotype.Service; import org.exmaple.webservices.IHelloService; @WebService( endpointInterface = "org.example.services.IHelloService" ) @SOAPBinding(style=Style.RPC) public class IHelloServiceImpl implements IHelloService { @Override public String hello(String name) { return "Hello, " + name; } }
实现 Web Service
的服务端后则需要对Spring项目整合CXF的一些配置,在 web.xml
中配置CXF框架路由。
<servlet> <servlet-name>cxf</servlet-name> <servlet-class>org.apache.cxf.transport.servlet.CXFServlet</servlet-class> <load-on-startup>1</load-on-startup> </servlet> <servlet-mapping> <servlet-name>cxf</servlet-name> <url-pattern>/services/*</url-pattern> </servlet-mapping>
在Spring配置(eg: application.xml)文件中加入以下配置项:
<jaxws:server id="helloService" serviceClass="org.example.services.IHelloService" address="/HelloService"> <jaxws:serviceBean> <bean class="org.example.services.impl.IHelloServiceImpl"></bean> </jaxws:serviceBean> </jaxws:server>
最后启动项目访问 http://localhost:8080/services?wsdl
即可查看发布的服务。