WebService WSDL 文件结构详解

目录

webService 服务端准备

浏览器访问

WSDL文件剖析

数据类型说明

@WebResult 与 @WebParam

学生实体验证示例

服务端

客户端


webService 服务端准备

1、web Service 服务端准备如下:

CalculatorService:web Service 服务端对外提供的服务接口

扫描二维码关注公众号,回复: 5386577 查看本文章

CalculatorServiceImpl:web Service 服务端对外提供的服务接口的实现类

Web_Service:用于启动 web Service 服务

2、提供一个简单的服务接口,用于计算 "加法" 与 "乘法"。

web Service 服务端 3 个类的源码分别如下:

import javax.jws.*;

/**
 * Created by Administrator on 2019/1/25 0025.
 * 计算器接口
 * SEI(Service Endpoint Interface):服务终端接口,即webService提供的服务接口
 * SIB(Service Implemention Bean):服务实现类,即webService提供的服务接口的实现类
 *
 * @javax.jws.WebService : 将 Java 类标记为实现 Web Service,或者将 Java 接口标记为定义 Web Service 接口
 * 不过是服务端还是客户端,接口上都必须写上此注解
 */
@WebService
public interface CalculatorService {
    //加法
    public float addition(float a, float b);
    //乘法
    public float multiplication(float a, float b);
}
import javax.jws.WebService;
import java.util.logging.Logger;

/**
 * Created by Administrator on 2019/1/25 0025.
 * SEI(Service Endpoint Interface):服务终端接口,即webService提供的服务接口
 * SIB(Service Implemention Bean):服务实现类,即webService提供的服务接口的实现类
 *
 * @javax.jws.WebService : 将 Java 类标记为实现 Web Service,或者将 Java 接口标记为定义 Web Service 接口
 * endpointInterface:终端接口,定义服务抽象 Web Service 协定的服务端点接口的完整名称,通常设置为服务实现类的全路径
 * webService 服务端中的接口与实现类都必须加 @WebService 注解
 * @WebService 注解接口一共提供了以下属性,而且都有默认值,它们将来都会出现在 wsdl 的 xml 文件中
 * String name() default "";
 * String targetNamespace() default "";
 * String serviceName() default "";
 * String portName() default "";
 * String wsdlLocation() default "";
 * String endpointInterface() default "";
 */
@WebService(endpointInterface = "com.lct.web_service.CalculatorService")
public class CalculatorServiceImpl implements CalculatorService {
    //日志记录器
    public static final Logger logger = Logger.getGlobal();
    @Override
    public float addition(float a, float b) {
        logger.info("加法计算:" + a + " + " + b + " = " + (a + b));
        return a + b;
    }
    @Override
    public float multiplication(float a, float b) {
        logger.info("乘法计算:" + a + " x " + b + " = " + (a * b));
        return a * b;
    }
}
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 为应用名称、calculator 为标识
         * 这个地址符合 http 地址即可,为了看起来更像是 web访问,所以才写了应用名,即使是 http://192.168.1.20:3333/xxx 也是可以的
         */
        String wsUrl = "http://192.168.1.20:3333/web_server/calculator";
        /**
         * javax.xml.ws.Endpoint 表示一个 web service 终端,这是一个抽象类,其中提供了静态方法可以直接调用
         * Endpoint publish(String address, Object implementor)
         * address:传输协议的 url 地址;
         * implementor(实现者):web service 终端的实现类,因为一个 ws 接口可能会有多个实现类
         */
        Endpoint.publish(wsUrl, new CalculatorServiceImpl());
        logger.info("webService 服务启动...");
    }
}

浏览器访问

1、启动 webService 服务端,然后直接通过浏览器进行访问;

2、访问时结尾必须加 "?wsdl" 参数;

为了后面分析 wsdl 文件更加清晰,这里贴出 wsdl 文件的全部内容如下:

This XML file does not appear to have any style information associated with it. The document tree is shown below.
<!--
     Published by JAX-WS RI (http://jax-ws.java.net). RI's version is JAX-WS RI 2.2.9-b130926.1035 svn-revision#5f6196f2b90e9460065a4c2f4e30e065b245e51e. 
-->
<!--
     Generated by JAX-WS RI (http://jax-ws.java.net). RI's version is JAX-WS RI 2.2.9-b130926.1035 svn-revision#5f6196f2b90e9460065a4c2f4e30e065b245e51e. 
-->
<definitions xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd" xmlns:wsp="http://www.w3.org/ns/ws-policy" xmlns:wsp1_2="http://schemas.xmlsoap.org/ws/2004/09/policy" xmlns:wsam="http://www.w3.org/2007/05/addressing/metadata" xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/" xmlns:tns="http://web_service.lct.com/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns="http://schemas.xmlsoap.org/wsdl/" targetNamespace="http://web_service.lct.com/" name="CalculatorServiceImplService">
    <types>
        <xsd:schema>
            <xsd:import namespace="http://web_service.lct.com/" schemaLocation="http://192.168.1.20:3333/web_server/calculator?xsd=1"/>
        </xsd:schema>
    </types>
    <message name="multiplication">
        <part name="parameters" element="tns:multiplication"/>
    </message>
    <message name="multiplicationResponse">
        <part name="parameters" element="tns:multiplicationResponse"/>
    </message>
    <message name="addition">
        <part name="parameters" element="tns:addition"/>
    </message>
    <message name="additionResponse">
        <part name="parameters" element="tns:additionResponse"/>
    </message>
    <portType name="CalculatorService">
        <operation name="multiplication">
            <input wsam:Action="http://web_service.lct.com/CalculatorService/multiplicationRequest" message="tns:multiplication"/>
            <output wsam:Action="http://web_service.lct.com/CalculatorService/multiplicationResponse" message="tns:multiplicationResponse"/>
        </operation>
        <operation name="addition">
            <input wsam:Action="http://web_service.lct.com/CalculatorService/additionRequest" message="tns:addition"/>
            <output wsam:Action="http://web_service.lct.com/CalculatorService/additionResponse" message="tns:additionResponse"/>
        </operation>
    </portType>
    <binding name="CalculatorServiceImplPortBinding" type="tns:CalculatorService">
        <soap:binding transport="http://schemas.xmlsoap.org/soap/http" style="document"/>
        <operation name="multiplication">
            <soap:operation soapAction=""/>
            <input>
                <soap:body use="literal"/>
            </input>
            <output>
                <soap:body use="literal"/>
            </output>
        </operation>
        <operation name="addition">
            <soap:operation soapAction=""/>
            <input>
                <soap:body use="literal"/>
            </input>
            <output>
                <soap:body use="literal"/>
            </output>
        </operation>
    </binding>
    <service name="CalculatorServiceImplService">
        <port name="CalculatorServiceImplPort"                  binding="tns:CalculatorServiceImplPortBinding">
            <soap:address location="http://192.168.1.20:3333/web_server/calculator"/>
        </port>
    </service>
</definitions>

WSDL文件剖析

WSDL(Web Services Description Language) 是基于 XML web服务描述语言,用于描述 Web Service 及其函数、参数和返回值等,这样客户端才能方便调用。一些开发工具既能根据 Web service 生成 WSDL 文档,又能导入 WSDL 文档,生成调用相应 WebService 的代理类代码。

WSDL 文件保存在Web服务器上,通过一个 url 地址即可访问。客户端要调用一个WebService服务之前,要知道该服务的WSDL文件的地址,WebService 服务提供商通常通过两种方式来暴露它的 WSDL 文件地址:1、注册到UDDI服务器,以便被人查找;2、直接告诉给客户端调用者。

1、WSDL 文件作为一个 XML 文件,顶部必须用 <?xml version="1.0" encoding="UTF-8"?> 声明。

2、WSDL 文档的根元素是 definitions 元素,WSDL 文档包含5部分:types, message, portType, binding,service 。

1)definitions 元素

A、WSDL 文档的根元素都是 definition 元素,其中主要的是最后的两个属性,targetNameSpace 与 name。

B、targetNameSpace 属性 对应 @WebService 注解的 targetNameSpace属性,name 属性对应 @WebService 注解的 serviceName 属性。

C、可以在 webService 服务端的接口实现类中的 @WebService 注解中通过修改属性,从而修改 wsdl 文件的definition 元素的属性值,否则 targetNameSpace 为 "http://" 加 "包名的反写",name 为 "实现类名称"+"Service"。

2)types 元素:定义访问的类型,如请求的方法名,参数,以及返回值等。

可以浏览器访问 schemaLocation 属性的值,看到详细信息。

3)message:SOAP 协议消息传递的参数,同样包括请求与返回两部分。

message 的元素会和 types 中的元素进行对应。

4)portType:指明服务器的接口,并且通过 operation 绑定相应的 input、ouput 消息。

5)binding:指定消息传到所使用的格式

6)service:指定服务所发布的名称

数据类型说明

1、Java 默认提供的 JWS API 开发 webService 时,支持自己的 8 种基本类型: boolean、byte、short、int、long、float、double 和 char、也支持引用类型 List、Set、Array,以及自定义的 POJO 对象 等,但唯独不支持 Map 类型。

2、如果服务接口方法的参数或者返回值是 Map 类型,则 Endpoint.publish 发布时直接报错失败,如下所示为方法的返回值使用 Map 类型时,启动服务时报错:

Exception in thread "main" javax.xml.ws.WebServiceException:
class com.lct.web_service.jaxws.GetStudentMapResponse do not have a property of the name return

3、所以要么就服务接口中的方法参数以及返回值尽量不要使用 Map 类型,换成其它类型,如 List 等,如果一定要用 Map 类型,则只能导入 CXF 等第三方框架的 Jar 包,不用改变任何代码,即可正常发布成功。客户端仍然使用 JWS 的 API 即可,可以不用导入 CXF 的 包。

@WebResult 与 @WebParam

上面 types 元素定义访问的类型,如请求的方法名,参数,以及返回值等,使用的都是默认值,如果想要进行修改,则可以使用 这两个注解。

 @WebResult :定义 wsdl 文件中,服务端方法返回值名称
 @WebParam :定义 wsdl 文件中,客户端请求的方法参数名称

定义在接口中的方法上即可,如下所示进行修改,其余内容不变。

import javax.jws.*;

/**
 * Created by Administrator on 2019/1/25 0025.
 * 计算器接口
 * SEI(Service Endpoint Interface):服务终端接口,即webService提供的服务接口
 * SIB(Service Implemention Bean):服务实现类,即webService提供的服务接口的实现类
 *
 * @javax.jws.WebService : 将 Java 类标记为实现 Web Service,或者将 Java 接口标记为定义 Web Service 接口
 * 不过是服务端还是客户端,接口上都必须写上此注解
 */
@WebService
public interface CalculatorService {
    /**
     * @WebResult :定义 wsdl 文件中,服务端方法返回值名称
     * @WebParam :定义 wsdl 文件中,客户端请求的方法参数名称
     */
    //加法
    @WebResult(name = "additionResult")
    public float addition(@WebParam(name = "a") float a, @WebParam(name = "b") float b);

    //乘法
    @WebResult(name = "multiplicationResult")
    public float multiplication(@WebParam(name = "a") float a, @WebParam(name = "b") float b);
}

学生实体验证示例

这里提供两个服务,一是查询学生在校是否有惩罚的服务(如警告、记过等),二是查询学生的缴费状态。

服务端

服务端内容结构如上。

Student:学生实体类、StudentService:提供的学生服务接口、StudentServiceImpl:服务接口的实现

Web_Service:用于启动 webService 服务

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 +
                '}';
    }
}
import com.lct.domain.Student;

import javax.jws.WebParam;
import javax.jws.WebResult;
import javax.jws.WebService;

/**
 * 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 也是同理,作用是使生成的 wsdl 文件看起来更直观,从调用上来说,写不写都是一样的
     */
    @WebResult(name = "isPayTuition")
    public boolean isPayTuition(@WebParam(name = "Student") Student student);
}
import com.lct.domain.Student;

import javax.jws.WebService;
import java.util.Date;
import java.util.Random;
import java.util.UUID;
import java.util.logging.Logger;

/**
 * Created by Administrator on 2019/2/13 0013.
 *
 * @javax.jws.WebService : 将 Java 类/接口标记为 Web Service(web服务),服务端的 web service 接口与其实现类都必须写 @WebService 注解
 * endpointInterface:终端接口,定义服务抽象 Web Service 协定的服务端点接口的完整名称,通常设置为服务实现类的全路径
 * 写了endpointInterface,@WebResult、@WebParam才会有效
 * serviceName:对应生成的 WSDL 文件中的 definitions 元素中的 name 属性值,默认以服务类名称+"Service"为值
 * 客户端连接时使用QName创建实例时需要传入此 name 属性值,以及targetNamespace属性值,都可以在 @WebService 中指定
 */
@WebService(endpointInterface = "com.lct.web_service.StudentService", serviceName = "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;
    }
}
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 服务启动,等待客户端请求...");
    }
}

启动 webService 后,浏览器便可以进行访问,整个 wsdl 文件:http://192.168.1.20:3333/web_server/student?wsdl、数据类型地址:http://192.168.1.20:3333/web_server/student?xsd=1 (可在 types 的 schemaLocation 属性中找到)。

客户端

新建客户端项目,客户端使用 Java JDK wsimport.exe 工具根据服务端提供的 wsdl 文件地址生成代理类,在 cmd 窗口中操作命令如下:

 wsimport -d E:/wmx/webservice -keep -verbose http://192.168.1.20:3333/web_server/student?wsdl

其中 -d 表示输出内容存放的目录,此目录必须事先存在;最后是 wsdl 地址,结尾必须加上 ?wsdl 参数。

如上所需要的一共7个,2个方法输入输出一共生成4个类,加上 ObjectFactory 对象工厂类、以及 Student 实体类、StudentService 服务接口。导入到客户端项目中:

其中的 Web_service 用于启动应用,调用服务端方法,其内容如下:

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

import javax.xml.namespace.QName;
import javax.xml.ws.Service;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.logging.Logger;

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

    public static void main(String[] args) {
        try {
            /** url:webservice 服务端提供的服务地址,结尾必须加 "?wsdl"*/
            URL url = new URL("http://192.168.1.20:3333/web_server/student?wsdl");

            /** QName 表示 XML 规范中定义的限定名称,QName 的值包含名称空间 URI、本地部分和前缀。
             * QName(final String namespaceURI, final String localPart):指定名称空间 URI 和本地部分的 QName 构造方法。
             * 如下所示的两个数据都可以浏览器访问服务端时返回 xml 中第一行找到,如:
             * <definitions xmlns:wsu=xxxxxxx targetNamespace="http://web_service.lct.com/" name="StudentService">
             * namespaceURI 可以通过服务端的 @WebService 的 targetNamespace 属性指定
             * localPart 可以通过服务端的 @WebService 的 serviceName 属性指定
             */
            QName qName = new QName("http://web_service.lct.com/", "StudentService");

            /**
             * Service 对象提供 Web 服务的客户端视图
             * Service 作为以下内容的工厂:1、目标服务端点的代理,2、用于远程操作的动态面向消息调用的 javax.xml.ws.Dispatch 实例。
             * create(java.net.URL wsdlDocumentLocation,QName serviceName):创建 Service 实例。
             * wsdlDocumentLocation : 服务 WSDL 文档位置的 URL
             * serviceName : 服务的 QName
             */
            Service service = Service.create(url, qName);

            /**
             * 使用 getPorts 方法可以对服务上可用的端口/接口进行枚举
             * getPort(Class<T> serviceEndpointInterface):获取支持指定服务端点接口的对象实例
             * serviceEndpointInterface:指定返回的代理所支持的服务端点接口
             */
            StudentService studentService = service.getPort(StudentService.class);

            Student student = studentService.getStudentById(9527);
            boolean flag = studentService.isPayTuition(student);
            logger.info("获取学生信息:姓名:" + student.getSName() + ",惩罚状态:" + student.getPunishStatus() + ",生日:" + student.getBirthday());
            logger.info("此学生是否已经交完学费:" + flag);

        } catch (MalformedURLException e) {
            e.printStackTrace();
        }
    }
}

先启动服务端,然后运行客户端,结果如下:

webService 调用服务端完全没有问题。

猜你喜欢

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