Apache CXF 入门第一个示例

目录

CXF Summary

二进制 jar 包

Maven 依赖

第一个案例

服务端

浏览器访问

客户端

服务调用测试


CXF Summary

1、JWS 是 Java 语言对 WebService 服务的一种实现,用来开发和发布服务,不需要任何第三方库,为 Java 开发者提供便捷发布和调用 WebService 服务的途径。

2、借助一些 WebService 框架可以很轻松地把自己的业务对象发布成 WebService 服务,实现更复杂的功能,Java 语言常用的第三方 WebService 框架有:axis,xfire,cxf 等。可以参考《WebService 理论详解》中的 "常见 Web Service 框架"

3、CXF(Celtix + XFire) 是 Apache 旗下重磅的 SOA (Service Oriented Architecture) 面向服务的框架,它实现了 ESB(企业服务总线),能很好的和 Spring 进行集成。

4、CXF 官网地址:http://cxf.apache.org/

5、CXF 不但是一个优秀的 Web Services / SOAP / WSDL 引擎,也是一个不错的 ESB 总线,为 SOA 的实施提供了一种选择方案。虽然 Axis2 出现的时间较早,但 CXF 的追赶速度快。

二进制 jar 包

1、如果项目是手动导包的方式,则需要获取 CXF 的开发包

2、进入官网下载地址:http://cxf.apache.org/download.html

3、如上图所示 .gz 为 Linux系统版本,.zip 为 Windows 系统版本

4、如上图所示:

1、bin :存放了 wsdl2java 等工具

2、docs:存放的 API 文档

3、lib:包含了开发包,里面一共 183 个子文件,显然如果开发时,全部导入就有点臃肿了。

4、samples:存放了示例

Maven 依赖

1、如果项目是  Maven 管理,则可以从 Maven 中央仓库获取:https://mvnrepository.com/search?q=cxf

第一个案例

1、webService 服务端开发流程为:

1)定义 SEI(Service Endpoint Interface)服务终端接口,即 webService 提供的服务接口

2)定义 SIB(Service Implemention Bean)服务实现类,即 webService 提供的服务接口的实现类

3)发布 webService 服务  Endpoint publish(String address, Object implementor)

2、如果仅仅是简单的发布一个 webService 服务,则代码和《JWS(Java Web Service) 第一个案例入门》完全一样,只需要把 CXF 的 Jar 包导入,JWS 的代码不用变。

3、而客户端也和 JWS 时完全一样,连 CXF 包都可以不用导入,当然如果开发 cxf 另外更复杂的功能除外。

4、已经知道 Java JDK 自带的 JWS 开发 webService 时,方法的参数与返回值支持 Java 8种基本类型,支持 Lsit、Set 等集合类型,Array 类型,自定义的 POJO 对象,但是不支持 Map 类型。而 CXF 可以支持 Map 类型。本文的示例中将会出现 Map 类型。

服务端

1、如果仅仅是简单的发布一个 webService 服务,则代码和《JWS(Java Web Service) 第一个案例入门》完全一样,只需要把 CXF 的 Jar 包导入,JWS 的代码不用变。

2、这里新建一个 Java SE 项目,不使用 Maven 管理,直接手动导入 Jar 包。

3、因为代码都是 JWS 的代码,所以本文将不再详细说明,可以参考《JWS(Java Web Service) 第一个案例入门》。

如上所示:

Student:是一个自定义的学生实体类

StudentService:web Service 服务接口,里面的注释应该尽量详细,因为客户端开发人员使用工具生成代理类时,接口中的内容是可以还原的。

StudentServiceImpl:服务接口的实现类,客户端代码生成工具是无法还原其中的内容的。

Web_Service:用于 Main 方法启动 webService 服务。

4、已经知道下载的 cxf 3.30 版本中的 lib 目录下默认提供了 182个 jar 包,如果想为了简单,则可以将它们全部导入到项目中。然而如果只是想发布一个 webService 服务,其实是用不了全部的包的,经过反复测试验证,导入下面这些包即可满足基本开发,如果以后想开发其它功能而缺少 jar 包时,则再补充即可:

Student 实体类内容如下:

import java.util.Date;

/**
 * Created by Administrator on 2019/2/13 0013.
 * Student 实体
 */
public class Student {
    /**
     * sID:学生学号
     * sName:学生姓名
     * sex:性別,true 表示男生、false 表示女生
     * birthday:学生生日
     * punishStatus:在校是否被惩罚过,未惩罚(0)、警告(1)、记过(2)、记大过(3)、留校察看(4)、开除学籍(5)
     */
    private Integer sID;
    private String sName;
    private Boolean sex;
    private Date birthday;
    private Integer punishStatus;

    public Boolean getSex() {
        return sex;
    }

    public void setSex(Boolean sex) {
        this.sex = sex;
    }

    public Date getBirthday() {
        return birthday;
    }

    public void setBirthday(Date birthday) {
        this.birthday = birthday;
    }

    public Integer getPunishStatus() {
        return punishStatus;
    }

    public void setPunishStatus(Integer punishStatus) {
        this.punishStatus = punishStatus;
    }

    public Integer getsID() {
        return sID;
    }

    public void setsID(Integer sID) {
        this.sID = sID;
    }

    public String getsName() {
        return sName;
    }

    public void setsName(String sName) {
        this.sName = sName;
    }

    @Override
    public String toString() {
        return "Student{" +
                "birthday=" + birthday +
                ", sID=" + sID +
                ", sName='" + sName + '\'' +
                ", sex=" + sex +
                ", punishStatus=" + punishStatus +
                '}';
    }
}

StudentService 接口内容如下:

import com.lct.domain.Student;

import javax.jws.WebParam;
import javax.jws.WebResult;
import javax.jws.WebService;
import java.util.Map;

/**
 * Created by Administrator on 2019/2/13 0013.
 * 提供查询学生的服务接口
 *
 * @javax.jws.WebService : 将 Java 类/接口标记为 Web Service(web服务)
 * 服务端的 web service 接口与其实现类都必须写 @WebService 注解
 */
@WebService
public interface StudentService {

    /**
     * 根据学生学号获取学生 "奖惩信息"
     *
     * @param sId :学生学号
     * @return 返回整个学生的简要实体数据, 主要是演示返回值为实体
     */
    @WebResult(name = "getStudentById")
    public Student getStudentById(@WebParam(name = "sId") Integer sId);

    /**
     * 学生是否支付全部学费
     *
     * @param student
     * @return
     * @WebParam 的作用是 java jdk wsimport 工具生成的代理类看起来可以更直观,否则会是 arg0、arg1 ...
     * 单纯从调用上来说,写没写 @WebParam 调用都是一样的
     */
    @WebResult(name = "isPayTuition")
    public boolean isPayTuition(@WebParam(name = "Student") Student student);

    /**
     * 获取所有学生
     *
     * @WebResult 、@WebParam 写不写都是可以的,写了只是看起来更直观而已
     * 建议的方式是 @WebResult 可以不写,@WebParam 写上解释一下参数
     */
    public Map<Integer, Student> getAllStudentsForMap();
}

StudentServiceImpl 实现类内容如下:

import com.lct.domain.Student;

import javax.jws.WebService;
import java.util.*;
import java.util.logging.Logger;

/**
 * Created by Administrator on 2019/2/13 0013.
 *
 * @javax.jws.WebService : 将 Java 类/接口标记为 Web Service(web服务),服务端的 web service 接口与其实现类都必须写 @WebService 注解
 * endpointInterface :必须写上,值使用服务接口的全路径即可
 */
@WebService(endpointInterface = "com.lct.web_service.StudentService")
public class StudentServiceImpl implements StudentService {
    /**
     * 日志记录器
     */
    public static final Logger logger = Logger.getGlobal();

    @Override
    public Student getStudentById(Integer sId) {
        /**随机生成一个学生,只做演示*/
        Student student = new Student();
        student.setsID(sId);
        student.setBirthday(new Date());
        student.setPunishStatus(new Random().nextInt(6));
        student.setsName(UUID.randomUUID().toString());
        logger.info("获取学号 " + sId + " 的学生信息结果为:" + student.toString());
        return student;
    }

    @Override
    public boolean isPayTuition(Student student) {
        /**随机生成是否已经交费,nextInt(a)生成 [0,a)直接的随机整数*/
        boolean flag = new Random().nextInt(2) == 0 ? true : false;
        logger.info(student.toString() + " 是否已经全部缴费?" + flag);
        return flag;
    }

    @Override
    public Map<Integer, Student> getAllStudentsForMap() {
        Map<Integer, Student> map = new HashMap<Integer, Student>();
        for (int i = 0; i < 3; i++) {
            Student student = new Student();
            student.setsID((i + 1));
            student.setBirthday(new Date());
            student.setPunishStatus(new Random().nextInt(6));
            student.setsName(UUID.randomUUID().toString());
            map.put((i + 1), student);
        }
        return map;
    }
}

Web_Service 启动类内容如下,其中核心的一句就是 Endpoint.publish(wsUrl, new StudentServiceImpl());这是 JWS 的 API,不是 CXF 。

import javax.xml.ws.Endpoint;
import java.util.logging.Logger;

/**
 * Created by Administrator on 2019/1/25 0025.
 * webServer 启动类
 */
public class Web_Service {
    //日志记录器
    public static final Logger logger = Logger.getGlobal();

    public static void main(String[] args) {
        /**webService服务器提供给客户端访问的地址
         * 192.168.1.20 为服务器 ip、3333为指定的端口号、web_server 为应用名称、student 为标识
         * 这个地址符合 http 地址即可,为了看起来更像是 web访问,所以才写了应用名,即使是 http://192.168.1.20:3333/xxx 也是可以的
         */
        String wsUrl = "http://192.168.1.20:3333/web_server/student";
        /**
         * javax.xml.ws.Endpoint 表示一个 web service 终端,这是一个抽象类,其中提供了静态方法可以直接调用
         * Endpoint publish(String address, Object implementor)
         * address:传输协议的 url 地址;
         * implementor(实现者):web service 终端的实现类,因为一个 ws 接口可能会有多个实现类
         */
        Endpoint.publish(wsUrl, new StudentServiceImpl());
        logger.info("webService 服务启动,等待客户端请求...");
    }
}

特别提醒:

1、服务端代码编写完毕,功能就是开启一个 webService 服务供客户端调用,其中没有一句 CXF 库的代码

2、main 方法一运行,服务就会发布成功,如果将 lib 下的 CXF 包全部删除,则服务无法启动,因为接口中使用了 Map 数据类型

3、虽然只是导入了 CXF 的包,代码仍然与 JWS 时一样,而结果却不相同,此时会支持 Map 类型,而且生成的 wsdl 文件也不再是以前 jws 生成的了,而是 CXF。

浏览器访问

1、启动服务端

2、生成的 wsdl 文件与未使用 CXF 时 JWS 生成的略有不同,CXF 时的 types 部分直接参数也包含了进来,而 JWS 时是另外访问的。

3、为了日后方便查看,这里将完整内容粘贴如下:

<?xml version='1.0' encoding='UTF-8'?><wsdl:definitions xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/" xmlns:tns="http://web_service.lct.com/" xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/" xmlns:ns1="http://schemas.xmlsoap.org/soap/http" name="StudentServiceImplService" targetNamespace="http://web_service.lct.com/">
  <wsdl:types>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:tns="http://web_service.lct.com/" elementFormDefault="unqualified" targetNamespace="http://web_service.lct.com/" version="1.0">
  <xs:element name="getAllStudentsForMap" type="tns:getAllStudentsForMap"/>
  <xs:element name="getAllStudentsForMapResponse" type="tns:getAllStudentsForMapResponse"/>
  <xs:element name="getStudentById" type="tns:getStudentById"/>
  <xs:element name="getStudentByIdResponse" type="tns:getStudentByIdResponse"/>
  <xs:element name="isPayTuition" type="tns:isPayTuition"/>
  <xs:element name="isPayTuitionResponse" type="tns:isPayTuitionResponse"/>
  <xs:complexType name="isPayTuition">
    <xs:sequence>
      <xs:element minOccurs="0" name="Student" type="tns:student"/>
    </xs:sequence>
  </xs:complexType>
  <xs:complexType name="student">
    <xs:sequence>
      <xs:element minOccurs="0" name="birthday" type="xs:dateTime"/>
      <xs:element minOccurs="0" name="punishStatus" type="xs:int"/>
      <xs:element minOccurs="0" name="sex" type="xs:boolean"/>
      <xs:element minOccurs="0" name="sID" type="xs:int"/>
      <xs:element minOccurs="0" name="sName" type="xs:string"/>
    </xs:sequence>
  </xs:complexType>
  <xs:complexType name="isPayTuitionResponse">
    <xs:sequence>
      <xs:element name="isPayTuition" type="xs:boolean"/>
    </xs:sequence>
  </xs:complexType>
  <xs:complexType name="getStudentById">
    <xs:sequence>
      <xs:element minOccurs="0" name="sId" type="xs:int"/>
    </xs:sequence>
  </xs:complexType>
  <xs:complexType name="getStudentByIdResponse">
    <xs:sequence>
      <xs:element minOccurs="0" name="getStudentById" type="tns:student"/>
    </xs:sequence>
  </xs:complexType>
  <xs:complexType name="getAllStudentsForMap">
    <xs:sequence/>
  </xs:complexType>
  <xs:complexType name="getAllStudentsForMapResponse">
    <xs:sequence>
      <xs:element name="_return">
        <xs:complexType>
          <xs:sequence>
            <xs:element maxOccurs="unbounded" minOccurs="0" name="entry">
              <xs:complexType>
                <xs:sequence>
                  <xs:element minOccurs="0" name="key" type="xs:int"/>
                  <xs:element minOccurs="0" name="value" type="tns:student"/>
                </xs:sequence>
              </xs:complexType>
            </xs:element>
          </xs:sequence>
        </xs:complexType>
      </xs:element>
    </xs:sequence>
  </xs:complexType>
</xs:schema>
  </wsdl:types>
  <wsdl:message name="isPayTuition">
    <wsdl:part element="tns:isPayTuition" name="parameters">
    </wsdl:part>
  </wsdl:message>
  <wsdl:message name="getStudentById">
    <wsdl:part element="tns:getStudentById" name="parameters">
    </wsdl:part>
  </wsdl:message>
  <wsdl:message name="getAllStudentsForMap">
    <wsdl:part element="tns:getAllStudentsForMap" name="parameters">
    </wsdl:part>
  </wsdl:message>
  <wsdl:message name="getAllStudentsForMapResponse">
    <wsdl:part element="tns:getAllStudentsForMapResponse" name="parameters">
    </wsdl:part>
  </wsdl:message>
  <wsdl:message name="getStudentByIdResponse">
    <wsdl:part element="tns:getStudentByIdResponse" name="parameters">
    </wsdl:part>
  </wsdl:message>
  <wsdl:message name="isPayTuitionResponse">
    <wsdl:part element="tns:isPayTuitionResponse" name="parameters">
    </wsdl:part>
  </wsdl:message>
  <wsdl:portType name="StudentService">
    <wsdl:operation name="isPayTuition">
      <wsdl:input message="tns:isPayTuition" name="isPayTuition">
    </wsdl:input>
      <wsdl:output message="tns:isPayTuitionResponse" name="isPayTuitionResponse">
    </wsdl:output>
    </wsdl:operation>
    <wsdl:operation name="getStudentById">
      <wsdl:input message="tns:getStudentById" name="getStudentById">
    </wsdl:input>
      <wsdl:output message="tns:getStudentByIdResponse" name="getStudentByIdResponse">
    </wsdl:output>
    </wsdl:operation>
    <wsdl:operation name="getAllStudentsForMap">
      <wsdl:input message="tns:getAllStudentsForMap" name="getAllStudentsForMap">
    </wsdl:input>
      <wsdl:output message="tns:getAllStudentsForMapResponse" name="getAllStudentsForMapResponse">
    </wsdl:output>
    </wsdl:operation>
  </wsdl:portType>
  <wsdl:binding name="StudentServiceImplServiceSoapBinding" type="tns:StudentService">
    <soap:binding style="document" transport="http://schemas.xmlsoap.org/soap/http"/>
    <wsdl:operation name="isPayTuition">
      <soap:operation soapAction="" style="document"/>
      <wsdl:input name="isPayTuition">
        <soap:body use="literal"/>
      </wsdl:input>
      <wsdl:output name="isPayTuitionResponse">
        <soap:body use="literal"/>
      </wsdl:output>
    </wsdl:operation>
    <wsdl:operation name="getStudentById">
      <soap:operation soapAction="" style="document"/>
      <wsdl:input name="getStudentById">
        <soap:body use="literal"/>
      </wsdl:input>
      <wsdl:output name="getStudentByIdResponse">
        <soap:body use="literal"/>
      </wsdl:output>
    </wsdl:operation>
    <wsdl:operation name="getAllStudentsForMap">
      <soap:operation soapAction="" style="document"/>
      <wsdl:input name="getAllStudentsForMap">
        <soap:body use="literal"/>
      </wsdl:input>
      <wsdl:output name="getAllStudentsForMapResponse">
        <soap:body use="literal"/>
      </wsdl:output>
    </wsdl:operation>
  </wsdl:binding>
  <wsdl:service name="StudentServiceImplService">
    <wsdl:port binding="tns:StudentServiceImplServiceSoapBinding" name="StudentServiceImplPort">
      <soap:address location="http://192.168.1.20:3333/web_server/student"/>
    </wsdl:port>
  </wsdl:service>
</wsdl:definitions>

客户端

1、客户端代码也和 JWS 时完全一样,连 CXF 包都可以不用导入,当然如果开发 cxf 另外更复杂的功能除外。因为本身也牵涉到多少代码。

2、值得注意的是,客户端使用工具生成的代理类放置的包路径建议和服务端保持一致,一是因为类之间相互引用时使用的是绝对路径,是写死了过来的,二是可能引起一些其它的问题。可以根据服务端的 wsdl 确定包名。

3、这里仍然新建一个普通的 Java SE 项目。

wsdl2java 简介

1、根据服务端 wsdl 文件生成客户端代理类可以使用 Java JDK 自带的 wsimport.exe 工具,参考《 Java JDK wsimport.exe 根据wsdl文件生成类文件》,也可以使用 CXF 框架自带的工具 wsdl2java,解压后,在 bin 目录下,其中还提供了很多的其它工具。

2、cmd 进入此目录后即可使用这些工具,在 cmd 中可以使用 wsdl2java -help 查看帮助,即可看到所有的参数,以及使用方法。

3、其中常用的命令如下:

.\wsdl2java -d E:/wmx/webservice/cxf -encoding UTF-8 -verbose wsdlurl

-d:表示生产的代理类存放的目录,目录不存在时会自动创建。wsimport.exe 工具则目录必须事先存在

-encoding:指定生成的代理类的编码,指定和服务端的一致,否则里面的中文会乱码

-verbose:表示控制台显示转换进度信息

4、直接将生成好的所有代理类导入到客户端中,注意将根包目录导入,确保和服务器路径一致,这样类中默认引用的路径才不会错。注意下面是一个新项目作为客户端。

5、如上所示引入之后,编码则简单了,无关 JWS ,也无关 CXF,就是调用这些代理类即可,Web_service 类为测试调用类,内容如下:

import com.lct.web_service.GetAllStudentsForMapResponse;
import com.lct.web_service.Student;
import com.lct.web_service.StudentService;
import com.lct.web_service.StudentServiceImplService;

import java.util.List;
import java.util.logging.Logger;

/**
 * Created by Administrator on 2019/1/25 0025.
 * CXF wsdl2java 工具常用指令:.\wsdl2java -d E:/wmx/webservice/cxf -encoding UTF-8 -verbose wsdlurl
 */
public class Web_service {
    //日志记录器
    public static final Logger logger = Logger.getGlobal();

    public static void main(String[] args) {
        /**1、创建实现类对象
         * */
        StudentServiceImplService studentServiceImplService = new StudentServiceImplService();
        /** 2、获取服务接口实例
         * 这些代码只能看着源码跟着感觉来写,因为代码完全是人家写的,对方通常也不会提供什么使用文档的
         */
        StudentService studentService = studentServiceImplService.getStudentServiceImplPort();
        /**
         *3、有了服务接口实例,就可以调用其中的方法了,这里返回值也是对方使用的对象
         */
        Student student = studentService.getStudentById(11);//调用服务
        System.out.println(student);
        System.out.println(studentService.isPayTuition(student));//调用服务

        //调用服务,Map数据类型时,JDK 的 JWS 是不支持的,但是 CXF 支持,
        GetAllStudentsForMapResponse.Return aReturn = studentService.getAllStudentsForMap();
        //这是 CXF 生成时使用的数据类型,看着源码就可以轻松使用
        List<GetAllStudentsForMapResponse.Return.Entry> entryList = aReturn.getEntry();
        for (GetAllStudentsForMapResponse.Return.Entry entry : entryList) {
            System.out.println(entry.getKey() + ":::" + entry.getValue());
        }
    }
}

服务调用测试

调用成功,文档结束。

猜你喜欢

转载自blog.csdn.net/wangmx1993328/article/details/87533336