ServerSWebService之CXF

         前言:WebService就目前来讲已经是一种成熟的技术了,目前对WebService支持的框架也很多,虽然本人并没有对各框架做个测试,但分析比较各框架的优缺点(较为主观),最后还是选择了CXF作为样例框架进行分析,主要是因为性能较佳,支持JAX-WS,Spring无缝集成,上手快等特点,本文旨在提供CXF基本入门demo,帮助快速入门了解CXF的使用。

本篇文章重点关注以下问题:

  • CXF简介,及对安装的简要说明;
  • CXF发布服务和调用服务的几种方式;
  • 客户端调用服务时候的代理超时机制;
  • CXF对WebService的拦截器支持;
  • CXF整合Spring发布、调用WebService。

1、 CXF简介及安装说明

1.1   CXF简介  

      Apache CXF = Celtix + XFire,开始叫 Apache CeltiXfire,后来更名为 Apache CXF。 

     Apache CXF 是一个开源的 WebServices 框架,CXF 帮助您利用 Frontend 编程 API 来构建和开发 Services ,像 JAX-WS 。这些 Services 可以支持多种协议,比如:SOAP、XML/HTTP、RESTful HTTP 或者CORBA ,并且可以在多种传输协议上运行,比如:HTTP、JMS 或者 JBI,CXF 大大简化了 Services 的创建,同时它继承了 XFire 传统,一样可以天然地和 Spring 进行无缝集成。

1.2 安装流程

  1. 到官网下载安装包:http://cxf.apache.org/
  2. 将bin目录下的命令添加到环境变量中:CXF_HOME:D:\Program Files\apache-cxf-3.1.11,再将bin追加到path中;
  3. cmd命令行中输入wsdl2java,看是不是已将所有命令添加到环境编程成功。

2、CXF发布、调用服务的几种方式

    CXF服务端、、客户端实现WebService服务的方式主要有两点:

  1.  ServerFactoryBean
  2. JaxWsServiceFactoryBean(建议用此类)

       调用原则如下图:



 2.1 ServerFactoryBean实现服务端和客户端

2.1.1 ServerFactoryBean实现服务端编程

        首先做两点说明,使用CXF发布一个服务于与Jax-Ws发布一个服务完全不同:

  • 即使不使用@WebService注解,一样可以发布成功某个服务;
  • 即使此类没有对外公布的方法,一样可以发布成功。

      OK,废话不多说,直接上代码:

      先定义对外发布服务的接口:

public interface IHelloService {
    String sayHello(String name);
}
    然后利用ServerFactoryBean对外发布服务:
/**
 * 使用ServerFactoryBean发布CXF的Web服务
 */
public class HelloService implements IHelloService {
    
    /**
     * 对外发布的方法
     * @param name
     * @return
     */
    public String sayHello(String name) {
        System.err.println("hello, " + name);
        return "hello, " + name;
    }

    public static void main(String[] args) {
        ServerFactoryBean bean = new ServerFactoryBean();
        
        // 服务的发布地址
        bean.setAddress("http://localhost:9999/sayHello");
        // 对外提供服务的类的类型,在面向接口编程时填写接口,否则写实现类
        bean.setServiceClass(IHelloService.class);
        // 对外提供服务的功能实现类
        bean.setServiceBean(new HelloService());
        // 发布服务  publish()...
        bean.create();
        System.out.println("服务已发布...");
    }
}
    使用CXF发布完此服务后,使用wsimport生成客户端一样可以成功调用此服务。最后废话一句,CXF要依赖的包太多了,当然也是我懒得去区分各个包的作用导致的。在浏览器中输入http://localhost:9999/sayHello?wsdl验证服务是否发布。 

2.1.2 ServerFactoryBean实现客户端编程

      首先获取服务接口,有两种方式,如何可以直接联系服务方拿到定义的接口文件则可以拿过来直接使用,当然也可以利用wsimport获得接口文件来使用(此方法在java RMI一文中有阐述)。这里我就直接把服务端的接口文件直接拿过来用了。

      直接上代码:

/**
 * 使用ClientProxyFactoryBean调用CXF的Web服务(必须是使用ServerFactoryBean方式发布的)
 */
public class HelloServiceClient {

    public static void main(String[] args) {
        ClientProxyFactoryBean bean = new ClientProxyFactoryBean();
        
        // 设置客户端需要使用服务的地址
        bean.setAddress("http://localhost:9999/sayHello");
        // 创建服务接口对象(接口对象用的仍是服务端的服务接口对象)
        IHelloService helloService = (IHelloService) bean.create(IHelloService.class);
        // 使用服务
        String result = helloService.sayHello("熊燕子");
        System.out.println(result);
    }
}

    当然,客户端既然用的仍然是CXF,那么还是需要导入那么多包的。哭

2.2 JaxWsServiceFactoryBean实现服务端和客户端

       JaxWsServiceFactoryBean是ServerFactoryBean的子类,也是功能扩展类,此类必须要在被发布为服务的类的上添加@WebService注解,如果不加注解,虽然运行不会报错,但是也不会对外暴露任何方法。最重要的一点是,使用此类生成的wsdl文件更加规范

2.2.1 JaxWsServiceFactoryBean实现服务端

    先定义服务接口,直接上代码:

@WebService
@BindingType(value=SOAPBinding.SOAP12HTTP_BINDING)
/**
 * 通过@BindingType(value=SOAPBinding.SOAP12HTTP_BINDING)转换为实现SOAP1.2
 */
public interface IHelloService {

	public String sayHello(String name);
}

    然后发布服务 :

/**
 * 使用JaxWsServerFactoryBean发布CXF的Web服务
 * 注意:需要发布的服务接口必须加入WebService注解,如果不加,虽然不报错,但是所有的方法都暴露不出来
 */
public class HelloService implements IHelloService {
    
    /**
     * 对外发布的方法
     * @param name
     * @return
     */
    @Override
    public String sayHello(String name) {
        System.err.println("hello, " + name);
        return "hello, " + name;
    }
    
    public static void main(String[] args) {
        JaxWsServerFactoryBean bean = new JaxWsServerFactoryBean();
        
        // 设置服务的发布地址
        bean.setAddress("http://localhost:9999/sayHello");
        // 对外提供服务的类的类型,在面向接口编程时填写接口,否则写实现类
        bean.setServiceClass(IHelloService.class);
        // 对外提供服务的功能实现类
        bean.setServiceBean(new HelloService());
        // 发布服务  publish()...
        bean.create();
        System.out.println("服务已发布...");
    }
}
         使用CXF发布完此服务后,使用wsimport生成客户端一样可以成功调用此服务。与wsimport类似的还有CXF提供的wsdl2java命令,也同样可以生成客户端代码。此工具位于CXF_HOME/bin目录下,只是参数与wsimport有所不同,它包含以下常用参数:
  • -d:指明代码生成的目录;
  • -p:指定生成的新的包结构。
比如对上述发布服务生成客户端代码:wsdl2java -d C:\Users\Administrator\Desktop\temp http://localhost:9999/sayHello?wsdl,则会生成如下java文件,拷贝到客户度即可使用。在下面会对如何调用进行说明。

 2.2.2 JaxWsServiceFactoryBean实现客户端

       用JaxWsServiceFactoryBean发布的服务,在客户端需要使用JaxWsProxyFactoryBean类进行调用,调用过程与前面的过程类似。

       这里还是直接把服务端的服务接口文件IHelloService.javaz直接拷贝到客户端来使用。(当然,wsimport和wsdl2java都可以用来生成功能接口)

/**
 * 利用JaxWsProxyFactoryBean进行WebService服务调用(只能调用JaxWsServerFactoryBean发布的服务)
 */
public class HelloServiceClient {

    public static void main(String[] args) {
        JaxWsProxyFactoryBean bean = new JaxWsProxyFactoryBean();
        // 设置客户端需要使用服务的地址
        bean.setAddress("http://localhost:9999/sayHello");
        // 设置服务接口的类型
        bean.setServiceClass(IHelloService.class);
        // 创建服务接口对象
        IHelloService helloService = (IHelloService) bean.create();
        // 调用WebService服务
        String ret = helloService.sayHello("aa");
        System.out.println(ret);
    }
}

 2.3 客户端调用方法补充

        利用wsdl2java命令生成客户端调用代码,进行客户端开发。

/**
 * 通过wsdl2java生成客户端代码调用Webservice服务
 */
public class HelloServiceClient {

    public static void main(String[] args) {
      // 获取服务类对象
      IHelloServiceService helloServiceService = new IHelloServiceService();
      // 获取服务类对象的接口
      IHelloService        helloService        = helloServiceService.getIHelloServicePort();
      // 调用服务
      String               result              = helloService.sayHello("熊燕子");
      System.out.println(result);
    }
}

    可以发现wsdl2java不管是命令还是生成调用客户端代码,都与wsimport非常类似。

3. 客户端调用服务时候的代理超时机制

        远程调用有可预见的失败概率,所以客户端需对服务请求超时做准备。CXF在调用WebService服务时,可以设置响应超时时间或读取超时时间,设置方式也比较简单。为验证客户端超时机制,前面的demo主要有两个地方需要修改:

  • WebService接口方法中,需要增加延时;
  • 客户端调用设置超时和代理;
        服务端仍采用JaxWsServerFactoryBean的方式发布,与上面不同的是,服务的实现中加入5秒的延迟,以验证客户端超时机制的正确性,服务实现代码如下:
/**
 * 使用JaxWsServerFactoryBean发布CXF的Web服务
 * 注意:方法的实现中增加了延迟5秒
 */
public class HelloService implements IHelloService {
    
    /**
     * 对外发布的方法
     * @param name
     * @return
     */
    @Override
    public String sayHello(String name) {
        try {
            Thread.sleep(5000);     /* 服务端增加延迟,以在客户端验证超时机制. */
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.err.println("hello, " + name);
        return "hello, " + name;
    }
    
    public static void main(String[] args) {
        JaxWsServerFactoryBean bean = new JaxWsServerFactoryBean();
        
        // 设置服务的发布地址
        bean.setAddress("http://localhost:9999/sayHello");
        // 对外提供服务的类的类型,在面向接口编程时填写接口,否则写实现类
        bean.setServiceClass(IHelloService.class);
        // 对外提供服务的功能实现类
        bean.setServiceBean(new HelloService());
        // 发布服务  publish()...
        bean.create();
        System.out.println("服务已发布...");
    }
}
 客户端采用JaxWsProxyFactoryBean进行客户端服务调用,与前面不同的加入了超时代理和超时机制:
/**
 * 利用JaxWsProxyFactoryBean进行WebService服务调用(只能调用JaxWsServerFactoryBean发布的服务)
 * 中间部分增加了客户端超时机制
 */
public class HelloServiceClient {

    public static void main(String[] args) {
        JaxWsProxyFactoryBean bean = new JaxWsProxyFactoryBean();
        // 设置客户端需要使用服务的地址
        bean.setAddress("http://localhost:9999/sayHello");
        // 设置服务接口的类型
        bean.setServiceClass(IHelloService.class);
        // 创建服务接口对象
        IHelloService helloService = (IHelloService) bean.create();
        
        /** 超时机制代码-------------begin------------ */
        // 生成客户端代理对象(主要代理超时任务)
        Client client = ClientProxy.getClient(helloService);
        // 获取传输通道对象 --> 此对象关联超时策略
        HTTPConduit conduit = (HTTPConduit) client.getConduit();
        // 获取HttpClientd代理的策略对象,并设置代理的端口号、超时时间等信息
        HTTPClientPolicy httpClientPolicy = new HTTPClientPolicy();
        // 设置代理的IP和端口号
//        httpClientPolicy.setProxyServer("");
//        httpClientPolicy.setProxyServerPort(9999);
        // 设置连接超时
        httpClientPolicy.setConnectionTimeout(1000);
        // 设置读取超时
        httpClientPolicy.setReceiveTimeout(1000);
        conduit.setClient(httpClientPolicy);
        /** 超时机制代码------------ -end ------------ */
        
        // 调用WebService服务
        String ret = helloService.sayHello("熊燕子");
        System.out.println(ret);
    }
}
 代码搭建完成后,可进行两方面的调试:         1.测试连接超时( setConnectionTimeout ),即1秒之内未连接上服务端,客户端可得到连接超时的反馈;            测试方法:服务端不启动,直接运行客户端,客户端控制台报错:connect time out         2. 测试服务调用超时(setReceiveTimeout),即1秒之内要返回读取结果。            测试方法:服务端启动,服务的实现增加延迟5秒,启动客户端,客户端控制台报错:Read timed out

 4、 CXF对WebService的拦截器支持

        WebService服务主要用于远程调用,在进行不同系统之间对接时,必须考虑各系统的安全问题。因此,WebService服务一般都需要进行权限校验。如果用原生的JDK发布WebService服务,则程序员必须对每个SOAP消息进行解析,然后再判断权限。而CXF框架可以除了可以帮我们发布、调用WebService服务,还可以帮助我们进行SOAP消息的解析和生成,这样有助于开发效率的提升。

       WebService分为入拦截器和出拦截器,无论是服务端和客户端都有这两种拦截器。 CXF 除了提供了一些默认的系统拦截器,如日志拦截器,还支持用于自定义拦截器。 

4.1 CXF框架的系统拦截器 

        服务接口还是使用上面定义的IHelloService,这里只上拦截器使用的方法代码:

        服务端增加日志拦截器:

/**
 * 使用JaxWsServerFactoryBean发布CXF的Web服务
 * 注意:服务端增加了系统拦截器,可用于日志记录
 */
public class HelloService implements IHelloService {
    
    /**
     * 对外发布的方法
     * @param name
     * @return
     */
    @Override
    public String sayHello(String name) {
        System.err.println("hello, " + name);
        return "hello, " + name;
    }
    
    public static void main(String[] args) {
        JaxWsServerFactoryBean bean = new JaxWsServerFactoryBean();
        
        // 设置服务的发布地址
        bean.setAddress("http://localhost:9999/sayHello");
        // 对外提供服务的类的类型,在面向接口编程时填写接口,否则写实现类
        bean.setServiceClass(IHelloService.class);
        // 对外提供服务的功能实现类
        bean.setServiceBean(new HelloService());
        
        /** 服务端加入拦截器. */
        // 加入请求的入消息拦截器(日志拦截器)
        bean.getInInterceptors().add(new LoggingInInterceptor());
        // 加入请求的出消息拦截器(日志拦截器)
        bean.getOutInterceptors().add(new LoggingOutInterceptor());
        
        // 发布服务  publish()...
        bean.create();
        System.out.println("服务已发布...");
    }
}

         客户度增加日志拦截器:

/**
 * 利用JaxWsProxyFactoryBean进行WebService服务调用(只能调用JaxWsServerFactoryBean发布的服务)
 * 中间部分增加了客户端日志拦截器
 */
public class HelloServiceClient {

    public static void main(String[] args) {
        JaxWsProxyFactoryBean bean = new JaxWsProxyFactoryBean();
        // 设置客户端需要使用服务的地址
        bean.setAddress("http://localhost:9999/sayHello");
        // 设置服务接口的类型
        bean.setServiceClass(IHelloService.class);
        // 创建服务接口对象
        IHelloService helloService = (IHelloService) bean.create();
        
        /** 加入客户端日志拦截器. */
        // 获取Client代理对象  
        Client client = ClientProxy.getClient(helloService);
        // 加入请求响应的入消息拦截器(日志拦截器)
        client.getInInterceptors().add(new LoggingInInterceptor());  
        // // 加入请求的出消息拦截器(日志拦截器)
        client.getOutInterceptors().add(new LoggingOutInterceptor());  
        
        // 调用WebService服务
        String ret = helloService.sayHello("熊燕子");
        System.out.println(ret);
    }
}

       服务端发布服务后,客户端进行调用,控制台打印分别为:

      客户端:

 

       服务端:

4.2 CXF框架自定义拦截器 

      此处以权限校验拦截器为例,介绍如何使用CXF框架对自定义拦截器的支持,实现权限控制。

      CXF 中自定义拦截器,其实就是对SOAP 消息的封装和解析。这里对SOAP消息进行简单介绍:简单对象访问协议(SOAP)是交换数据的一种协议规范,是一种轻量的、简单的、基于XML的协议,它被设计成在WEB上交换结构化的和固化的信息。其格式如下:

<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">  
    <soap:Header>  
           不是强制出现的, head 元素的内容由程序员控制添加, 主要用于携带一些其它的信息  
    </soap:Header>  
    <soap:Body>  
            body 元素的内容总是默认的,但是有两种情况  
            1. 当webService 正确交互时,Body元素内容由WSDL 控制  
            2. 当WebService 交互错误时,body元素的内容将是Fault 元素  
    </soap:Body>  
</soap:Envelope> 

    是不是很类似html的定义!

步骤一:自定义权限拦截器

<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">  
    <soap:Header>  
        <user>  
            <username>熊燕子</username>  
            <passwor>123456</passwor>  
        </user>  
    </soap:Header>  
    <soap:Body>
	<ns2:sayHello xmlns:ns2="http://cxf4.wj.com/">
		<arg0>熊燕子</arg0>
	</ns2:sayHello>
     </soap:Body> 
</soap:Envelope>

 步骤二:自定义客户端出拦截器

/** 
 * 客户端拦截器:拦截客户端发出的SOAP 消息,然后在<SOAP:Header> 标签中添加自定义xml元素 
 */  
public class UserOutInterceptor extends AbstractPhaseInterceptor<SoapMessage>{  
      
    private String username;
    private String password;
  
    public UserOutInterceptor(String username, String password) {  
        super(Phase.PRE_PROTOCOL);
        this.username = username;
        this.password = password;
    }  
  
    @Override  
    public void handleMessage(SoapMessage soapMessage) throws Fault {  
          
        try {  
            //1. 用原生的w3c 的API 构建 <soap:header>中的一个 元素  
            DocumentBuilder documentBuilder = DocumentBuilderFactory.newInstance().newDocumentBuilder();  
            Document document = documentBuilder.newDocument();  
            Element userEle  = document.createElement("user");  
            Element usernameEle = document.createElement("username");  
            Element passwordEle = document.createElement("password");  
              
            usernameEle.setTextContent(username);  
            passwordEle.setTextContent(password);  
              
            userEle.appendChild(usernameEle);  
            userEle.appendChild(passwordEle);  
              
            //2. 创建一个header,然后添加到当前的header列表中  
            Header header = new Header(new QName("user"),userEle);  
            soapMessage.getHeaders().add(header );  
        } catch (ParserConfigurationException e) {  
            e.printStackTrace();  
        }  
    }  
}

 步骤三:服务端加入拦截器进行权限校验

/** 
 * 服务器端拦截器:在消息到达服务器端时,拦截SOAP 消息,解析 <SOAP:Header> 元素中的指定元素 
 * 继承AbstractPhaseInterceptor 
 * 消息类型:SOAP 消息 
 */  
public class UserInInterceptor extends AbstractPhaseInterceptor<SoapMessage> {  
  
    public UserInInterceptor() {  
        super(Phase.PRE_PROTOCOL);  
        //准备协议化之前拦截   
    }  
  
    @Override  
    public void handleMessage(SoapMessage soapMessage) throws Fault {  
  
        String username;  
        String password;  
        //1. 解析用户名和密码,如果解析过程出错,证明请求参数格式不正确  
        try {  
  
            Header userHeader = soapMessage.getHeader(new QName("user"));  
            //必须是  org.w3c.dom.Element  
            Element userElement = (Element) userHeader.getObject();  
            username = userElement.getElementsByTagName("username").item(0).getTextContent();  
            password = userElement.getElementsByTagName("password").item(0).getTextContent();  
        } catch (Exception ex) {  
            throw new Fault(new RuntimeException("请求参数格式不正确"));  
        }  
          
        //2. 验证用户名和密码  
        if("熊燕子".equals(username) && "123456".equals(password)){  
            System.out.println("权限认证通过....");  
        }else{  
            throw new Fault(new RuntimeException("对不起,您的用户名或密码有误!"));  
        }  
    }  
}

 步骤四:服务端加入自定义权限拦截器,启动服务端

/**
 * 使用JaxWsServerFactoryBean发布CXF的Web服务
 * 注意:服务端增加了系统拦截器,可用于日志记录;加入了自定义权限拦截器进行权限控制
 */
public class HelloService implements IHelloService {
    
    /**
     * 对外发布的方法
     * @param name
     * @return
     */
    @Override
    public String sayHello(String name) {
        System.err.println("hello, " + name);
        return "hello, " + name;
    }
    
    public static void main(String[] args) {
        JaxWsServerFactoryBean bean = new JaxWsServerFactoryBean();
        
        // 设置服务的发布地址
        bean.setAddress("http://localhost:9999/sayHello");
        // 对外提供服务的类的类型,在面向接口编程时填写接口,否则写实现类
        bean.setServiceClass(IHelloService.class);
        // 对外提供服务的功能实现类
        bean.setServiceBean(new HelloService());
        
        /** 服务端加入拦截器. */
        // 加入请求的入消息拦截器(日志拦截器)
        bean.getInInterceptors().add(new LoggingInInterceptor());
        /** 对所有SOAP消息进行权限校验 */
        bean.getInInterceptors().add(new UserInInterceptor());
        // 加入请求的出消息拦截器(日志拦截器)
        bean.getOutInterceptors().add(new LoggingOutInterceptor());
        
        // 发布服务  publish()...
        bean.create();
        System.out.println("服务已发布...");
    }
}

 步骤五:客户端加入拦截器,自动填充请求相关的权限信息,然后发送WebService请求

/**
 * 利用JaxWsProxyFactoryBean进行WebService服务调用(只能调用JaxWsServerFactoryBean发布的服务)
 * 中间部分增加了客户端日志拦截器, 自定义权限登录的拦截器
 */
public class HelloServiceClient {

    public static void main(String[] args) {
        JaxWsProxyFactoryBean bean = new JaxWsProxyFactoryBean();
        // 设置客户端需要使用服务的地址
        bean.setAddress("http://localhost:9999/sayHello");
        // 设置服务接口的类型
        bean.setServiceClass(IHelloService.class);
        // 创建服务接口对象
        IHelloService helloService = (IHelloService) bean.create();
        
        /** 加入客户端日志拦截器. */
        // 获取Client代理对象  
        Client client = ClientProxy.getClient(helloService);
        // 加入请求响应的入消息拦截器(日志拦截器)
        client.getInInterceptors().add(new LoggingInInterceptor());  
        // // 加入请求的出消息拦截器(日志拦截器)
        client.getOutInterceptors().add(new LoggingOutInterceptor());
        /** 在头字段中加入用户名、密码 */
        client.getOutInterceptors().add(new UserOutInterceptor("熊燕子", "123456"));  
        
        // 调用WebService服务
        String ret = helloService.sayHello("熊燕子");
        System.out.println(ret);
    }
}

 步骤六:查看校验过程

5、CXF整合Spring发布 

      CXF与Spring联系紧密,这从CXF的依赖jar包中也可见一般,因此,CXF与Spring整合也很轻松简单。下面还是以发布、调用WebService服务为例来介绍其整合过程。

5.1 CXF整合Spring之发布WebService

步骤一:准备工作

       建立一个Web项目,并将CXF官方包lib目录下的所有jar包全部加入工程;

步骤二:配置Web,xml

  • 配置Spring相关内容,如监听器、指明配置文件等;
  • 配置CXF核心Servlet;
<?xml version="1.0" encoding="UTF-8"?>
<web-app version="2.4" 
	xmlns="http://java.sun.com/xml/ns/j2ee" 
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
	xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee 
	http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd">
	
	<!-- 通过上下文参数指定spring配置文件的位置 -->
	<context-param>
		<param-name>contextConfigLocation</param-name>
		<param-value>classpath:cxf-servlet.xml</param-value>
	</context-param>
	<listener>
		<listener-class>
			org.springframework.web.context.ContextLoaderListener
		</listener-class>
	</listener>
	
	<!-- 配置CXF的核心Servlet -->
	<servlet>
		<servlet-name>cxf</servlet-name>
		<servlet-class>org.apache.cxf.transport.servlet.CXFServlet</servlet-class>
		<!-- 通过初始化参数指定cxf配置文件的位置 -->
		<!-- 
		<init-param>
			<param-name>config-location</param-name>
			<param-value>classpath:cxf-servlet.xml</param-value>
		</init-param>
		 -->
	</servlet>
	<servlet-mapping>
		<servlet-name>cxf</servlet-name>
		<url-pattern>/cxf/*</url-pattern>
	</servlet-mapping>
	<welcome-file-list>
	  <welcome-file>index.jsp</welcome-file>
	</welcome-file-list>
</web-app>
 步骤三:创建spring的配置文件,命名spring-cxf.xml,此XML文件用于配置CXF相关信息

        一般来说,Spring的一个基本原则就是面向接口编程,但是WebService也可进行简单发布,即不面向接口编程,两种形式有所不同,这里都做简要介绍。

<?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:jaxrs="http://cxf.apache.org/jaxrs" 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/jaxrs http://cxf.apache.org/schemas/jaxrs.xsd
            http://cxf.apache.org/jaxws http://cxf.apache.org/schemas/jaxws.xsd
            http://cxf.apache.org/core http://cxf.apache.org/schemas/core.xsd">
	<!-- 引入CXF Bean定义如下,早期的版本中使用 -->
	<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" />
	
	<!-- 第一种发布方式:简单发布(没有接口的发布) -->
	<!-- id:唯一标示  implementor:提供服务的类 address:服务的请求url-->
	<jaxws:endpoint id="helloService" implementor="cn.itcast.cxf.HelloService" address="/hello">
		<!-- 加入请求的消息拦截器 -->
		<jaxws:inInterceptors>
			<bean class="org.apache.cxf.interceptor.LoggingInInterceptor"></bean>
		</jaxws:inInterceptors>
		<!-- 加入响应的消息拦截器 -->
		<jaxws:outInterceptors>
			<bean class="org.apache.cxf.interceptor.LoggingOutInterceptor"></bean>
		</jaxws:outInterceptors>
	</jaxws:endpoint>
	
	<!-- 第二种发布方式:带有接口的发布 -->
	<!-- id:唯一标示 serviceClass:接口类型 address:服务的请求url-->
	<jaxws:server id="hiService" serviceClass="cn.itcast.cxf.IHiService" address="/hi">
		<jaxws:serviceBean>
			<!-- 服务的实现类 -->
			<bean class="cn.itcast.cxf.HiServiceImpl"></bean>
		</jaxws:serviceBean>
		<!-- 加入请求的消息拦截器 -->
		<jaxws:inInterceptors>
			<bean class="org.apache.cxf.interceptor.LoggingInInterceptor"></bean>
		</jaxws:inInterceptors>
		<!-- 加入响应的消息拦截器 -->
		<jaxws:outInterceptors>
			<bean class="org.apache.cxf.interceptor.LoggingOutInterceptor"></bean>
		</jaxws:outInterceptors>
	</jaxws:server>
	
	<jaxws:server id="personService" serviceClass="cn.itcast.cxf.service.IPersonService" address="/person">
		<jaxws:serviceBean>
			<!-- 服务的实现类 -->
			<bean class="cn.itcast.cxf.service.PersonServiceImpl"></bean>
		</jaxws:serviceBean>
		<!-- 加入请求的消息拦截器 -->
		<jaxws:inInterceptors>
			<bean class="org.apache.cxf.interceptor.LoggingInInterceptor"></bean>
		</jaxws:inInterceptors>
		<!-- 加入响应的消息拦截器 -->
		<jaxws:outInterceptors>
			<bean class="org.apache.cxf.interceptor.LoggingOutInterceptor"></bean>
		</jaxws:outInterceptors>
	</jaxws:server>
</beans>
 步骤四:直接贴上服务类代码

     首先是非面向接口编程的服务类:HelloService,对此实现类直接加注解发布,版本1.2;

@WebService
@BindingType(value=SOAPBinding.SOAP12HTTP_BINDING)
public class HelloService {

	public String sayHello(String name){
		return "hello, " + name;
	}
}
    面向接口编程,定义接口IHiService,对接口加注解发布,版本1.2:
@WebService
@BindingType(value=SOAPBinding.SOAP12HTTP_BINDING)
public interface IHiService {

	public String sayHi(String name);
}

    服务接口实现类如下:

public class HiServiceImpl implements IHiService {

	public String sayHi(String name) {
		return "hi, " + name;
	}
}

    最后还有涉及具体对象的服务发布:

    首先定义一个普通javaBean对象Person:

public class Person {
    private String id;
    private String name;
    private String address;
    
    public final String getId()                    { return id; }
    public final String getName()                  { return name; }
    public final String getAddress()               { return address; }
    public final void   setId(String id)           { this.id = id; }
    public final void   setName(String name)       { this.name = name; }
    public final void   setAddress(String address) { this.address = address; }
}

    涉及对象Person的服务接口,对接口加注解发布,版本1.2;

@WebService
@BindingType(value=SOAPBinding.SOAP12HTTP_BINDING)
public interface IPersonService {
	public Person findPersonById(String id);
}

    涉及对象Person对象的服务接口实现类如下:

public class PersonServiceImpl implements IPersonService {

    public Person findPersonById(String id) {
        Person p = new Person();
        p.setId(id);
        p.setAddress("江苏省南京市");
        p.setName("熊燕子");
        return p;
    }
}

 步骤五:启动发布,验证服务发布成功

        如验证Hello服务发布成功,可在启动Tomcat后,在浏览器中输入:http://localhost:8080/Web_WebService_CXF_Server/cxf/,如出现wsdl描述信息,则说明发布成功:

  5.2 CXF整合Spring之调用WebService

         最后在这一节中,面对上面整合Spring发布的三个服务,我使用三种方式进行调用:一是利用wsdl2java生成客户端代码,二是直接把服务端的服务类接口直接拿过来使用,三是利用Spring调用服务。

5.2.1 利用wsdl2java生成客户端代码调用HelloService服务

步骤一:利用wsdl2java命令和服务的wsdl地址生成客户端代码,如下图所示:

 
        通过上述指令,可以生成如下代码:

步骤二:编写客户端调用服务的代码

/**
 * 利用wsdl2java命令生成客户端代码,以便调用WebService服务
 * @author Administrator
 *
 */
public class HelloServiceClient {

    public static void main(String[] args) {
        HelloServiceService helloServiceService = new HelloServiceService();
        HelloService        helloService        = helloServiceService.getHelloServicePort();
        
        String result = helloService.sayHello("熊燕子");
        System.out.println(result);
    }
}
 步骤三:查看调用结果(略)

 5.2.2 直接使用服务接口类调用IHiService服务

步骤一;把服务端的服务接口类拷贝到客户端 注意:服务接口类需拷贝到与服务端同目录下,否则会报错。

步骤二:编写客户端调用代码

/**
 * 直接拷贝服务功能接口至客户端,利用JaxWsProxyFactoryBean实现客户端
 * @author Administrator
 */
public class HiServiceClient {

    public static void main(String[] args) {
        JaxWsProxyFactoryBean bean = new JaxWsProxyFactoryBean();  
        // 设置客户端需要使用服务的地址  
        bean.setAddress("http://localhost:8080/Web_WebService_CXF_Server/cxf/hi");  
        // 设置服务接口的类型  
        bean.setServiceClass(IHiService.class);
        // 创建服务接口对象  
        IHiService hiService = (IHiService) bean.create();
        // 调用WebService服务
        String result = hiService.sayHi("熊燕子");
        System.out.println(result);
    }
}

 5.2.3 利用Spring调用WebService服务

步骤一:在Spring配置文件中配置服务

<?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:jaxrs="http://cxf.apache.org/jaxrs" 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/jaxrs http://cxf.apache.org/schemas/jaxrs.xsd
			            http://cxf.apache.org/jaxws http://cxf.apache.org/schemas/jaxws.xsd
			            http://cxf.apache.org/core http://cxf.apache.org/schemas/core.xsd">
	
	<jaxws:client id="personService" address="http://localhost:8080/Web_WebService_CXF_Server/cxf/person" serviceClass="com.wj.cxf.service.IPersonService"></jaxws:client>
</beans>

 步骤二:获取bean对象,调用服务

/**
 * 利用Spring来调用服务
 * @author Administrator
 */
public class PersonServiceClient {
    
    public static void main(String[] args) {
        ApplicationContext ctx = new ClassPathXmlApplicationContext("com/wj/cxf/ClientBeans.xml");
        IPersonService s = (IPersonService) ctx.getBean("personService");
        Person person = s.findPersonById("1223");
        System.out.println("id      :" + person.getId());
        System.out.println("name    :" + person.getName());
        System.out.println("address :" + person.getAddress());
    }
}

 步骤三:查看结果:

 

代码地址:http://pan.baidu.com/s/1eSDx4bS,密码:qi1n
 

猜你喜欢

转载自super-wangj.iteye.com/blog/2378947
CXF