当前位置 : 首页 » 文章分类 :  开发  »  Apache-CXF

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>

参考


拦截器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 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

参考


参考


上一篇 Java-Annotation

下一篇 SOAP

阅读
评论
4.3k
阅读预计21分钟
创建日期 2017-02-24
修改日期 2017-07-22
类别

页面信息

location:
protocol:
host:
hostname:
origin:
pathname:
href:
document:
referrer:
navigator:
platform:
userAgent:

评论