webservice


一、基本概念

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)客户端

写客户端之前要先明确:

  1. 服务端地址

  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

image-20230104141822998

默认引擎是这个

image-20230104143338518

2、生成webservice代码

image-20230104141900468

image-20230104141928918

删除掉.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();
}

  目录