JAVA 的的的的 WebService 支持支持支持支持
李海峰李海峰李海峰李海峰((((QQ:61673110))))-Andrew830314@163.com
SOA(Service-Oriented Architecture)面向服务架构是一种思想,它将应用程序的不同功能单元
通过中立的契约(独立于硬件平台、操作系统和编程语言)联系起来,使得各种形式的功能
单元更好的集成。目前来说,WebService 是 SOA 的一种较好的实现方式,WebService 采用
HTTP 作为传输协议,SOAP(Simple Object Access Protocol)作为传输消息的格式。但
WebService 并不是完全符合 SOA 的概念,因为 SOAP 协议是 WebService 的特有协议,并
未符合 SOA 的传输协议透明化的要求。SOAP 是一种应用协议,早期应用于 RPC 的实现,
传输协议可以依赖于 HTTP、SMTP 等。
SOA 的产生共经历了如下过程:
通常采用 SOA 的系统叫做服务总线(BUS),结构如下图所示:
------------------------------------------------------------------------------------
JAVA 中的中的中的中的 Web 服务规范
服务规范::::
服务规范服务规范
JAVA 中共有三种 WebService 规范,分别是 JAXM&SAAJ、JAX-WS(JAX-RPC)、JAX-RS。
下面来分别简要的介绍一下这三个规范。
(1.)JAX-WS::::
JAX-WS(Java API For XML-WebService),JDK1.6 自带的版本为 JAX-WS2.1,其底层支
持为 JAXB。早期的基于 SOAP 的 JAVA 的 Web 服务规范 JAX-RPC(Java API For
XML-Remote Procedure Call)目前已经被 JAX-WS 规范取代,JAX-WS 是 JAX-RPC 的演进
版本,但 JAX-WS 并不完全向后兼容 JAX-RPC,二者最大的区别就是 RPC/encoded 样式的
WSDL,JAX-WS 已经不提供这种支持。JAX-RPC 的 API 从 JAVA EE5 开始已经移除,如
果你使用 J2EE1.4,其 API 位于 javax.xml.rpc.*包。
JAX-WS(JSR 224)规范的 API 位于 javax.xml.ws.*包,其中大部分都是注解,提供 API 操
作 Web 服务(通常在客户端使用的较多,由于客户端可以借助 SDK 生成,因此这个包中的
API 我们较少会直接使用)。
WS-MetaData(JSR 181)是 JAX-WS 的依赖规范,其 API 位于 javax.jws.*包,使用注解配
置公开的 Web 服务的相关信息和配置 SOAP 消息的相关信息。
(2.)JAXM&SAAJ::::
JAXM(JAVA API For XML Message)主要定义了包含了发送和接收消息所需的 API,相当
于 Web 服务的服务器端,其 API 位于 javax.messaging.*包,它是 JAVA EE 的可选包,因此
你需要单独下载。
SAAJ(SOAP With Attachment API For Java,JSR 67)是与 JAXM 搭配使用的 API,为构建
SOAP 包和解析 SOAP 包提供了重要的支持,支持附件传输,它在服务器端、客户端都需要
使用。这里还要提到的是 SAAJ 规范,其 API 位于 javax.xml.soap.*包。
JAXM&SAAJ 与 JAX-WS 都是基于 SOAP 的 Web 服务,相比之下 JAXM&SAAJ 暴漏了 SOAP
更多的底层细节,编码比较麻烦,而 JAX-WS 更加抽象,隐藏了更多的细节,更加面向对
象,实现起来你基本上不需要关心 SOAP 的任何细节。那么如果你想控制 SOAP 消息的更
多细节,可以使用 JAXM&SAAJ,目前版本为 1.3。
(3.)JAX-RS::::
JAX-RS 是 JAVA 针对 REST(Representation State Transfer)风格制定的一套 Web 服务规范,
由于推出的较晚,该规范(JSR 311,目前 JAX-RS 的版本为 1.0)并未随 JDK1.6 一起发行,
你需要到 JCP 上单独下载 JAX-RS 规范的接口,其 API 位于 javax.ws.rs.*包。
这里的 JAX-WS 和 JAX-RS 规范我们采用 Apache CXF 作为实现,CXF 是 Objectweb Celtix
和 Codehaus XFire 合并而成。CXF 的核心是 org.apache.cxf.Bus(总线),类似于 Spring 的
ApplicationContext,Bus 由 BusFactory 创建,默认是 SpringBusFactory 类,可见默认 CXF
是依赖于 Spring 的,Bus 都有一个 ID,默认的 BUS 的 ID 是 cxf。你要注意的是 Apache CXF
2.2 的发行包中的 jar 你如果直接全部放到 lib 目录,那么你必须使用 JDK1.6,否则会报
JAX-WS 版本不一致的问题。对于 JAXM&SAAJ 规范我们采用 JDK 中自带的默认实现。
------------------------------------------------------------------------------------
1.JAVA 的的的的WebService 规范规范规范规范JAX-WS::::
Web 服务从前面的图中不难看出自然分为 Server、Client 两部分,Server 公开 Web 服务,
Client 调用 Web 服务,JAX-WS 的服务端、客户端双方传输数据使用的 SOAP 消息格式封
装数据,在后面我们会看到其实 SOAP 信封内包装的就是一段 XML 代码。
I.服务端示例
服务端示例::::
服务端示例
服务端示例
我们先看一个服务器端示例:
(1.)公开 Web 服务的接口 IHelloService:
Customer selectMaxAgeStudent(Customer c1, Customer c2);
Customer selectMaxLongNameStudent(Customer c1, Customer c2);
package net.ilkj.soap.server;
import javax.jws.WebService;
@WebService
public interface IHelloService {
}
我们看到这个接口很简单,仅仅是使用类级别注解@WebService 就标注了这个接口的方法
将公开为 Web 服务,使用了这个注解的接口的所有方法都将公开为 Web 服务的操作,如果
你想屏蔽某个方法,可以使用方法注解@Method 的 exclude=true。我们也通常把公开为 Web
服务的接口叫做 SEI(Service EndPoint Interface)服务端点接口。
(2.)实现类 HelloServiceImpl:
package net.ilkj.soap.server;
public class HelloServiceImpl implements IHelloService {
@Override
public Customer selectMaxAgeStudent(Customer c1, Customer c2) {
if (c1.getBirthday().getTime() > c2.getBirthday().getTime())
else
}
return c2;
return c1;
return c2;
return c1;
if (c1.getName().length() > c2.getName().length())
else
@Override
public Customer selectMaxLongNameStudent(Customer c1, Customer c2)
{
}
}
这个实现类没有任何特殊之处,但是如果你的实现类还实现了其他的接口,那么你需要在实
现类上使用@WebService 注解的 endpointInterface 属性指定那个接口是 SEI(全类名)。
(3.)Customer 类:
package net.ilkj.soap.server;
private long id;
private String name;
private Date birthday;
public long getId() {
}
return id;
this.id = id;
public String getName() {
}
return name;
public void setId(long id) {
}
import java.util.Date;
import javax.xml.bind.annotation.XmlRootElement;
@XmlRootElement(name = "Customer")
public class Customer {
}
这个类是公开为 Web 服务的接口中的参数类型和返回值,因此你需要使用 JAXB 注解告诉
CXF 如何在 XML 和 Java Object 之间处理,因为前面说过 SOAP 消息格式包装的是一段 XML
代码,那么无论是服务器端还是客户端在接收到 SOAP 消息时都需要将 XML 转化为 Java
Object,在发送 SOAP 消息时需要将 Java Object 转化为 XML。
(4.)发布 Web 服务:
package net.ilkj.soap.server;
import javax.xml.ws.Endpoint;
public class SoapServer {
public void setBirthday(Date birthday) {
}
public void setName(String name) {
}
this.name = name;
public Date getBirthday() {
}
return birthday;
this.birthday = birthday;
public static void main(String[] args) {
new HelloServiceImpl());
}
Endpoint.publish("http://127.0.0.1:8080/helloService",
}
注意我们发布 Web 服务使用的是 javax.xml.ws.*包中的 EndPoint 的静态方法 publish()。
(5.)查看 WSDL:
我们访问 http://127.0.0.1:8080/helloService?wsdl 地址,您会看到很长的 XML 文件(由于浏
览器的问题,如果你看到的是空白页面,请查看源代码),这就是 WSDL(WebService Definition
Language),对于你要访问的 Web 服务,只要在其地址后加上,就可以在浏览器中查看用于
描述 Web 服务的 WSDL,这也是一种 XML,Web 服务能够被各种编程语言书写的程序访
问就是通过 WSDL 这种通用的契约来完成的。
如果你已经看到 WSDL,那么表示我们的 Web 服务发布成功了。你可能会差异,我们没有
借助 Tomcat 这样的 Web 服务器,直接运行一个 main 方法是怎么发布的 Web 服务呢?其实
CXF 内置了 Jetty(Servlet 容器),因此你不需要将你的程序部署到 Tomcat 等 Web 服务器
也可以正常发布 Web 服务。
------------------------------------------------------------------------------------
II.分析分析分析分析 WSDL 的构成的构成的构成的构成::::
下面我们来解释一下你所看到的 WSDL 的结构,你可以对照你所生成 WSDL(文件太长,
Word 里实在放不下)。
(1.))。
再向下你会看到 XXX 元素和 XXXResponse 元素,其中 XXX 是方法名称(你可以使用
@WebMethod 的 operationName 属性值指定 XXX 的值),XXX 是对方法参数的封装,
XXXResponse 是对返回值的封装,上例中你会看到
内容,
最 后 你 会 看 到 一 组
我们看到方法参数名称为 arg0、arg1、…,如果你想指定方法参数的名字在方法参数前使用
@WebParam 的 name 属性指定值,同样,方法的返回值同样可以使用@WebResult 注解指定
相关的属性值。
例如:
@WebResult(name = "method")
Customer selectMaxAgeStudent(@WebParam(name = "c1") Customer c1,
@WebParam(name = "c2") Customer c2);
(3.)
(6.)Other--->MyEclipse--->Web Services--->Web Services Client,然后依据设置向导即可
完成,但最好还是使用 CXF 的 wsdl2java 来完成,因为 CXF2.2+版本开始支持 JAX-WS2.1
规范,而 MyEclipse 自带的好像是 XFire 的 wsdl2java,生成的客户端代码可能不是最新规范
的。
我们上面的 WSDL 会生成如下所示的客户端代码:
Customer.java
HelloServiceImplService.java
IHelloService.java
ObjectFactory.java
package-info.java
SelectMaxAgeStudent.java
SelectMaxAgeStudentResponse.java
SelectMaxLongNameStudent.java
SelectMaxLongNameStudentResponse.java
其中 package-info.java、ObjectFactory.java 是 JAXB 需要的文件;HelloServiceImplService.java
继承自 javax.xml.ws.Service 类,用于提供 WSDL 的客户端视图,里面使用的是大量
javax.xml.ws.*包中的注解;剩下的类是 Web 服务的接口、方法参数、响应值的类。
在 CXF 中使用 JaxWsProxyFactoryBean 客户端代理工厂调用 Web 服务,代码如下所示:
package net.ilkj.soap.client;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.GregorianCalendar;
import org.apache.cxf.jaxws.JaxWsProxyFactoryBean;
import
.getInstance();
calendar
public static void main(String[] args) throws ParseException {
JaxWsProxyFactoryBean soapFactoryBean = new
soapFactoryBean.setServiceClass(IHelloService.class);
Object o = soapFactoryBean.create();
IHelloService helloService = (IHelloService) o;
Customer c1 = new Customer();
c1.setId(1);
c1.setName("A");
GregorianCalendar calendar = (GregorianCalendar)
soapFactoryBean.setAddress("http://127.0.0.1:8080/helloService");
com.sun.org.apache.xerces.internal.jaxp.datatype.XMLGregorianCalendar
Impl;
public class SoapClient {
JaxWsProxyFactoryBean();
GregorianCalendar
SimpleDateFormat("yyyy-MM-dd").parse("1989-01-28"));
SimpleDateFormat("yyyy-MM-dd").parse("1990-01-28"));
c2).getName());
}
这里要注意的就是 Customer 的生日字段 JAX-WS 在客户端映射为了 XMLGregorianCalendar
类型。我们运行这个客户端,结果输出 A,我们的 Web 服务调用成功。你还要注意 Web 服
务调用可能经常出现超时的问题,但你切不可以为只要 WSDL 可以访问,就代表 Web 服务
一定可以访问,因为是否可以访问与 SEI 的实现类有关,而 WSDL 仅是 SEI 的一种 XML
表示。
Customer c2 = new Customer();
c2.setId(2);
c2.setName("B");
calendar
.setTime(new
c1.setBirthday(new XMLGregorianCalendarImpl(calendar));
c2.setBirthday(new XMLGregorianCalendarImpl(calendar));
System.out.println(helloService.selectMaxAgeStudent(c1,
.setTime(new
}