目录
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 调用服务端完全没有问题。