Apache-CXF
[TOC]
概述
CXF是apache旗下的开源框架,由ObjectWeb Celtix和CodeHaus XFire这两门经典的框架合成,是一套非常流行的web service框架。ObjectWeb Celtix是由IONA公司赞助,于2005年成立的开源Java ESB产品,XFire则是业界知名的SOAP堆栈。ApacheCXF提供了对JAX-WS的全面支持,并且可以根据实际项目的需要,采用代码优先(Code First)或者 WSDL 优先(WSDL First)来轻松地实现 Web Services 的发布和使用,同时它能与spring进行完美结合。
CXF提供了以下功能:
- WebService服务标准支持:
- Java API for XML Web Services (JAX-WS)
- SOAP 1.1, 1.2
- WebService描述语言(Web Services Description Language ,WSDL)
- 消息传输优化机制(Message Transmission Optimization Mechanism,MTOM)
- WS-Basic Profile, WS-Security, WS-Addressing, WS-ReliableMessaging, WS-Policy
- 前端建模:CXF允许使用不同的前端API来创建Service。如CXF允许使用简单的工厂Bean并通过JAX-WS实现来创建WebService,允许创建动态WebService客户端。
- 工具支持:CXF提供了在Java Bean、WebService和WSDL之间进行转换的工具,提供了对Maven和Ant集成的支持,并无缝地支持Spring集成。
- RESTful支持:CXF支持Restful,并支持Java平台的JAX-RS实现。
- 对不同传输和绑定的支持:CXF支持不同数据类型的传输,除了支持SOAP和HTTP协议绑定外,还支持JAXB和AEGIS绑定。
- 对非XML绑定的支持:CXF支持非XML绑定,如JSON、CORBA、JBI和SCA等。
- Code First和Xml First:CXF支持使用Code First或者Xml First的方式创建WebService。
- 灵活的部署: 可以运行在Tomcat,Jboss,Jetty(内置),weblogic上面
cxf发布WebService
服务端流程
CXF发布WebService流程:
- 第一步: 导入cxf的jar包。手动导入或使用maven引用。
- 第二步: 编写WebService接口(SEI)。需要在SEI接口上添加@Webservice注解。
- 第三步: 编写WebService接口实现类。不需要加注解。
- 第四步: 利用CXF发布服务,需要利用cxf自带的jetty服务器
- 1、创建一个JaxWsServerFactoryBean工厂类对象
- 2、setServiceClass(),设置SEI接口的class对象
- 3、setServiceBean(),设置SEI实现类对象
- 4、setAddress(),设置服务发布地址
- 5、create(),发布服务
客户端流程
cxf发布的WebService当然也能利用原始JAX-WS调用,即利用java自带的wsimport
命令生成客户端需要的代码。
或者,cxf也提供了一个wsdl转java的工具:wsdl2java
,也能生成客户端调用代码。
JaxWsProxyFactoryBean类
- 1、创建一个JaxWsProxyFactoryBean工厂类对象
- 2、setServiceClass(),设置服务接口的class对象,即以wsdl文档的portType元素的name为名的接口
- 3、setAddress(),设置WebService服务地址,即服务端setAddress()发布的地址,加不加?wsdl都行
- 4、create(),生成代理对象
- 5、利用代理对象调用服务端方法
这种方法的弊端是客户端必须依赖服务器端的接口,要求服务器端的webservice必须是java实现,这样也就失去了使用webservice的意义。
JaxWsDynamicClientFactory类
- 1、创建一个JaxWsDynamicClientFactory工厂类对象,JaxWsDynamicClientFactory.newInstance()
- 2、创建Client对象,createClient(),参数为wsdl地址
- 3、client.invoke()调用服务,参数为(接口方法,arg0, arg1, …)
调用webxml.com的手机号码归属地服务
package com.masi.cxftest.client;
import org.apache.cxf.endpoint.Client;
import org.apache.cxf.jaxws.endpoint.dynamic.JaxWsDynamicClientFactory;
public class MobileCodeWSTest
{
public static void main( String[] args )throws Exception
{
JaxWsDynamicClientFactory clientFactory = JaxWsDynamicClientFactory.newInstance();
Client client = clientFactory.createClient("http://ws.webxml.com.cn/WebServices/MobileCodeWS.asmx?WSDL");
Object[] result = client.invoke("getMobileCodeInfo", "1868245","");
System.out.println(result[0]);
}
}
运行结果:
1868245:广东 深圳 广东联通GSM卡
实例
添加cxf的maven依赖
创建一个maven-archetype-quickstart 简单maven项目(非web项目),在pom.xml文件中添加cxf的依赖,保存后稍等片刻,maven会将cxf依赖的jar包下载到本地仓库目录中。
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<!-- CXF版本 -->
<cxf.version>3.1.1</cxf.version>
</properties>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>3.8.1</version>
<scope>test</scope>
</dependency>
<!-- cxf -->
<dependency>
<groupId>org.apache.cxf</groupId>
<artifactId>cxf-rt-frontend-jaxws</artifactId>
<version>${cxf.version}</version>
</dependency>
<dependency>
<groupId>org.apache.cxf</groupId>
<artifactId>cxf-rt-transports-http</artifactId>
<version>${cxf.version}</version>
</dependency>
<dependency>
<!-- 如果CXF不集成到Web服务器中,需要cxf自带的jetty来发布服务 -->
<groupId>org.apache.cxf</groupId>
<artifactId>cxf-rt-transports-http-jetty</artifactId>
<version>${cxf.version}</version>
</dependency>
</dependencies>
服务端
创建包com.masi.cxftest.server,在包内创建下面的接口IHelloWS,实现类HelloWSImpl,和服务发布类HelloWSServer。
创建WebService接口
新建接口IHelloWS,即SEI(ServiceEnpointInterface),注意要加@WebService注解
package com.masi.cxftest.server;
import javax.jws.WebService;
@WebService
public interface IHelloWS {
public String sayHello(String name);
}
实现WebService接口
package com.masi.cxftest.server;
public class HelloWSImpl implements IHelloWS{
@Override
public String sayHello(String name) {
return "你好,"+name;
}
}
发布服务
WebService发布类
package com.masi.cxftest.server;
import org.apache.cxf.jaxws.JaxWsServerFactoryBean;
public class HelloWSServer {
public static void main(String[] args) {
JaxWsServerFactoryBean factoryBean = new JaxWsServerFactoryBean();//创建jws工厂类
factoryBean.setServiceClass(IHelloWS.class);//设置SEI接口class对象
factoryBean.setAddress("http://192.168.1.108:8989/CXFHelloWS");//设置服务发布地址
factoryBean.setServiceBean(new HelloWSImpl());//设置SEI实现类对象
factoryBean.create();//创建WebService
System.out.println("WebService created!");
}
}
Run As->Java Application 启动服务端,cxf自带了一个jetty服务器,在浏览器输入(注意后面加?wsdl)
http://192.168.1.108:8989/CXFHelloWS?wsdl
能打开wsdl文件说明服务发布成功。
客户端调用
创建包com.masi.cxftest.client,包中创建客户端调用类HelloWSClient:
package com.masi.cxftest.client;
import org.apache.cxf.jaxws.JaxWsProxyFactoryBean;
public class HelloWSClient {
public static void main(String[] args) {
JaxWsProxyFactoryBean factoryBean = new JaxWsProxyFactoryBean();//创建一个JaxWsProxyFactoryBean工厂类对象
factoryBean.setServiceClass(IHelloWS.class);//设置portType的class对象
factoryBean.setAddress("http://192.168.1.108:8989/CXFHelloWS?wsdl");//设置WebService服务地址
//factoryBean.setAddress("http://192.168.1.108:8989/CXFHelloWS");//不加?wsdl也可以
IHelloWS iHelloWS = (IHelloWS)factoryBean.create();//生成portType代理对象
System.out.println(iHelloWS.sayHello("cxf ws test"));//调用服务方法
}
}
Run As->Java Application 启动客户端,调用成功返回:你好,cxf ws test
cxf与spring整合
CXF原生支持spring,可以和Spring无缝集成。
添加cxf及spring依赖
创建Maven web项目(maven-archetype-webapp),Artifact Id(即maven工程名)为cxf-spring-test,在pom.xml文件中添加cxf和spring的依赖,添加tomcat7插件:
<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/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.masi</groupId>
<artifactId>cxf-spring-test</artifactId>
<packaging>war</packaging>
<version>0.0.1-SNAPSHOT</version>
<name>cxf-spring-test Maven Webapp</name>
<url>http://maven.apache.org</url>
<properties>
<!-- Spring版本 -->
<spring.version>4.1.7.RELEASE</spring.version>
<!-- CXF版本 -->
<cxf.version>3.1.1</cxf.version>
</properties>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>3.8.1</version>
<scope>test</scope>
</dependency>
<!-- Spring -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aop</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-beans</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<version>${spring.version}</version>
</dependency>
<!-- End Spring -->
<!-- cxf -->
<dependency>
<groupId>org.apache.cxf</groupId>
<artifactId>cxf-rt-frontend-jaxws</artifactId>
<version>${cxf.version}</version>
</dependency>
<dependency>
<groupId>org.apache.cxf</groupId>
<artifactId>cxf-rt-transports-http</artifactId>
<version>${cxf.version}</version>
</dependency>
<!-- 如果CXF不集成到Web服务器中,需要cxf自带的jetty来发布服务,如果将cxf放到tomcat中应去掉此jar包,否则会有类冲突
<dependency>
<groupId>org.apache.cxf</groupId>
<artifactId>cxf-rt-transports-http-jetty</artifactId>
<version>${cxf.version}</version>
</dependency> -->
<!-- End CXF -->
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.tomcat.maven</groupId>
<artifactId>tomcat7-maven-plugin</artifactId>
<version>2.2</version>
<configuration>
<port>9090</port>
<path>/cxf-spring-web</path>
<uriEncoding>UTF-8</uriEncoding>
<server>tomcat7</server>
</configuration>
</plugin>
</plugins>
<finalName>cxf-spring-test</finalName>
</build>
</project>
服务端
WebService接口及实现类
刚创建好的web工程没有src/main/java目录,在Java Build path中添加src/main/java目录。
创建包com.masi.cxfspring.server,在包内创建接口IHelloWS,和实现类HelloWSImpl:
package com.masi.cxfspring.server;
import javax.jws.WebService;
@WebService
public interface IHelloWS {
public String sayHello(String name);
}
package com.masi.cxfspring.server;
public class HelloWSImpl implements IHelloWS{
@Override
public String sayHello(String name) {
return "你好,"+name+",来自cxf+spring";
}
}
web.xml配置
打开src/main/webapp/WEB-INF/web.xml,指定Spring上下文配置的路径,加载Spring,以及CXF Servlet配置,CXF利用org.apache.cxf.transport.servlet.CXFServlet来拦截所有web请求。
<web-app>
<display-name>Archetype Created Web Application</display-name>
<!-- spring上下文配置的路径 -->
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:applicationContext-server.xml</param-value>
</context-param>
<!-- 加载Spring容器 -->
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<!-- CXF Servlet -->
<servlet>
<servlet-name>CXFServlet</servlet-name>
<servlet-class>org.apache.cxf.transport.servlet.CXFServlet</servlet-class>
</servlet>
<!-- 匹配/services下的所有请求 -->
<servlet-mapping>
<servlet-name>CXFServlet</servlet-name>
<url-pattern>/services/*</url-pattern>
</servlet-mapping>
</web-app>
Spring上下文配置
在src/main/resources中添加一个Spring上下文配置applicationContext-server.xml,注意web.xml中的contextConfigLocation配置必须能够找到此配置文件,如果不在web.xml中配置contextConfigLocation,默认的路径是/WEB-INF/applicationContext.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"
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">
<import resource="classpath:META-INF/cxf/cxf.xml" />
<jaxws:endpoint id="helloWebService" implementor="com.masi.cxfspring.server.HelloWSImpl" address="/HelloWS">
</jaxws:endpoint>
</beans>
启动tomcat
工程上右键->Run As->Maven build…->
Goals:tomcat7:run
启动tomcat
在浏览器输入地址 http://localhost:9090/cxf-spring-web/services
可看到发布的服务列表,点击WSDL链接,跳转到wsdl页面
http://localhost:9090/cxf-spring-web/services/HelloWS?wsdl
注意tomcat启动后地址为:http://localhost:{port}{path}{url-pattern}
其中{port}为tomcat插件中配置的端口,本例中是9090
{path}为tomcat插件中配置的路径,本例中是/cxf-spring-web,如果不配置path,默认为工程名cxf-spring-test
{url-pattern}是web.xml中配置的servlet-mapping中的url-pattern,本例中是/services
客户端
Spring上下文配置
在src/main/resources中添加一个Spring上下文配置applicationContext-client.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"
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">
<jaxws:client id="helloWSClient" serviceClass="com.masi.cxfspring.server.IHelloWS"
address="http://localhost:9090/cxf-spring-web/services/HelloWS">
</jaxws:client>
</beans>
客户端调用类
创建包com.masi.cxfspring.client,在包内创建接口HelloClient,加载Spring上下文配置,获取bean,调用服务
package com.masi.cxfspring.client;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import com.masi.cxfspring.server.IHelloWS;
public class HelloClient {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext-client.xml");
IHelloWS helloClient = (IHelloWS)context.getBean("helloWSClient");
System.out.println(helloClient.sayHello("Matt马特"));
}
}
Run As->Java Application启动客户端,在控制台看到服务端的返回结果。
错误
Servlet类冲突
错误:
java.lang.ClassCastException: org.apache.cxf.transport.servlet.CXFServlet cannot be cast to javax.servlet.Servlet
原因:
servlet-api.jar与tomcat内置的servlet-api包冲突
https://issues.apache.org/jira/browse/CXF-3306
http://www.tuicool.com/articles/YZz2Mb
http://www.bubuko.com/infodetail-658239.html
http://www.cnblogs.com/xiluhua/p/4469443.html
http://www.cnblogs.com/tiramisuyj/p/4811782.html
解决:
maven中添加servlet-api.jar依赖时增加<scope>provided</scope>
,provided表明该包只在编译和测试的时候用,而在运行阶段,假定目标容器已经提供了这个jar包
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>servlet-api</artifactId>
<version>2.4</version>
<scope>provided</scope>
</dependency>
但本地项目中已经加了scope provided配置,还是出问题,后来改为tomcat6:run-war命令启动tomcat得到解决(之前是tomcat6:run启动)
ServletContainerInitializer类冲突
错误:
java.lang.ClassCastException: Cannot cast org.springframework.web.SpringServletContainerInitializer to javax.servlet.ServletContainerInitializer
原因:
jar包重复
http://bbs.csdn.net/topics/390779873
解决:
去掉如下依赖:
<dependency>
<!-- 如果CXF不集成到Web服务器中,需要cxf自带的jetty来发布服务 -->
<groupId>org.apache.cxf</groupId>
<artifactId>cxf-rt-transports-http-jetty</artifactId>
<version>${cxf.version}</version>
</dependency>
参考
手把手教你 cxf3.1.4整合spring4.2
http://blog.csdn.net/jquerys/article/details/50634576WebService CXF学习(高级篇1):整合Spring框架
http://xuzhfa123.iteye.com/blog/564289CXF实战之集成Spring(三)
http://blog.csdn.net/accountwcx/article/details/47088419CXF实战之在Tomcat中发布Web Service(二)
http://blog.csdn.net/accountwcx/article/details/47085269
拦截器Interceptor
拦截器(Interceptor)是CXF功能最主要的扩展点,可以在不对核心模块进行修改的情况下,动态添加很多功能。拦截器和JAX-WS Handler、Filter的功能类似,当服务被调用时,就会创建一个拦截器链(Interceptor Chain),拦截器链在服务输入(IN)或输出(OUT)阶段实现附加功能。
拦截器可以在客户端,也可以在服务端添加。当客户端发起一个WebService请求时,在客户端会创建输出拦截器链,服务端接收到客户端的后,会创建输入拦截器链。当服务端返回响应消息时,响应消息会经过服务端的输出拦截链,客户端接收到服务端的响应时,会创建输入拦截器链,响应消息先经过输入拦截器链处理。
错误
错误:
javax.xml.ws.soap.SOAPFaultException: MustUnderstand headers: [{http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd}Security] are not understood.
原因:
webservice服务端没有处理SOAP头部中的wss认证信息,具体的,jaxws:endpoint发布服务的时候没有配置jaxws:inInterceptors处理wss认证
解决:
jaxws:endpoint发布服务时候配置jaxws:inInterceptors处理wss认证
参考
CXF实战之拦截器Interceptor(四)
http://blog.csdn.net/accountwcx/article/details/47102319CXF实战之自定义拦截器(五)
http://blog.csdn.net/accountwcx/article/details/47147831
CXF BUS
CXF中的BUS就是一个骨架,管理着CXF的扩展插件以及提供拦截器。注意,BUS提供的拦截器与具体的服务endpoint提供的拦截器是有一点区别的。BUS提供的拦截器将作用于所有的收到、发送以及错误(Fault)的消息。默认情况下,BUS没有为我们提供任何拦截器。
例如,为所有的endpoint提供日志拦截:
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:cxf="http://cxf.apache.org/core"
xsi:schemaLocation="
http://cxf.apache.org/core http://cxf.apache.org/schemas/core.xsd
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd">
<import resource= "classpath:META-INF/cxf/cxf.xml" />
<bean id="logOutbound" class="org.apache.cxf.interceptor.LoggingOutInterceptor"/>
<cxf:bus>
<cxf:outInterceptors>
<ref bean="logOutbound"/>
</cxf:outInterceptors>
</cxf:bus>
</beans>
注意:在使用<cxf:bus>
的时候,一定要引入 命名空间xmlns:cxf=http://cxf.apache.org/core ,及其对应的模式http://cxf.apache.org/schemas/core.xsd
错误:通配符的匹配很全面, 但无法找到元素 ‘cxf:bus’ 的声明
原因:beans命名空间和模式文档有问题,错误配置如下:
<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:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:cxf="http://cxf.apache.org/core"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://cxf.apache.org/jaxws
http://cxf.apache.org/core
http://cxf.apache.org/schemas/core.xsd
http://cxf.apache.org/schemas/jaxws.xsd">
jaxws和core命名空间与其对应的xsd文档没有分别组成一对值。
解决:
xsi:schemaLocation改为
http://cxf.apache.org/jaxws
http://cxf.apache.org/schemas/jaxws.xsd
http://cxf.apache.org/core
http://cxf.apache.org/schemas/core.xsd
参考
Configuring the Bus
http://cxf.apache.org/docs/bus-configuration.htmlCXF BUS 施用
http://www.myexception.cn/open-source/1257299.htmlCXF bus interceptor配置
http://hougbin.iteye.com/blog/709866
参考
WebService CXF学习:入门篇、进阶篇、高级篇
http://xuzhfa123.iteye.com/category/91905CXF实战
http://blog.csdn.net/accountwcx/article/category/1283262WebService基础学习(三)—CXF
http://www.cnblogs.com/yangang2013/p/5712299.html
上一篇 Java-Annotation
下一篇 SOAP
页面信息
location:
protocol
: host
: hostname
: origin
: pathname
: href
: document:
referrer
: navigator:
platform
: userAgent
: