前言
在跨系统和跨平台的系统通信中,WebService是一个事实上的标准,其以平台无关性,获得了广泛的应用。本文将讲述如何基于spring来集成CXF,并开发出第一个Hello World的应用。
1.Web Service是什么?
Web service是一个平台独立的,低耦合的,自包含的、基于可编程的web的应用程序,可使用开放的XML(标准通用标记语言下的一个子集)标准来描述、发布、发现、协调和配置这些应用程序,用于开发分布式的互操作的应用程序。
Web Service技术,能使得运行在不同机器上的不同应用无须借助附加的、专门的第三方软件或硬件,就可相互交换数据或集成。依据Web Service规范实施的应用之间,无论它们所使用的语言、 平台或内部协议是什么, 都可以相互交换数据。Web Service是自描述、 自包含的可用网络模块,可以执行具体的业务功能。Web Service也很容易部署,因为它们基于一些常规的产业标准以及已有的一些技术,诸如标准通用标记语言下的子集XML、HTTP。Web Service减少了应用接口的花费。Web Service为整个企业甚至多个组织之间的业务流程的集成提供了一个通用机制。
2.Web Service模型
WebService体系结构基于三种角色(即服务提供者、服务注册中心和服务请求者)之间的交互。交互涉及发布、查找和绑定操作,这些角色和操作一起作用于WebServices组件,即WebServices软件模块及其描述。在典型情况下,服务提供者托管可通过网络访问的的软件模块,定义WebServices的服务描述并把它发布到服务注册中心;服务请求者使用查找操作来从服务注册中心检索服务描述,然后使用服务描述与服务提供者进行绑定并调用WebServices实现和同它交互。
这里写图片描述
从图中可以看出,SOA结构中共有三种角色:
(1)、服务提供者:发布自己的服务,并且对服务请求进行响应。
(2)、服务注册中心:注册已经发布的WebServices,对其进行分类,并提供搜索服务。
(3)、服务请求者:利用服务注册中心查找所需的服务,然后使用该服务。
SOA体系结构中的组件必须具有上述一种或多种角色,这些角色之间使用三种操作:
(1)、发布操作:使服务提供者可以向服务注册中心注册自己的功能及访问接口。
(2)、查找操作:使服务请求者可以通过服务注册中心查找特定种类的服务。
(3)、绑定操作:使服务请求者能够真正使用服务提供者提供的服务。
3.WebService开源框架的选择
目前主流的Web Service框架主要有3种: Axis 2, CXF, 和 Spring WS。Axis 2和CXF都是来自于Apache, 各个方面相差不多,但是目前市场上使用CXF比较多一点。
使用篇
cfx一套最简配置jar包
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包
maven依赖
<dependency>
<groupId>org.apache.cxf</groupId>
<artifactId>cxf-core</artifactId>
<version>3.0.4</version>
</dependency>
<dependency>
<groupId>org.apache.cxf</groupId>
<artifactId>cxf-rt-bindings-soap</artifactId>
<version>3.0.4</version>
</dependency>
<dependency>
<groupId>org.apache.cxf</groupId>
<artifactId>cxf-rt-databinding-jaxb</artifactId>
<version>3.0.4</version>
</dependency>
<dependency>
<groupId>org.apache.cxf</groupId>
<artifactId>cxf-rt-frontend-jaxws</artifactId>
<version>3.0.4</version>
</dependency>
<dependency>
<groupId>org.apache.cxf</groupId>
<artifactId>cxf-rt-frontend-simple</artifactId>
<version>3.0.4</version>
</dependency>
<dependency>
<groupId>org.apache.cxf</groupId>
<artifactId>cxf-rt-transports-http</artifactId>
<version>3.0.4</version>
</dependency>
<dependency>
<groupId>org.apache.cxf</groupId>
<artifactId>cxf-rt-transports-udp</artifactId>
<version>3.0.4</version>
</dependency>
<dependency>
<groupId>org.apache.cxf</groupId>
<artifactId>cxf-rt-ws-addr</artifactId>
<version>3.0.4</version>
</dependency>
<dependency>
<groupId>org.apache.cxf</groupId>
<artifactId>cxf-rt-wsdl</artifactId>
<version>3.0.4</version>
</dependency>
<dependency>
<groupId>org.apache.cxf</groupId>
<artifactId>cxf-rt-ws-policy</artifactId>
<version>3.0.4</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.apache.neethi/neethi -->
<dependency>
<groupId>org.apache.neethi</groupId>
<artifactId>neethi</artifactId>
<version>3.0.3</version>
</dependency>
<dependency>
<groupId>org.apache.cxf</groupId>
<artifactId>cxf-rt-transports-http-jetty</artifactId>
<version>3.0.3</version>
</dependency>
参考:http://blog.csdn.net/w1014074794/article/details/47862163
1.最简单的demo
服务端接口定义
服务端提供的获取用户的方法
@WebService
public interface IUserWebService {
User getUserByName(@WebParam(name="userName") String userName);
List<User> getAllUsers();
}
注意:“@WebService”标记表示该接口是一个WebService服务;“@WebMethod”表示表示以下方法为WebService服务中的方法;“@WebParam(name="username")”表示方法中的参数,username属性限制了参数的名称,若没有指定该属性,参数将被重命名。
@WebParam(name="userName") 经测试, 可省略, 但为了便于阅读,还是加上
服务端接口实现
模拟从服务端获取用户
public class UserWebServiceImpl implements IUserWebService {
private static List<User> users = new ArrayList<User>();
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<User> 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<User> 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. 可能是其他的项目地址.
2.如何集成spring框架
前提:检查spring版本
了解你的spring版本, cxf对spring版本, 需要的依赖是不同的.至于什么版本匹配, 还需要自行百度, 我只实验了spring4.0 + ,需要cxf3.0以上的版本支持,不然报错.
在Web.xml中声明CXF监听器
<servlet>
<servlet-name>CXFServlet</servlet-name>
<servlet-class>org.apache.cxf.transport.servlet.CXFServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>CXFServlet</servlet-name>
<url-pattern>/webservice/*</url-pattern>
</servlet-mapping>
注意:<servlet-mapping>下的<url-pattern>指明了服务访问地址的形式,
“/*”
代表URL地址中,包名称后直接跟服务endpoint地址,若指明<url-pattern>为/webservice/*,则URL为“包名/webservice/endpoing?wsdl”。例如之前例子的:http://localhost:8080/userWebService?wsdl
, 其中userWebService
等同于这里的endpoing
创建WebService声明的Spring配置文件spring-cxf.xml
<?xml version="1.0" encoding="utf-8"?>
<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">
<!-- 2.7以上不用写这些 -->
<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" />
<!-- /2.7以上不用写 -->
<bean id="userWebService" class="coolframe.demo.webservice.IUserWebService"/>
<jaxws:endpoint id="随便起这个" implementor="#userWebService" address="/UserWebService" />
注意:
<jaxws:endpoint>
定义了一个WebService,implementor是WebService处理类,值上面定义的bean的id,其具体的实现类在class中指明,address是它的访问路径,就是上面提到的将要在URL中显示的endpoint的名称。
- 下面可以启动项目, 访问:http://host:port/项目名/webservice/services?wsdl
看一下都有什么webservice服务, - 读取wsdl文件内容,访问路径: http://host:port/项目名/services/serviceName?wsdl
配置多个接口的方法
<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<User> userList = userWebService.getAllUsers();
System.out.println(userList);
}
// 输出结果: [User [name=isName:李四], User [name=isName:王五], User [name=isName:赵六]]
}
异常问题总结
- jar包缺失
在使用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.cxf</groupId>
<artifactId>cxf-rt-transports-http-jetty</artifactId>
<version>3.0.3</version>
</dependency>
- spring版本和cxf版本不匹配
java.lang.NoSuchMethodError: org.springframework.aop.support.AopUtils.isCglibProxyClass(Ljava/lang/Class;)Z
解决办法:
根据spring版本自行百度,更换cxf依赖版本
- xml加载取顺序问题
org.springframework.beans.factory.NoSuchBeanDefinitionException: No bean named 'cxf' is defined
解决办法:
把cxf的配置不要放在DispatcherServlet里, 放到contextConfigLocation里加载
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>
classpath*:conf/spring-cxf.xml
</param-value>
</context-param>
- xml文件找不到
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
网友评论