java 使用WebService实现接口的发布和访问(附python客户端调用方式)

最近公司业务需要对接第三方的接口,这些接口用的是webService基于soap+xml发布的,这技术对于以前写业务接口的前辈来说应该是滚瓜烂熟的了。什么?你居然不熟悉在pojo和xml之间互相转换?玛莎拉蒂不会开??那你无了。在那个年代webservice算是炙手可热的一门技术了,所以还有不少的老项目是基于webservice的,但是对于新生代的业务人员来说,这样的技术如果无人提及,可能咱也不会想着去学习,即便后面webservice可以实现restful风格的接口,但现在springboot等这样的框架是完全着力于restful规范的,非特殊业务不会用到webservice去开发。咱学习积极性高,权当了解,才不是为了业务需求。言归正传,一起来熟悉一下吧

概要

'''
Web Service所使用的是Internet上统一、开放的标准,如HTTP、XML、SOAP(简单对象访问协议)、WSDL等,所以Web Service可以在任何支持这些标准的环境(Windows,Linux)中使用。
这有助于大量异构程序和平台之间的互操作性,从而使存在的应用程序能够被广泛的用户访问。

从应用来说,Web Service是一件值得企业特别注意的事情。Web Service的应用范围目前包括两个方面:企业之间的应用、以及企业内部的应用。

'''
WebService即web服务,它是一种跨编程语言和跨操作系统平台的远程调用技术
JAVA 中共有三种WebService 规范,分别是JAX-WS(JAX-RPC)、JAXM&SAAJ、JAX-RS。
webService三要素:soap、wsdl、uddi

soap

即简单对象访问协议(Simple Object Access Protocol),它是用于交换XML(标准通用标记语言下的一个子集)编码信息的轻量级协议,由Envelope,Headers,Body组成

wsdl

Web Service描述语言WSDL(SebService Definition Language)就是用机器能阅读的方式提供的一个正式描述文档而基于XML(标准通用标记语言下的一个子集)的语言,用于描述Web Service及其函数、参数和返回值。因为是基于XML的,所以WSDL既是机器可阅读的,又是人可阅读的。

uddi

用于服务注册的,wsdl+uddi实现服务发现

如果一个功能,需要被多个系统使用可以使用webservice开发一个服务端接口,供不同的客户端应用。主要应用在企业内部系统之间的接口调用、面向公网的webservice服务。
咱只要弄清楚接口、契约、实现即可。
-定义接口并抽象出需要用到的功能方法
-创建实现类去实现该接口,并给出相应的业务处理逻辑
下面给出一个供参考的webservice的服务端以及客户端

服务端

首先定义业务接口

@WebService //标记该接口为webService接口
public interface WeatherServiceInterface {
    String getWeatherByCityName(String cityName);
}

实现该接口

public class WeatherServiceImpl implements WeatherServiceInterface {
    @Override
    public String getWeatherByCityName(String cityName) {
        return cityName + "天气还可以!";
    }
}

发布服务,需给定发布的地址 ip+port
1.使用javax扩展包下的Endpoint.publish发布服务

Endpoint.publish("http://localhost:8080/ws/weather", new WeatherServiceImpl());
System.out.println("Weather webservice  was published!");

2.使用Apache cxf发布服务

public class App {
    public static void main(String[] args) {
        JaxWsServerFactoryBean factoryBean = new JaxWsServerFactoryBean();

        factoryBean.setAddress("http://localhost:8080/ws/weather");

        factoryBean.setServiceBean(new WeatherServiceImpl());
        // 客户端请求报文拦截器
        factoryBean.getInInterceptors().add(new LoggingInInterceptor());
        // 客户端响应报文拦截器
        factoryBean.getOutInterceptors().add(new LoggingOutInterceptor());

        Server server = factoryBean.create();
        server.start();

        System.out.println("Weather webservice   was published!");
    }
}

这里为了查看客户端和服务端之间的沟通详细过程,做了日志拦截打印,可以查看到请求头和请求体以及响应的相关内容
发布成功后可以通过发布的地址后面加上 ?wsdl (说明书)查看服务接口业务方法的调用方式(细心看的同学肯定能找到接口名,接口方法,方法对应的参数及类型等,这里就不贴出来了)

客户端

idea中新建module
客户端需要把之前的WeatherServiceInterface拷贝过来,再利用CXF这个库拿到这个接口代理实现类然后就可以调用接口中的方法了(具体如何实现有兴趣可以去看看cxf这个库的源码)。

import static org.junit.Assert.assertTrue;

import org.apache.cxf.jaxws.JaxWsProxyFactoryBean;
import org.junit.Test;

/**
 * Unit test for simple App.
 */
public class AppTest {
    /**
     * Rigorous Test :-)
     */
    @Test
    public void shouldAnswerWithTrue() throws Exception {
        JaxWsProxyFactoryBean jaxWsProxyFactoryBean = new JaxWsProxyFactoryBean();
        jaxWsProxyFactoryBean.setAddress("http://localhost:8080/ws/weather");
        jaxWsProxyFactoryBean.setServiceClass(WeatherService.class);
        WeatherService weatherServiceProxy = (WeatherService)       
        jaxWsProxyFactoryBean.create();
        String weather = weatherServiceProxy.getWeatherByCityName("上海");
        System.out.println(weather);
    }
}

运行后,查看服务端的控制台打印信息:

2020-12-20 16:50:06,369 544309 [ qtp20853837-18] INFO  plPort.WeatherServiceInterface  - Inbound Message
----------------------------
ID: 3
Address: http://localhost:8080/ws/weather
Encoding: UTF-8
Http-Method: POST
Content-Type: text/xml; charset=UTF-8
Headers: {Accept=[*/*], Cache-Control=[no-cache], connection=[keep-alive], Content-Length=[213], content-type=[text/xml; charset=UTF-8], Host=[localhost:8080], Pragma=[no-cache], SOAPAction=[""], User-Agent=[Apache CXF 3.0.1]}
Payload: 
上海
--------------------------------------
2020-12-20 16:50:06,373 544313 [ qtp20853837-18] INFO  plPort.WeatherServiceInterface  - Outbound Message
---------------------------
ID: 3
Response-Code: 200
Encoding: UTF-8
Content-Type: text/xml
Headers: {}
Payload: 上海天气还可以!
--------------------------------------

请求报文中的参数名称(包括wsdl里面每个方法的参数名称),这里显示的是arg0,是可以通过接口参数注解@webParams("paramName")来指定的

在真正的业务中一个接口类里面可能会有很多业务方法,并且在没有服务端接口类的情况下只看wsdl说明书,客户端做相关业务处理效率太低了。

apache cxf 可以根据wsdl说明自动生成所有的业务类,使用wsdl2java命令即可生成一个业务包
(cxf下载地址http://cxf.apache.org/download.html)

./wsdl2java -client http://localhost:8080/ws/weather?wsdl

windows下可以通过wsdl2java.bat命令生成
这里就不贴出来了

webService rest风格的例子:

server端

接口类

import org.example.pojo.User;

import java.util.List;

import javax.ws.rs.*;


@Path("/userService")
@Produces("*/*")
public interface IUserService {

    @POST
    @Path("/user")
    @Consumes({"application/xml", "application/json"})
    public void saveUser(@HeaderParam("auth") String auth, User user);

    @PUT
    @Path("/user")
    @Consumes({"application/xml", "application/json"})
    public void updateUser(User user);

    @GET
    @Path("/user")
    @Produces({"application/xml", "application/json"})
    public List findAllUsers(@HeaderParam("auth") String auth);

    @GET
    @Path("/user/{id}")
    @Consumes("application/xml") // 服务器支持请求的数据类型
    @Produces({"application/xml", "application/json"}) //服务器支持的返回数据格式类型
    public User finUserById(@PathParam("id") Integer id);

    @DELETE
    @Path("/user/{id}")
    @Consumes({"application/xml", "application/json"})
    public void deleteUser(@PathParam("id") Integer id);
}

其中@Consumer指定请求的数据格式上面有支持xml和json的,@Produces返回数据格式(xml/json)
具体实现我这里只是一些简单的处理和打印就不贴了。
发布服务 使用cxf中的工厂类


import org.apache.cxf.jaxrs.JAXRSServerFactoryBean;
import org.example.service.UserServiceImpl;

/**
 * Create by zengqi on 2020/12/19
 **/
public class Server {
    public static void main(String[] args) {
        JAXRSServerFactoryBean jaxrsServerFactoryBean = new JAXRSServerFactoryBean();
        jaxrsServerFactoryBean.setAddress("http://localhost:8888/server/rest");
        jaxrsServerFactoryBean.setServiceClass(UserServiceImpl.class);
        jaxrsServerFactoryBean.create();
        System.out.println("Server  was published");
    }
}

如果你的实现类正常返回了数据,这时候访问http://localhost:8888/server/rest/userService/user可以拿到json/xml数据了
接口地址拼接方式为:发布地址+接口Path+方法Path
这才是我们熟知的方式嘛。。。

客户端调用:

可以使用cxf里面的Client去请求,也可以使用网络框架去请求,如HttpClient,okhttp,requests等,不赘述
使用cxf Client请求:

public class AppTest {
    /**
     * Rigorous Test :-)
     */
    @Test
    public void shouldAnswerWithTrue() {
        Collection collection = WebClient.create("http://localhost:8888/server/rest/userService/user")
                .accept(MediaType.APPLICATION_JSON)
                .getCollection(User.class);
        System.out.println(collection);
    }
}

MediaType可以指定返回格式为json/xml
到此,算是对webService有一个基本了解了!

这里记录一下python调用webService的方法:
restful风格的对python来说可以通过一般的网络请求库直接调用,基于soap+xml的webService接口需要用到suds-jurko/zeep库,下面简单记录一下调用方式

suds:

from suds.client import Client
from suds.xsd.doctor import ImportDoctor, Import

imp = Import('http://www.w3.org/2001/XMLSchema',
             location='http://www.w3.org/2001/XMLSchema.xsd')
imp.filter.add('http://WebXml.com.cn/')
doctor = ImportDoctor(imp)

imp = Import('http://www.w3.org/2001/XMLSchema',
             location='http://www.w3.org/2001/XMLSchema.xsd')
imp.filter.add('http://WebXml.com.cn/')
doctor = ImportDoctor(imp)
url = "http://localhost:8080/ws/weather?wsdl"
client = Client(url, doctor=doctor)
result = client.service.getWeatherbyCityName(u'广州')
print(result)

zeep实现方式类似,但是这个库据说比suds要更新(推荐)。

from zeep import Client

url = "http://localhost:8080/ws/weather?wsdl"
client = Client(url)
result = client.service.getWeatherbyCityName(u'广州')
print(result)

值得注意的地方python调用的时候url后面需要跟上?wsdl
python也是能做webService接口的,定义接口,约束,以及实现,便可以通过web库发布服务。

你可能感兴趣的:(java 使用WebService实现接口的发布和访问(附python客户端调用方式))