【转载】使用CXF开发WebService

CXF相关的文章看了很多,但是这篇是讲解的最清楚的!

原文链接:http://my.oschina.net/huangyong/blog/286439#OSC_h1_3

以下是文章的部分内容,仅供备忘!

 

在 Web 容器中使用 Spring + CXF 发布 WS

Tomcat + Spring + CXF,这个场景应该更加接近我们的实际工作情况,开发过程也是非常自然。

第一步:配置 Maven 依赖


<?xml version="1.0" encoding="UTF-8"?>
<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/xsd/maven-4.0.0.xsd">

    <modelVersion>4.0.0</modelVersion>

    <groupId>demo.ws</groupId>
    <artifactId>soap_spring_cxf</artifactId>
    <version>1.0-SNAPSHOT</version>
    <packaging>war</packaging>

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <spring.version>4.0.5.RELEASE</spring.version>
        <cxf.version>3.0.0</cxf.version>
    </properties>

    <dependencies>
        <!-- Spring -->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>${spring.version}</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-web</artifactId>
            <version>${spring.version}</version>
        </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>
    </dependencies>

</project>

第二步:写一个 WS 接口及其实现

接口部分:


package demo.ws.soap_spring_cxf;

import javax.jws.WebService;

@WebService
public interface HelloService {

    String say(String name);
}

实现部分:


package demo.ws.soap_spring_cxf;

import javax.jws.WebService;
import org.springframework.stereotype.Component;

@WebService
@Component
public class HelloServiceImpl implements HelloService {

    public String say(String name) {
        return "hello " + name;
    }
}

需要在实现类上添加 Spring 的 org.springframework.stereotype.Component 注解,这样才能被 Spring IOC 容器扫描到,认为它是一个 Spring Bean,可以根据 Bean ID(这里是 helloServiceImpl)来获取 Bean 实例。

第三步:配置 web.xml


<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://java.sun.com/xml/ns/javaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
         http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
         version="3.0">

    <!-- Spring -->
    <context-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>classpath:spring.xml</param-value>
    </context-param>
    <listener>
        <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
    </listener>

    <!-- CXF -->
    <servlet>
        <servlet-name>cxf</servlet-name>
        <servlet-class>org.apache.cxf.transport.servlet.CXFServlet</servlet-class>
    </servlet>
    <servlet-mapping>
        <servlet-name>cxf</servlet-name>
        <url-pattern>/ws/*</url-pattern>
    </servlet-mapping>

</web-app>

所有带有 /ws 前缀的请求,将会交给被 CXFServlet 进行处理,也就是处理 WS 请求了。目前主要使用了 Spring IOC 的特性,利用了 ContextLoaderListener 加载 Spring 配置文件,即这里定义的 spring.xml 文件。

第四步:配置 Spring

配置 spring.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:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans-4.0.xsd
       http://www.springframework.org/schema/context
       http://www.springframework.org/schema/context/spring-context-4.0.xsd">

    <context:component-scan base-package="demo.ws"/>

    <import resource="spring-cxf.xml"/>

</beans>

以上配置做了两件事情:

  1. 定义 IOC 容器扫描路径,即这里定义的 demo.ws,在这个包下面(包括所有子包)凡是带有 Component 的类都会扫描到 Spring IOC 容器中。
  2. 引入 spring-cxf.xml 文件,用于编写 CXF 相关配置。将配置文件分离,是一种很好的开发方式。

第五步:配置 CXF

配置 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"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans-4.0.xsd
       http://cxf.apache.org/jaxws
       http://cxf.apache.org/schemas/jaxws.xsd">

    <jaxws:server id="helloService" address="/soap/hello">
        <jaxws:serviceBean>
            <ref bean="helloServiceImpl"/>
        </jaxws:serviceBean>
    </jaxws:server>

</beans>

通过 CXF 提供的 Spring 命名空间,即 jaxws:server,来发布 WS。其中,最重要的是 address 属性,以及通过 jaxws:serviceBean 配置的 Spring Bean。

可见,在 Spring 中集成 CXF 比想象的更加简单,此外,还有一种更简单的配置方法,那就是使用 CXF 提供的 endpoint 方式,配置如下:


<?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-4.0.xsd
       http://cxf.apache.org/jaxws
       http://cxf.apache.org/schemas/jaxws.xsd">

    <jaxws:endpoint id="helloService" implementor="#helloServiceImpl" address="/soap/hello"/>

</beans>

使用 jaxws:endpoint 可以简化 WS 发布的配置,与 jaxws:server 相比,确实是一种进步。

注意:这里的 implementor 属性值是 #helloServiceImpl,这是 CXF 特有的简写方式,并非是 Spring 的规范,意思是通过 Spring 的 Bean ID 获取 Bean 实例。

同样,也可以在 Spring 中使用 simple 方式来发布 WS,配置如下:


<?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:simple="http://cxf.apache.org/simple"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans-4.0.xsd
       http://cxf.apache.org/simple
       http://cxf.apache.org/schemas/simple.xsd">

    <simple:server id="helloService" serviceClass="#helloService" address="/soap/hello">
        <simple:serviceBean>
            <ref bean="#helloServiceImpl"/>
        </simple:serviceBean>
    </simple:server>

</beans>

可见,simple:server 与 jaxws:server 的配置方式类似,都需要配置一个 serviceBean

比较以上这三种方式,我个人更加喜欢第二种,也就是 endpoint 方式,因为它够简单!

至于为什么 CXF 要提供如此之多的 WS 发布方式?我个人认为,CXF 为了满足广大开发者的喜好,也是为了向前兼容,所以这些方案全部保留下来了。

第六步:启动 Tomcat

将应用部署到 Tomcat 中,在浏览器中输入以下地址可进入 CXF 控制台:

http://localhost:8080/ws

CXF 控制台

通过以上过程,可以看出 CXF 完全具备 RI 的易用性,并且与 Spring 有很好的可集成性,而且配置也非常简单。

同样通过这个地址可以查看 WSDL:

http://localhost:8080/ws/soap/hello?wsdl

注意:紧接在 /ws 前缀后面的 /soap/hello,其实是在 address="/soap/hello" 中配置的。

现在已经成功地通过 CXF 对外发布了 WS,下面要做的事情就是用 WS 客户端来调用这些 endpoint 了。

您可以不再使用 JDK 内置的 WS 客户端,也不必通过 WSDL 打客户端 jar 包,因为 CXF 已经为您提供了多种 WS 客户端解决方案,根据您的口味自行选择吧!

4. 关于 CXF 提供的 WS 客户端

方案一:静态代理客户端


package demo.ws.soap_cxf;

import org.apache.cxf.jaxws.JaxWsProxyFactoryBean;

public class JaxWsClient {

    public static void main(String[] args) {
        JaxWsProxyFactoryBean factory = new JaxWsProxyFactoryBean();
        factory.setAddress("http://localhost:8080/ws/soap/hello");
        factory.setServiceClass(HelloService.class);

        HelloService helloService = factory.create(HelloService.class);
        String result = helloService.say("world");
        System.out.println(result);
    }
}

这种方案需要自行通过 WSDL 打客户端 jar 包,通过静态代理的方式来调用 WS。这种做法最为原始,下面的方案更有特色。

方案二:动态代理客户端


package demo.ws.soap_cxf;

import org.apache.cxf.endpoint.Client;
import org.apache.cxf.jaxws.endpoint.dynamic.JaxWsDynamicClientFactory;

public class JaxWsDynamicClient {

    public static void main(String[] args) {
        JaxWsDynamicClientFactory factory = JaxWsDynamicClientFactory.newInstance();
        Client client = factory.createClient("http://localhost:8080/ws/soap/hello?wsdl");

        try {
            Object[] results = client.invoke("say", "world");
            System.out.println(results[0]);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

这种方案无需通过 WSDL 打客户端 jar 包,底层实际上通过 JDK 的动态代理特性完成的,CXF 实际上做了一个简单的封装。与 JDK 动态客户端不一样的是,此时无需使用 HelloService 接口,可以说是货真价实的 WS 动态客户端。

方案三:通用动态代理客户端


package demo.ws.soap_cxf;

import org.apache.cxf.endpoint.Client;
import org.apache.cxf.endpoint.dynamic.DynamicClientFactory;

public class DynamicClient {

    public static void main(String[] args) {
        DynamicClientFactory factory = DynamicClientFactory.newInstance();
        Client client = factory.createClient("http://localhost:8080/ws/soap/hello?wsdl");

        try {
            Object[] results = client.invoke("say", "world");
            System.out.println(results[0]);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

这种方案与“方案三”类似,但不同的是,它不仅用于调用 JAX-WS 方式发布的 WS,也用于使用 simple 方式发布的 WS,更加智能了。

方案四:基于 CXF simple 方式的客户端


package demo.ws.soap_cxf;

import org.apache.cxf.frontend.ClientProxyFactoryBean;

public class SimpleClient {

    public static void main(String[] args) {
        ClientProxyFactoryBean factory = new ClientProxyFactoryBean();
        factory.setAddress("http://localhost:8080/ws/soap/hello");
        factory.setServiceClass(HelloService.class);
        HelloService helloService = factory.create(HelloService.class);
        String result = helloService.say("world");
        System.out.println(result);
    }
}

这种方式仅用于调用 simple 方式发布的 WS,不能调用 JAX-WS 方式发布的 WS,这是需要注意的。

方案五:基于 Spring 的客户端

方法一:使用 JaxWsProxyFactoryBean


<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans-4.0.xsd">

    <bean id="factoryBean" class="org.apache.cxf.jaxws.JaxWsProxyFactoryBean">
        <property name="serviceClass" value="demo.ws.soap_spring_cxf.HelloService"/>
        <property name="address" value="http://localhost:8080/ws/soap/hello"/>
    </bean>

    <bean id="helloService" factory-bean="factoryBean" factory-method="create"/>

</beans>

方法二:使用 jaxws:client(推荐)


<?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-4.0.xsd
       http://cxf.apache.org/jaxws
       http://cxf.apache.org/schemas/jaxws.xsd">

    <jaxws:client id="helloService"
                  serviceClass="demo.ws.soap_spring_cxf.HelloService"
                  address="http://localhost:8080/ws/soap/hello"/>

</beans>

客户端代码:


package demo.ws.soap_spring_cxf;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class Client {

    public static void main(String[] args) {
        ApplicationContext context = new ClassPathXmlApplicationContext("spring-client.xml");

        HelloService helloService = context.getBean("helloService", HelloService.class);
        String result = helloService.say("world");
        System.out.println(result);
    }
}

谈不上那种方案更加优秀,建议根据您的实际场景选择最为合适的方案。

 

Spring + CXF 这对搭档,让发布 WS 更加简单,只需以下四个步骤:

  1. 配置 web.xml
  2. 编写 WS 接口及其实现
  3. 配置 CXF 的 endpoint
  4. 启动 Web 容器
posted @ 2015-11-30 12:35  postnull  阅读(396)  评论(0编辑  收藏  举报