在跨系统和跨平台的系统通信中,WebService是一个事实上的标准,其以平台无关性,获得了广泛的应用。本文将讲述如何基于spring来集成CXF,并开发出第一个Hello World的应用。
Web service是一个平台独立的,低耦合的,自包含的、基于可编程的web的应用程序,可使用开放的XML(标准通用标记语言下的一个子集)标准来描述、发布、发现、协调和配置这些应用程序,用于开发分布式的互操作的应用程序。
Web Service技术,能使得运行在不同机器上的不同应用无须借助附加的、专门的第三方软件或硬件,就可相互交换数据或集成。依据Web Service规范实施的应用之间,无论它们所使用的语言、 平台或内部协议是什么, 都可以相互交换数据。Web Service是自描述、 自包含的可用网络模块,可以执行具体的业务功能。Web Service也很容易部署,因为它们基于一些常规的产业标准以及已有的一些技术,诸如标准通用标记语言下的子集XML、HTTP。Web Service减少了应用接口的花费。Web Service为整个企业甚至多个组织之间的业务流程的集成提供了一个通用机制。
WebService体系结构基于三种角色(即服务提供者、服务注册中心和服务请求者)之间的交互。交互涉及发布、查找和绑定操作,这些角色和操作一起作用于WebServices组件,即WebServices软件模块及其描述。在典型情况下,服务提供者托管可通过网络访问的的软件模块,定义WebServices的服务描述并把它发布到服务注册中心;服务请求者使用查找操作来从服务注册中心检索服务描述,然后使用服务描述与服务提供者进行绑定并调用WebServices实现和同它交互。
从图中可以看出,SOA结构中共有三种角色:
(1)、服务提供者:发布自己的服务,并且对服务请求进行响应。
(2)、服务注册中心:注册已经发布的WebServices,对其进行分类,并提供搜索服务。
(3)、服务请求者:利用服务注册中心查找所需的服务,然后使用该服务。SOA体系结构中的组件必须具有上述一种或多种角色,这些角色之间使用三种操作:
(1)、发布操作:使服务提供者可以向服务注册中心注册自己的功能及访问接口。
(2)、查找操作:使服务请求者可以通过服务注册中心查找特定种类的服务。
(3)、绑定操作:使服务请求者能够真正使用服务提供者提供的服务。
目前主流的Web Service框架主要有3种: Axis 2, CXF, 和 Spring WS。Axis 2和CXF都是来自于Apache, 各个方面相差不多,但是目前市场上使用CXF比较多一点。
cxf3.0.4框架必需的包为:
cxf-core-3.0.4.jar
cxf-rt-bindings-soap-3.0.4.jar
cxf-rt-databinding-jaxb-3.0.4.jar
cxf-rt-frontend-jaxws-3.0.4.jar
cxf-rt-frontend-simple-3.0.4.jar
cxf-rt-transports-http-3.0.4.jar
cxf-rt-transports-udp-3.0.4.jar
cxf-rt-ws-addr-3.0.4.jar
cxf-rt-wsdl-3.0.4.jar
cxf-rt-ws-policy-3.0.4.jar
cxf-rt-wsdl-3.0.4.jar
neethi-3.0.3.jar
slf4j-api-1.7.9.jar
xmlschema-core-2.2.1.jar
注意:这里不包括spring依赖包和commoms下的jar包
<dependency>
<groupId>org.apache.cxfgroupId>
<artifactId>cxf-coreartifactId>
<version>3.0.4version>
dependency>
<dependency>
<groupId>org.apache.cxfgroupId>
<artifactId>cxf-rt-bindings-soapartifactId>
<version>3.0.4version>
dependency>
<dependency>
<groupId>org.apache.cxfgroupId>
<artifactId>cxf-rt-databinding-jaxbartifactId>
<version>3.0.4version>
dependency>
<dependency>
<groupId>org.apache.cxfgroupId>
<artifactId>cxf-rt-frontend-jaxwsartifactId>
<version>3.0.4version>
dependency>
<dependency>
<groupId>org.apache.cxfgroupId>
<artifactId>cxf-rt-frontend-simpleartifactId>
<version>3.0.4version>
dependency>
<dependency>
<groupId>org.apache.cxfgroupId>
<artifactId>cxf-rt-transports-httpartifactId>
<version>3.0.4version>
dependency>
<dependency>
<groupId>org.apache.cxfgroupId>
<artifactId>cxf-rt-transports-udpartifactId>
<version>3.0.4version>
dependency>
<dependency>
<groupId>org.apache.cxfgroupId>
<artifactId>cxf-rt-ws-addrartifactId>
<version>3.0.4version>
dependency>
<dependency>
<groupId>org.apache.cxfgroupId>
<artifactId>cxf-rt-wsdlartifactId>
<version>3.0.4version>
dependency>
<dependency>
<groupId>org.apache.cxfgroupId>
<artifactId>cxf-rt-ws-policyartifactId>
<version>3.0.4version>
dependency>
<dependency>
<groupId>org.apache.neethigroupId>
<artifactId>neethiartifactId>
<version>3.0.3version>
dependency>
<dependency>
<groupId>org.apache.cxfgroupId>
<artifactId>cxf-rt-transports-http-jettyartifactId>
<version>3.0.3version>
dependency>
参考:http://blog.csdn.net/w1014074794/article/details/47862163
服务端提供的获取用户的方法
@WebService
public interface IUserWebService {
User getUserByName(@WebParam(name="userName") String userName);
List getAllUsers();
}
注意:“@WebService”标记表示该接口是一个WebService服务;“@WebMethod”表示表示以下方法为WebService服务中的方法;“@WebParam(name=”username”)”表示方法中的参数,username属性限制了参数的名称,若没有指定该属性,参数将被重命名。
@WebParam(name=”userName”) 经测试, 可省略, 但为了便于阅读,还是加上
模拟从服务端获取用户
public class UserWebServiceImpl implements IUserWebService {
private static List users = new ArrayList();
static {
users.add(new User("001", "李四"));
users.add(new User("002", "王五"));
users.add(new User("003", "赵六"));
}
public User getUserByName(String userName) {
if (StringUtils.isEmpty(userName)) {
return null;
}
for (User user: users) {
if (userName.equals(user.getname())) {
return user;
}
}
return null;
}
public List getAllUsers() {
return users;
}
}
模拟WebService服务启动
public class WebServiceApp {
public static void main(String[] args) {
// webservice接口
IUserWebService userService = new UserWebServiceImpl();
// 访问地址
String address="http://localhost:8080/userWebService";
// 发布服务
Endpoint.publish(address, userService);
System.out.println("web service 已启动");
}
}
现在可以启动这个程序, 然后访问地址:http://localhost:8080/userWebService?wsdl
This XML file does not appear to have any style information associated with it. The document tree is shown below.
<wsdl:definitions xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/"
xmlns:tns="http://webservice.demo.coolframe/" xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/" xmlns:ns1="http://schemas.xmlsoap.org/soap/http"
name="UserWebServiceImplService" targetNamespace="http://webservice.demo.coolframe/">
<wsdl:types>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:tns="http://webservice.demo.coolframe/"
elementFormDefault="unqualified" targetNamespace="http://webservice.demo.coolframe/" version="1.0">
<xs:element name="getAllUsers" type="tns:getAllUsers" />
<xs:element name="getAllUsersResponse" type="tns:getAllUsersResponse" />
<xs:element name="getUserByName" type="tns:getUserByName" />
<xs:element name="getUserByNameResponse" type="tns:getUserByNameResponse" />
<xs:complexType name="getAllUsers">
<xs:sequence />
xs:complexType>
<xs:complexType name="getAllUsersResponse">
<xs:sequence>
<xs:element maxOccurs="unbounded" minOccurs="0" name="return" type="tns:user" />
xs:sequence>
xs:complexType>
<xs:complexType name="user">
<xs:sequence>
<xs:element minOccurs="0" name="id" type="xs:string" />
<xs:element minOccurs="0" name="name" type="xs:string" />
<xs:element maxOccurs="unbounded" minOccurs="0" name="phones" nillable="true" type="xs:string" />
xs:sequence>
xs:complexType>
<xs:complexType name="getUserByName">
<xs:sequence>
<xs:element minOccurs="0" name="userName" type="xs:string" />
xs:sequence>
xs:complexType>
<xs:complexType name="getUserByNameResponse">
<xs:sequence>
<xs:element minOccurs="0" name="return" type="tns:user" />
xs:sequence>
xs:complexType>
xs:schema>
wsdl:types>
<wsdl:message name="getUserByNameResponse">
<wsdl:part element="tns:getUserByNameResponse" name="parameters">wsdl:part>
wsdl:message>
<wsdl:message name="getAllUsers">
<wsdl:part element="tns:getAllUsers" name="parameters">wsdl:part>
wsdl:message>
<wsdl:message name="getAllUsersResponse">
<wsdl:part element="tns:getAllUsersResponse" name="parameters">wsdl:part>
wsdl:message>
<wsdl:message name="getUserByName">
<wsdl:part element="tns:getUserByName" name="parameters">wsdl:part>
wsdl:message>
<wsdl:portType name="IUserWebService">
<wsdl:operation name="getAllUsers">
<wsdl:input message="tns:getAllUsers" name="getAllUsers">wsdl:input>
<wsdl:output message="tns:getAllUsersResponse" name="getAllUsersResponse">wsdl:output>
wsdl:operation>
<wsdl:operation name="getUserByName">
<wsdl:input message="tns:getUserByName" name="getUserByName">wsdl:input>
<wsdl:output message="tns:getUserByNameResponse" name="getUserByNameResponse">wsdl:output>
wsdl:operation>
wsdl:portType>
<wsdl:binding name="UserWebServiceImplServiceSoapBinding" type="tns:IUserWebService">
<soap:binding style="document" transport="http://schemas.xmlsoap.org/soap/http" />
<wsdl:operation name="getAllUsers">
<soap:operation soapAction="" style="document" />
<wsdl:input name="getAllUsers">
<soap:body use="literal" />
wsdl:input>
<wsdl:output name="getAllUsersResponse">
<soap:body use="literal" />
wsdl:output>
wsdl:operation>
<wsdl:operation name="getUserByName">
<soap:operation soapAction="" style="document" />
<wsdl:input name="getUserByName">
<soap:body use="literal" />
wsdl:input>
<wsdl:output name="getUserByNameResponse">
<soap:body use="literal" />
wsdl:output>
wsdl:operation>
wsdl:binding>
<wsdl:service name="UserWebServiceImplService">
<wsdl:port binding="tns:UserWebServiceImplServiceSoapBinding" name="UserWebServiceImplPort">
<soap:address location="http://localhost:8080/userWebService" />
wsdl:port>
wsdl:service>
wsdl:definitions>
显示这样的页面说明服务启动成功了, 下面模拟客户端访问
public class WebServiceClient {
public static void main(String[] args) {
JaxWsProxyFactoryBean svr = new JaxWsProxyFactoryBean();
// 服务端接口
svr.setServiceClass(IUserWebService.class);
// 服务端地址
svr.setAddress("http://localhost:8080/userWebService");
IUserWebService userWebService = (IUserWebService) svr.create();
List users = userWebService.getAllUsers();
System.out.println(users);
}
}
输出结果:
[User [name=isName:李四], User [name=isName:王五], User [name=isName:赵六]]
启动服务端, 然后启动客户端来访问服务。
结论:我们从http://localhost:8080/userWebService地址获取到了想要的接口实现来获得userL的List. 当然, 实际项目中, 服务端的地址不可能是http://localhost:8080. 可能是其他的项目地址.
了解你的spring版本, cxf对spring版本, 需要的依赖是不同的.至于什么版本匹配, 还需要自行百度, 我只实验了spring4.0 + ,需要cxf3.0以上的版本支持,不然报错.
<servlet>
<servlet-name>CXFServletservlet-name>
<servlet-class>org.apache.cxf.transport.servlet.CXFServletservlet-class>
<load-on-startup>1load-on-startup>
servlet>
<servlet-mapping>
<servlet-name>CXFServletservlet-name>
<url-pattern>/webservice/*url-pattern>
servlet-mapping>
注意:下的指明了服务访问地址的形式,
“/*”
代表URL地址中,包名称后直接跟服务endpoint地址,若指明为/webservice/*,则URL为“包名/webservice/endpoing?wsdl”。例如之前例子的:http://localhost:8080/userWebService?wsdl
, 其中userWebService
等同于这里的endpoing
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:jaxws="http://cxf.apache.org/jaxws"
xmlns:http-conf = "http://cxf.apache.org/transports/http/configuration"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://cxf.apache.org/jaxws
http://cxf.apache.org/schemas/jaxws.xsd
http://cxf.apache.org/transports/http/configuration
http://cxf.apache.org/schemas/configuration/http-conf.xsd">
<import resource="classpath:META-INF/cxf/cxf.xml" />
<import resource="classpath:META-INF/cxf/cxf-extension-soap.xml" />
<import resource="classpath:META-INF/cxf/cxf-servlet.xml" />
<bean id="userWebService" class="coolframe.demo.webservice.IUserWebService"/>
<jaxws:endpoint id="随便起这个" implementor="#userWebService" address="/UserWebService" />
注意:
定义了一个WebService,implementor是WebService处理类,值上面定义的bean的id,其具体的实现类在class中指明,address是它的访问路径,就是上面提到的将要在URL中显示的endpoint的名称。
配置多个接口的方法
<bean id="roleWebService" class="coolframe.demo.webservice.RoleWebServiceImpl"/>
<bean id="userWebService" class="coolframe.demo.webservice.UserWebServiceImpl"/>
<jaxws:server id="aWebService"
serviceClass="coolframe.demo.webservice.IRoleWebService"
address="/RoleWebService">
<jaxws:serviceBean>
<ref bean="hello"/>
jaxws:serviceBean>
jaxws:server>
<jaxws:server id="bWebService"
serviceClass="coolframe.demo.webservice.IUserWebService"
address="/UserWebService">
<jaxws:serviceBean>
<ref bean="userWebService"/>
jaxws:serviceBean>
jaxws:server>
总结: 配置方面基本完成了, 剩下还是客户端的调用
模拟客户端的调用
public class WebServiceClient {
public static void main(String[] args) {
String serviceURL = "http://localhost:8000/coolframe/webservice/UserWebService";
JaxWsProxyFactoryBean factory = new JaxWsProxyFactoryBean();
factory.setServiceClass(IUserWebService.class);
factory.setAddress(serviceURL);
IUserWebService userWebService = (IUserWebService)factory.create();
List userList = userWebService.getAllUsers();
System.out.println(userList);
}
// 输出结果: [User [name=isName:李四], User [name=isName:王五], User [name=isName:赵六]]
}
在使用cxf过程中经常出 Cannot find any registered HttpDestinationFactory from the Bus,一般是没有引入cxf-rt-transports-http-jetty-xxx.jar。查看apache.cxf.transport.http.HTTPTransportFactory.getDestination(HTTPTransportFactory.java:270)类,jettyFactory为null,也就是缺少http-jetty的实现。
Caused by: java.io.IOException: Cannot find any registered HttpDestinationFactory from the Bus.
解决办法:
<dependency>
<groupId>org.apache.cxfgroupId>
<artifactId>cxf-rt-transports-http-jettyartifactId>
<version>3.0.3version>
dependency>
java.lang.NoSuchMethodError: org.springframework.aop.support.AopUtils.isCglibProxyClass(Ljava/lang/Class;)Z
解决办法:
根据spring版本自行百度,更换cxf依赖版本
org.springframework.beans.factory.NoSuchBeanDefinitionException: No bean named 'cxf' is defined
解决办法:
把cxf的配置不要放在DispatcherServlet里, 放到contextConfigLocation里加载
<context-param>
<param-name>contextConfigLocationparam-name>
<param-value>
classpath*:conf/spring-cxf.xml
param-value>
context-param>
java.io.FileNotFoundException: class path resource [META-INF/cxf/cxf-extension-soap.xml] cannot be opened because it does not exist
cxf 2.7 以上spring-cxf.xml配置中不要写下面的两句
<import resource="classpath:META-INF/cxf/cxf-extension-soap.xml" />
<import resource="classpath:META-INF/cxf/cxf-servlet.xml" />
参考资料:
http://blog.csdn.net/blueheart20/article/details/42971713