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: <soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"><soap:Body><ns2:getWeatherByCityName xmlns:ns2="http://example.org/">
<arg0>上海</arg0></ns2:getWeatherByCityName></soap:Body></soap:Envelope>
--------------------------------------
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: <soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"><soap:Body><ns2:getWeatherByCityNameResponse xmlns:ns2="http://example.org/"><return>上海天气还可以!</return></ns2:getWeatherByCityNameResponse></soap:Body></soap:Envelope>
--------------------------------------

请求报文中的参数名称(包括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<User> 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<? extends User> 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库发布服务。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 215,012评论 6 497
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,628评论 3 389
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 160,653评论 0 350
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,485评论 1 288
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,574评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,590评论 1 293
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,596评论 3 414
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,340评论 0 270
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,794评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,102评论 2 330
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,276评论 1 344
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,940评论 5 339
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,583评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,201评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,441评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,173评论 2 366
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,136评论 2 352

推荐阅读更多精彩内容