一、基本概念
1、简介
(1) webservice 即web服务,它是一种跨编程语言和跨操作系统平台的远程调用技术。
(2) JAVA 中共有三种WebService 规范,分别是JAX-WS(JAX-RPC)、JAXM&SAAJ、JAX-RS。
(3) webService三要素:soap、wsdl、uddi
2、webservice开发规范
JAVA 中共有三种WebService规范,分别是JAXM&SAAJ、JAX-WS(JAX-RPC)、JAX-RS。
下面来分别简要的介绍一下这三个规范。
JAX-WS:全称是JavaTM API forXML-Based Web Services
JAX-RS :全称是 JavaTM API forRESTful Web Services
(1)JAX-WS:
JAX-WS(Java API For XML-WebService)
,JDK1.6 自带的版本为 JAX-WS2.1
,其底层支持为JAXB。JAX-WS(JSR 224)规范的API 位于 javax.xml.ws.*
包,其中大部分都是注解,提供API 操作Web 服务(通常在客户端使用的较多,由于客户端可以借助SDK 生成,因此这个包中的API 我们较少会直接使用)。
(2)JAXM&SAAJ:
JAXM(JAVA API For XML Message)
主要定义了包含了发送和接收消息所需的API,相当于Web 服务的服务器端,其API 位于 javax.messaging.*
包,它是JAVA EE 的可选包,因此你需要单独下载。
SAAJ(SOAP With Attachment API For Java,JSR 67)是与JAXM 搭配使用的API,为构建SOAP 包和解析SOAP 包提供了重要的支持,支持附件传输,它在服务器端、客户端都需要使用。这里还要提到的是SAAJ 规范,其API 位于javax.xml.soap.*包。
JAXM&SAAJ 与JAX-WS 都是基于SOAP 的Web 服务,相比之下JAXM&SAAJ暴漏了SOAP更多的底层细节,编码比较麻烦,而JAX-WS 更加抽象,隐藏了更多的细节,更加面向对象,实现起来你基本上不需要关心SOAP 的任何细节。那么如果你想控制SOAP 消息的更多细节,可以使用JAXM&SAAJ,目前版本为1.3。
(3)JAX-RS:
JAX-RS 是JAVA 针对REST(RepresentationState Transfer)风格制定的一套Web 服务规范,由于推出的较晚,该规范(JSR 311,目前JAX-RS 的版本为1.0)并未随JDK1.6 一起发行,你需要到JCP 上单独下载JAX-RS 规范的接口,其API 位于javax.ws.rs.*包。
这里的JAX-WS 和JAX-RS 规范我们采用Apache CXF 作为实现,CXF 是Objectweb Celtix和Codehaus XFire 合并而成。CXF 的核心是org.apache.cxf.Bus(总线),类似于Spring 的ApplicationContext,Bus 由BusFactory 创建,默认是SpringBusFactory 类,可见默认CXF是依赖于Spring 的,Bus 都有一个ID,默认的BUS 的ID 是cxf。你要注意的是Apache CXF2.2 的发行包中的jar 你如果直接全部放到lib 目录,那么你必须使用JDK1.6,否则会报JAX-WS 版本不一致的问题。对于JAXM&SAAJ 规范我们采用JDK 中自带的默认实现。
3、三要素
(1)SOAP 协议
SOAP (Simple Object Access Protocol):简易对象访问协议,soap用来描述传递信息的格式。
它是用于交换XML(标准通用标记语言下的一个子集)编码信息的轻量级协议。它有三个主要方面:XML-envelope为描述信息内容和如何处理内容定义了框架,将程序对象编码成为XML对象的规则,执行远程过程调用(RPC)的约定。SOAP可以运行在任何其他传输协议上。
SOAP作为一个基于XML语言的协议用于有网上传输数据。
SOAP = 在HTTP的基础上+XML数据。
SOAP是基于HTTP的。
SOAP的组成如下
- Envelope – 必须的部分。以XML的根元素出现。
- Headers – 可选的。
- Body – 必须的。在body部分,包含要执行的服务器的方法。和发送到服务器的数据。
(2)WSDL 说明书
Web Service
描述语言 WSDL(SebService Definition Language)
就是用机器能阅读的方式提供的一个正式描述文档而基于XML(标准通用标记语言下的一个子集)的语言,用于描述Web Service及其函数、参数和返回值。因为是基于XML的,所以WSDL既是机器可阅读的,又是人可阅读的。
wsdl说明书
1) 通过wsdl说明书,就可以描述webservice服务端对外发布的服务;
2) wsdl说明书是一个基于xml文件,通过xml语言描述整个服务;
3) 在wsdl说明中,描述了:
- 对外发布的服务名称(类)
- 接口方法名称(方法)
- 接口参数(方法参数)
- 服务返回的数据类型(方法返回值)
一个WSDL文档的根元素是definitions元素,WSDL文档包含7个重要的元素:types, import, message, portType, operations, binding和service元素。
- definitions元素中一般包括若干个XML命名空间;
- Types元素用作一个容器,定义了自定义的特殊数据类型,在声明消息部分(有效负载)的时候
messages定义使用了types元素中定义的数据类型与元素; - Import元素可以让当前的文档使用其他WSDL文档中指定命名空间中的定义;
- Message元素描述了Web服务的有效负载。相当于函数调用中的参数和返回值;
- PortType元素定义了Web服务的抽象接口,它可以由一个或者多个operation元素,每个operation元素定义了一个RPC样式或者文档样式的Web服务方法;
- Operation元素要用一个或者多个messages消息来定义它的输入、输出以及错误;
- Binding元素将一个抽象的portType映射到一组具体的协议(SOAP或者HTTP)、消息传递样式(RPC或者document)以及编码样式(literal或者SOAP encoding);
- Service元素包含一个或者多个Port元素
每一个Port元素对应一个不同的Web服务,port将一个URL赋予一个特定的binding,通过location实现。可以使两个或者多个port元素将不同的URL赋给相同的binding。
(3)UDDI
Web 服务提供商又如何将自己开发的 Web 服务公布到因特网上,这就需要使用到 UDDI 了,UDDI的话,是一个跨产业,跨平台的开放性架构,可以帮助 Web 服务提供商在互联网上发布 Web 服务的信息。
UDDI 是一种目录服务,企业可以通过 UDDI 来注册和搜索 Web 服务。
简单来说,UDDI 就是一个目录,只不过在这个目录中存放的是一些关于 Web 服务的信息而已。
并且 UDDI 通过SOAP 进行通讯,构建于 . Net 之上。
UDDI 即 Universal Description,Discovery andIntegration,也就是通用的描述,发现以及整合。
UDDI 的目的是为电子商务建立标准;UDDI是一套基于Web的、分布式的、为WebService提供的、信息注册中心的实现标准规范,同时也包含一组使企业能将自身提供的Web Service注册,以使别的企业能够发现的访问协议的实现标准。
4、应用场景
Web Service 可以适用于应用程序集成、软件重用、跨防火墙通信等需求。不同的业务要求不同。具体如下:
(1) 跨防火墙通信
(2) 应用系统集成
(3) 软件和数据重用
简单来说,如果一个功能,需要被多个系统使用可以使用webservice开发一个服务端接口,供不同的客户端应用。主要应用在企业内部系统之间的接口调用、面向公网的webservice服务。
5、优缺点
优点:
a) 异构平台的互通性
理论上, Web Service 最大的优势是提供了异构平台的无缝街接技术手段。由于不同的用户使用不同的硬件平台,不同的操作平台,不同的操作系统,不同的软件,不同的协议通信,这就产生了互相通信的需求。 Web Service 使任何两个应用程序,只要能读写XML,那么就能互相通信。
b) 更广泛的软件复用(例如手机淘宝可以复用已有淘宝的业务逻辑.)
软件的复用技术通过组合已有模块来搭建应用程序,能大幅度提高软件的生产效率和质量。用户只要获得了描述 Web Service 的 WSDL 文件,就可以方便地生成客户端代理,并通过代理访问 Web Service 。
c) 成本低、可读性强、应用范围广
Web Service 可用基于 XML 的 SOAP 来表示数据和调用请求。并且通过 HTTP 协议传输 XML 格式的数据
d) 迅捷的软件发行方式
(每个web Service称为一个生产者.不同的生产者可以相互协同合作完成整个应用)
Web Service 将彻底地改变软件的发行方式。
软件供应商可以把软件分解成若Web Service 模块构成的系统,直接在 Web 上发布。
e) 最重要的一点
客户端与服务端可能是用不同的语言开发的,但是,通过webservice提供服务接口,客户端与服务端之前可以传递对象。
缺点:
由于soap是基于xml传输,本身使用xml传输会传输一些无关内容从而影响效率,随着soap协议的完善,soap协议增加了许多内容,这样就导致了使用soap去完成简单的数据传输而携带的信息更多效率再受影响;
Web Service作为web跨平台访问的标准技术,很多公司都限定要求使用Web Service,但如果是简单的接口可以直接使用http传输自定义数据格式,开发更快捷。
6、面向服务架构SOA
SOA(Service-OrientedArchitecture)
面向服务架构是一种思想,它将应用程序的不同功能单元通过中立的契约(独立于硬件平台、操作系统和编程语言)联系起来,使得各种形式的功能单元更好的集成。目前来说,WebService 是SOA 的一种较好的实现方WebService 采用HTTP 作为传输协议,SOAP(Simple Object Access Protocol)作为传输消息的格式。但WebService 并不是完全符合SOA 的概念,因为SOAP 协议是WebService 的特有协议,并未符合SOA 的传输协议透明化的要求。SOAP 是一种应用协议,早期应用于RPC 的实现,传输协议可以依赖于HTTP、SMTP 等。
二、ApacheCXF 和 Axis
三、ApacheCXF 框架
1、原生 java (实现 Jax-ws)
(1)服务端
依赖
<!--要进行jaxws 服务开发-->
<dependency>
<groupId>org.apache.cxf</groupId>
<artifactId>cxf-rt-frontend-jaxws</artifactId>
<version>3.5.5</version>
</dependency>
<!-- 内置jetty web服务器 -->
<dependency>
<groupId>org.apache.cxf</groupId>
<artifactId>cxf-rt-transports-http-jetty</artifactId>
<version>3.5.5</version>
</dependency>
注意:如果使用 4.0.0 则需要 jdk11
接口
import javax.jws.WebService;
@WebService
public interface TestService {
public String sayHello(String name);
}
import com.rewind.code.webservice.TestService;
import org.apache.cxf.jaxws.JaxWsServerFactoryBean;
public class TestServiceImpl implements TestService {
@Override
public String sayHello(String name) {
return "hello" + name;
}
}
发布服务
public static void main(String[] args) {
// 服务工厂
JaxWsServerFactoryBean factory = new JaxWsServerFactoryBean();
// 设置服务地址
factory.setAddress("http://localhost:8000/test");
// 设置服务类
factory.setServiceBean(new TestServiceImpl());
// 发布服务
factory.create();
// 提示
System.out.println("服务端启动");
}
查看 wsdl
浏览器访问 http://localhost:8000/test?wsdl
<wsdl:definitions xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/" xmlns:tns="http://impl.webservice.code.rewind.com/" xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/" xmlns:ns2="http://schemas.xmlsoap.org/soap/http" xmlns:ns1="http://webservice.code.rewind.com/" name="TestServiceImplService" targetNamespace="http://impl.webservice.code.rewind.com/">
<wsdl:import location="http://localhost:8000/test?wsdl=TestService.wsdl" namespace="http://webservice.code.rewind.com/"> </wsdl:import>
<wsdl:binding name="TestServiceImplServiceSoapBinding" type="ns1:TestService">
<soap:binding style="document" transport="http://schemas.xmlsoap.org/soap/http"/>
<wsdl:operation name="sayHello">
<soap:operation soapAction="" style="document"/>
<wsdl:input name="sayHello">
<soap:body use="literal"/>
</wsdl:input>
<wsdl:output name="sayHelloResponse">
<soap:body use="literal"/>
</wsdl:output>
</wsdl:operation>
</wsdl:binding>
<wsdl:service name="TestServiceImplService">
<wsdl:port binding="tns:TestServiceImplServiceSoapBinding" name="TestServiceImplPort">
<soap:address location="http://localhost:8000/test"/>
</wsdl:port>
</wsdl:service>
</wsdl:definitions>
(2)客户端
写客户端之前要先明确:
服务端地址
服务端接口、接口方法(方法参数、返回值)
依赖同服务端
接口
必须与服务端的包路径完全一致
import javax.jws.WebService;
@WebService
public interface TestService {
public String sayHello(String name);
}
调用
public static void main(String[] args) {
JaxWsProxyFactoryBean factoryBean = new JaxWsProxyFactoryBean();
factoryBean.setAddress("http://localhost:8000/test");
factoryBean.setServiceClass(TestService.class);
TestService testService = factoryBean.create(TestService.class);
String s = testService.sayHello("rewind");
System.out.println(s);
}
2、Springboot整合cxf(jax-ws)
(1)服务端
依赖
<!-- cxf webservice -->
<dependency>
<groupId>org.apache.cxf</groupId>
<artifactId>cxf-spring-boot-starter-jaxws</artifactId>
<version>3.4.3</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.apache.neethi/neethi -->
<dependency>
<groupId>org.apache.neethi</groupId>
<artifactId>neethi</artifactId>
<version>3.0.2</version>
</dependency>
<!-- https://mvnrepository.com/artifact/wsdl4j/wsdl4j -->
<dependency>
<groupId>wsdl4j</groupId>
<artifactId>wsdl4j</artifactId>
<version>1.6.2</version>
</dependency>
<!--JDK11需要添加此依赖-->
<!--<dependency>
<groupId>com.sun.xml.ws</groupId>
<artifactId>jaxws-ri</artifactId>
<version>2.3.1</version>
</dependency>-->
配置
server:
port: 8080
servlet:
context-path: /tms #项目默认地址前缀
cxf:
# 访问路径前缀,默认/services
path: /cxf
接口
每个独立 webservice服务的 targetNamespace 命名空间的名字不能相同。
import show.rewind.code3.entity.Role;
import show.rewind.code3.entity.User;
import javax.jws.WebMethod;
import javax.jws.WebParam;
import javax.jws.WebService;
import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;
import java.util.List;
import java.util.Map;
@WebService(
name = CxfConfig.TMS_SERVICE_NAME, // 对外发布的服务名
targetNamespace = "code3.rewind.show" // 该接口全限定路径
)
public interface UserService {
@WebMethod(operationName = "say")
public String say(@WebParam(name = "str") String str);
@WebMethod(operationName = "getRoleByUser")
public List<Role> getRoleByUser(@WebParam(name = "user") User user);
@XmlJavaTypeAdapter(MapAdapter.class)
@WebMethod(operationName = "getRoles")
public Map<String,List<Role>> getRoles();
}
实现类
import org.springframework.stereotype.Component;
import show.rewind.code3.entity.Role;
import show.rewind.code3.entity.User;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@Component
public class UserServiceImpl implements UserService{
@Override
public String say(String str) {
return "hello";
}
@Override
public List<Role> getRoleByUser(User user) {
List<Role> roleList=new ArrayList<Role>();
roleList.add(new Role(1, "AAAA"));
roleList.add(new Role(2, "BBBB"));
return roleList;
}
@Override
public Map<String, List<Role>> getRoles() {
Map<String,List<Role>> map=new HashMap<String,List<Role>>();
List<Role> roleList1=new ArrayList<Role>();
roleList1.add(new Role(1,"CCCC"));
map.put("cccc", roleList1);
List<Role> roleList2=new ArrayList<Role>();
roleList2.add(new Role(2,"DDDD"));
map.put("dddd", roleList2);
return map;
}
}
参数转化适配器
import javax.xml.bind.annotation.adapters.XmlAdapter;
public class MapAdapter extends XmlAdapter<MyRole[], Map<String,List<Role>>>{
/**
* 适配转换 MyRole[] -> Map<String, List<Role>>
*/
@Override
public Map<String, List<Role>> unmarshal(MyRole[] myRoles) throws Exception {
Map<String, List<Role>> map=new HashMap<String,List<Role>>();
for(int i=0;i<myRoles.length;i++){
MyRole myRole=myRoles[i];
map.put(myRole.getKey(), myRole.getValue());
}
return map;
}
/**
* 适配转换 Map<String, List<Role>> -> MyRole[]
*/
@Override
public MyRole[] marshal(Map<String, List<Role>> map) throws Exception {
MyRole[] roles=new MyRole[map.size()];
Set<Map.Entry<String, List<Role>>> entries = map.entrySet();
Integer index = 0;
for (Map.Entry<String, List<Role>> entry : entries) {
roles[index]=new MyRole();
roles[index].setKey(entry.getKey());
roles[index].setValue(entry.getValue());
index++;
}
return roles;
}
}
配置类
import org.apache.cxf.Bus;
import org.apache.cxf.bus.spring.SpringBus;
import org.apache.cxf.jaxws.EndpointImpl;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import show.rewind.code3.webservice.UserService;
import javax.xml.ws.Endpoint;
/**
* 发布服务
*/
@Configuration
public class CxfConfig {
public static final String USER_SERVICE_NAME = "userWebService";
@Autowired
private UserService userService;
/**
* 业务总线
*/
@Bean(name = Bus.DEFAULT_BUS_ID)
public SpringBus springBus() {
return new SpringBus();
}
/**
* 发布服务并指定访问URL
* @return
*/
@Bean
public Endpoint endpoint() {
EndpointImpl endpoint = new EndpointImpl(springBus(), userService);
endpoint.publish("/" + USER_SERVICE_NAME); // "/服务名"
// 添加自定义拦截器
endpoint.getInInterceptors().add(new MyInterceptor());
// 修改生成的 wsdl 的 soap:address location 地址
// 该地址是 wsdl 请求接口服务器的地址,默认cxf自动生成,为真实地址
// 如果使用 nginx 代理接口,或其他负载均衡网关的话,可能需要用到
// 参数为外部请求的网关地址
endpoint.setPublishedEndpointUrl("http://yourdomain.com/yourServicePath");
return endpoint;
}
/**
* 发布多个,只需在加一个 Endpoint 即可
*/
//@Bean
//@Qualifier(“roleEndpoint”)
//public Endpoint roleEndpoint() {
// Endpoint endpoint = new EndpointImpl(springBus(), roleService);
// //endpoint.getInInterceptors().add(new MyInterceptor()); // 添加自定义拦截器
// endpoint.publish("/" + ROLE_SERVICE_NAME); // "/服务名"
// return endpoint;
//}
}
wsdl访问路径
http://localhost:8080/tms/cxf/TMSWebService?wsdl
http://localhost:8080/
:服务地址端口/tms
:Spring MVC 前缀路径/cxf
:webService 前缀路径/TMSWebService
:对外开放服务名?wsdl
:固定
四、IDEA生成客户端代码
https://blog.csdn.net/tongxin_tongmeng/article/details/126523995
1、添加 webservice
默认引擎是这个
2、生成webservice代码
删除掉.class文件即可,copy到自己的项目目录下
3、调用代码
UserServiceImplService userServiceImplService = new UserServiceImplService();
UserService userService = userServiceImplService.getUserPort();
try {
MyRoleArray roles = userService.getRoles();
System.out.println(roles.getItem());
} catch (Exception e) {
e.printStackTrace();
}