WebService最优方案选择

news2024/12/24 8:25:28

需求

最近,接触到了一个java对接C#的项目,使用WebService技术开发。项目已经快告一段落了,经过这几个月接触和使用。我有了一个清晰的认识,之前也调研了互联网上大部分实现的通讯,他们的优缺点,我都有一定的了解,我认为的最好解决方案是 wsimport生成的代码,让我们看看这几种方式的优缺点吧。

WebService是什么

WebService是一种跨编程语言和跨操作系统平台的远程调用技术。

WebService使用场景

WebService可用于调用第三方系统API。

WebService创建方式

引入依赖

    <dependencies>
        <dependency>
            <groupId>org.apache.cxf</groupId>
            <artifactId>cxf-spring-boot-starter-jaxws</artifactId>
            <version>3.5.3</version>
<!--            <exclusions>-->
<!--                <exclusion>-->
<!--                    <groupId>org.apache.cxf</groupId>-->
<!--                    <artifactId>cxf-rt-frontend-jaxws</artifactId>-->
<!--                </exclusion>-->
<!--            </exclusions>-->
        </dependency>
        <dependency>
            <groupId>org.apache.cxf</groupId>
            <artifactId>cxf-rt-transports-http</artifactId>
            <version>3.5.3</version>
        </dependency>

        <dependency>
            <groupId>org.apache.cxf</groupId>
            <artifactId>cxf-rt-transports-http-jetty</artifactId>
            <version>3.5.3</version>
        </dependency>


    </dependencies>

创建bean

package com.hikktn;
 
import javax.jws.WebMethod;
import javax.jws.WebService;
 
@WebService
public interface IUserService {
 
    @WebMethod
    //返回一个字符串
    String getStr(String str);
}

实现接口

@WebService
public class UserServiceImpl implements IUserService{
 
    @WebMethod
    @Override
    public String getStr(String str) {
        String str2 = null;
        try {
            str2 = new String("处理后的字符串:".getBytes(),"UTF-8");
        } catch (UnsupportedEncodingException e) {
            throw new RuntimeException(e);
        }
        return str2 + str;
    }
}

第一种服务端

import org.apache.cxf.interceptor.Interceptor;
import org.apache.cxf.interceptor.LoggingInInterceptor;
import org.apache.cxf.jaxws.EndpointImpl;
import org.apache.cxf.message.Message;

import javax.xml.ws.Endpoint;
import java.util.List;

public class TestServer {

    private static String PATH = "http://127.0.0.1:7777/WebService_Server/jdk";

    //浏览器输入以上地址访问不到,但是http://127.0.0.1:7777/WebService_Server/jdk?wsdl这个地址可以查看到xml文件
    //相对路径随便写
    public static void main(String[] args) {
        //获取一个EndPoint实例
        EndpointImpl endpoint = (EndpointImpl) Endpoint.publish(PATH, new UserServiceImpl());
        //服务器端获取输入拦截器
        List<Interceptor<? extends Message>> inInterceptors = endpoint.getInInterceptors();
        //添加接受信息日志拦截器
        inInterceptors.add(new LoggingInInterceptor());
        System.out.println("成功发布!");
    }
}

第二种服务端

import org.apache.cxf.interceptor.LoggingInInterceptor;
import org.apache.cxf.jaxws.JaxWsServerFactoryBean;

/**
 * @author killian
 * @since 2022-11-04 14:54
 */
public class TestServer2 {

    public static void main(String[] args) {

        //以下是编译wsdl文件的另一种方法,这种方法使用CXF的JaxWsServerFactoryBean类中的方法创建
        System.out.println("启动服务器...");
        //创建地址
        String address = "http://127.0.0.1:7777/WebService_Server/cxf";
        //创建一个服务器工厂实例
        JaxWsServerFactoryBean jaxWsServerFactoryBean = new JaxWsServerFactoryBean();
        //设置暴露地址
        jaxWsServerFactoryBean.setAddress(address);
        //设置接口类
        jaxWsServerFactoryBean.setServiceClass(IUserService.class);
        //设置实现类
        jaxWsServerFactoryBean.setServiceBean(uSerService);
        //添加日志拦截器
        jaxWsServerFactoryBean.getInInterceptors().add(new LoggingInInterceptor());
        //创建服务器
        jaxWsServerFactoryBean.create();
        System.out.println("服务器成功启动!");
    }

}

启动服务后,下载Soap软件测试
在这里插入图片描述

放入url
http://127.0.0.1:7777/WebService_Server/jdk?wsdl
就可以测试
在这里插入图片描述

客户端调用(不建议)

public class TestXml {

    public static void main(String[] args) {
        try {
            StringWriter writer = new StringWriter();
            JAXBContext jc = JAXBContext.newInstance(Student.class);
            Marshaller ms = jc.createMarshaller();
            Student st = new Student("zhang", "w", "h", 11);
            ms.marshal(st, writer);
            String xmlStr = writer.toString();
            System.out.println(xmlStr);
        } catch (JAXBException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }
}

这种在网络上都是入门级别,玩玩demo可以。但是你要拿到项目中,就恼火了。
首先,这样的调用方式是完全不管命名空间的调用,而国外的超多API都是有很多嵌套的命名空间xml。
可以说这个是非常致命的缺点,无法生成有效的XML。

hutool调用WebService(简单场景)

具体大家看这篇博客,它有非常详细的教程。
Java使用Hutool调用WebService接口详解
优点:不用写HttpURLConnection通讯,简单的接口可以使用这个来开发。
缺点:代码繁多,没有更加优化的工具类,要去处理返回的XML,处理方式只有转换为Map,非常不利于后续维护和代码编写。

HttpURLConnection原生请求WebService(简单场景)

public class SoapSendUtil {

    /**
     * 默认连接超时
     */
    private static final int connectionTimeout = 60 * 1000;
    /**
     * 默认读取超时
     */
    private static final int readTimeout = 60 * 1000;

    /**
     * 发送请求
     *
     * @param SapPiUrl soap协议url
     * @param xmlStr   soap协议的xml
     * @param action   请求接口url
     * @return 响应报文
     * @throws IOException IO异常
     */
    public static String sendRequest(String SapPiUrl, String xmlStr, String action) throws IOException {
        //创建一个支持HTTP特定功能的URLConnection
        HttpURLConnection httpConn = null;
        //创建一个输出流对象
        OutputStream out = null;
        String returnXml = "";
        //打开链接
        httpConn = (HttpURLConnection) new URL(SapPiUrl).openConnection();
        //设置内容和字符格式
        httpConn.setRequestProperty("Content-Type", "text/xml; charset=utf-8");
        //设置请求和xml
        httpConn.setRequestProperty("SOAPAction", action);
        //设置请求为post亲求
        httpConn.setRequestMethod("POST");
        httpConn.setDoOutput(true);
        httpConn.setDoInput(true);
        httpConn.setConnectTimeout(connectionTimeout);
        httpConn.setReadTimeout(readTimeout);

        httpConn.connect();
        out = httpConn.getOutputStream(); // 获取输出流对象

        httpConn.getOutputStream().write(xmlStr.getBytes()); // 将要提交服务器的SOAP请求字符流写入输出流
        out.flush();
        out.close();
        int code = httpConn.getResponseCode(); // 用来获取服务器响应状态
        System.out.println(code);
        String tempString = null;
        StringBuffer sb1 = new StringBuffer();
        //如果响应状态为ok,就读取http内容
        if (code == HttpURLConnection.HTTP_OK) {
            BufferedReader reader = new BufferedReader(new InputStreamReader(httpConn.getInputStream(), StandardCharsets.UTF_8));
            while ((tempString = reader.readLine()) != null) {
                sb1.append(tempString);
            }
            if (null != reader) {
                reader.close();
            }
        } else {
            BufferedReader reader = new BufferedReader(new InputStreamReader(httpConn.getErrorStream(), StandardCharsets.UTF_8));
            // 一次读入一行,直到读入null为文件结束
            while ((tempString = reader.readLine()) != null) {
                sb1.append(tempString);
            }
            if (null != reader) {
                reader.close();
            }
        }
        // 响应报文
        returnXml = sb1.toString();
        return returnXml;
    }
}
import org.springframework.util.StringUtils;

import javax.xml.bind.*;
import javax.xml.bind.annotation.XmlAnyElement;
import javax.xml.namespace.QName;
import java.io.File;
import java.io.InputStream;
import java.io.StringReader;
import java.io.StringWriter;
import java.nio.charset.StandardCharsets;
import java.util.Collection;

/**
 * 使用Jaxb2.0实现XMLJava Object的Binder.
 * <p>
 * 特别支持Root对象是List的情形.
 */
public class JaxbBinderUtil {

    /**
     * 多线程安全的Context.
     */
    private JAXBContext jaxbContext;

    /**
     * @param types 所有需要序列化的Root对象的类型.
     */
    public JaxbBinderUtil(Class... types) {
        try {
            jaxbContext = JAXBContext.newInstance(types);
        } catch (JAXBException e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * Java Object->Xml.
     */
    public String toXml(Object root, String encoding) {
        try {
            StringWriter writer = new StringWriter();
            createMarshaller(encoding).marshal(root, writer);
            return writer.toString();
        } catch (JAXBException e) {
            throw new RuntimeException(e);
        }
    }

    public String toXml(Object root) {
        try {
            StringWriter writer = new StringWriter();
            createMarshaller(StandardCharsets.UTF_8.toString()).marshal(root, writer);
            return writer.toString();
        } catch (JAXBException e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * Java Object->Xml, 特别支持对Root Element是Collection的情形.
     */
    public String toXml(Collection root, String rootName, String encoding) {
        try {
            CollectionWrapper wrapper = new CollectionWrapper();
            wrapper.collection = root;
            JAXBElement wrapperElement = new JAXBElement(new QName(rootName),
                    CollectionWrapper.class, wrapper);
            StringWriter writer = new StringWriter();
            createMarshaller(encoding).marshal(wrapperElement, writer);
            return writer.toString();
        } catch (JAXBException e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * Xml->Java Object.
     */
    public Object fromXml(String xml) {
        try {
            StringReader reader = new StringReader(xml);
            return createUnmarshaller().unmarshal(reader);
        } catch (JAXBException e) {
            throw new RuntimeException(e);

        }
    }

    /**
     * 创建Marshaller, 设定encoding(可为Null).
     */
    public Marshaller createMarshaller(String encoding) {
        try {
            Marshaller marshaller = jaxbContext.createMarshaller();
            // 去除XML头
            marshaller.setProperty("com.sun.xml.bind.xmlDeclaration", Boolean.FALSE);
            // 指定是否使用换行和缩排对已编组 XML 数据进行格式化的属性名称
            marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE);

            if (StringUtils.isEmpty(encoding)) {
                marshaller.setProperty(Marshaller.JAXB_ENCODING, encoding);
            }
            return marshaller;
        } catch (JAXBException e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * 创建UnMarshaller.
     */
    public Unmarshaller createUnmarshaller() {
        try {
            return jaxbContext.createUnmarshaller();
        } catch (JAXBException e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * 封装Root Element 是 Collection的情况.
     */

    public static class CollectionWrapper {

        @XmlAnyElement
        protected Collection collection;

    }

    public Object fromXML(String fileName) {
        return fromXML(new File(fileName));
    }

    public Object fromXML(File file) {
        try {
            Unmarshaller unmarshaller = createUnmarshaller();
            return unmarshaller.unmarshal(file);
        } catch (JAXBException e) {
            throw new RuntimeException(e);
        }
    }

    public Object fromXML(InputStream stream) {
        try {
            Unmarshaller unmarshaller = createUnmarshaller();
            return unmarshaller.unmarshal(stream);
        } catch (JAXBException e) {
            throw new RuntimeException(e);
        }
    }
}

这两个工具类也是由大佬提供,他们需要结合起来编写。
具体使用
对象转 soap_使用jaxb 实现对象与xml之间的转换
优点:解决了上面的返回对象只能转Map的问题
缺点:需要为每个接口的入参和结果集,封装一层SoapBody和SoapHeader,并且要在package-info.java编写命名空间。java版本低一点就没有package-info.java了。

什么事命名空间?

<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:ser="http://server.hikktn.com/">
   <soapenv:Header/>
   <soapenv:Body>
      <ser:getArticle>
         <!--Optional:-->
         <arg0>
            <!--Optional:-->
            <guid>123</guid>
            <!--Optional:-->
            <name>true</name>
         </arg0>
      </ser:getArticle>
   </soapenv:Body>
</soapenv:Envelope>

xmlns后面跟着就是命令空间,它会为下面的xml标签加上一个小前缀。
package-info.java

@XmlSchema(xmlns = {
        @XmlNs(namespaceURI = "http://server.hikktn.com/", prefix = "ser"),
        @XmlNs(namespaceURI = "http://schemas.xmlsoap.org/soap/envelope/", prefix = "soapenv")
})
package com.eci.tbms.memoq.domain.vo;

import javax.xml.bind.annotation.XmlNs;
import javax.xml.bind.annotation.XmlSchema;

cxf方式

首先,我们需要去下载Apache CXF
CXF官方下载地址
然后解压,cmd打开对应路径。
该方案适用于json格式发送请求。
可以生成去除该字段类型的 客户端代码 ; 比如 JAXBElement 可以变成 String 类型
XML
复制代码

<jaxb:bindings version="2.0"    
               xmlns:jaxb="http://java.sun.com/xml/ns/jaxb">    
    <jaxb:bindings>    
        <jaxb:globalBindings generateElementProperty="false"/>    
    </jaxb:bindings>    
</jaxb:bindings>  

第二步 : 生成客户端代码
LaTeX
复制代码

wsimport -encoding utf-8 -b remove.xml -Xnocompile http://xxxxxxxx?WSDL

-encoding utf-8 表示 编码
-Xnocompile 表示生成java代码, 不加的话, 将会 只生成class 文件
-b 表示绑定指定文件
此时生成的客户端代码中 , 原先的JAXBElement 字段类型 , 就会变成 String 类型;
controller
在controller层使用,会因为各种webservice生成的代码问题,造成一部分问题。
优点:完美的解决了各种xml转换问题。用JSON格式传输。
缺点:就是API生成了所有的API,代码厚重。并不能直接调用,还是需要编写controller。

题外话

关于WebService的超时问题

请参照这篇博客:Future 接口调用超时时间

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/168957.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

linux系统下如何获取文件的创建时间

linux 获取文件的创建时间 提到获取文件的创建时间&#xff0c;写Java的小伙伴可能会说&#xff0c;那太简单了&#xff0c;java.nio.file.attribute.BasicFileAttributes下这个类不就记录了文件的相关信息吗&#xff0c;比如下面这段代码不就得到文件的创建时间了嘛&#xff…

chrono_CLOCK(二)

chrono_CLOCK&#xff08;二&#xff09; 文章目录chrono_CLOCK&#xff08;二&#xff09;从测量C程序运行时间引入C风格C风格时钟的成员和源码分析成员函数成员变量Clock提供的操作例子三个clock区别例子三个clock的精度问题方式一方式二从测量C程序运行时间引入 C风格 在C…

数据库,计算机网络、操作系统刷题笔记33

数据库&#xff0c;计算机网络、操作系统刷题笔记33 2022找工作是学历、能力和运气的超强结合体&#xff0c;遇到寒冬&#xff0c;大厂不招人&#xff0c;可能很多算法学生都得去找开发&#xff0c;测开 测开的话&#xff0c;你就得学数据库&#xff0c;sql&#xff0c;oracle…

自定义View的学习笔记1-1

这一系列主要是跟随扔物线的学习笔记。 自定义View主要是三个部分&#xff0c;绘制&#xff0c;布局&#xff0c;触摸反馈。 绘制是这三个部分中&#xff0c;最重要的一个。 先说绘制&#xff0c;所谓绘制&#xff0c;指的就是控件内容的显示。啥意思&#xff0c;比如我们作…

02 技术太卷我学Apex-级联值列表

02 技术太卷我学Apex-级联值列表 0 值列表概念 就是页面输入时从下拉列表中选择固定值。 值列表可以在APEX中【共享组件】-【其它组件】-【值列表】创建&#xff0c;也可以也页面上自己用sql语句&#xff08;一般需要级联值列表最好在页面上创建&#xff09;创建。 1 创建一…

Node版本锁定

Node版本锁定问题方案一、锁定Node版本二、自动切换Node版本问题 接手项目时&#xff0c;不知道项目所用的Node版本同一个项目&#xff0c;不同人用不同的Node版本&#xff0c;引起编译后的未知问题 方案 一、锁定Node版本 在package.json中配置 engines&#xff0c;限定项目…

【微电网】基于改进粒子群算法的微电网优化调度(Matlab代码实现)

&#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;欢迎来到本博客❤️❤️&#x1f4a5;&#x1f4a5; &#x1f3c6;博主优势&#xff1a;&#x1f31e;&#x1f31e;&#x1f31e;博客内容尽量做到思维缜密&#xff0c;逻辑清晰&#xff0c;为了方便读者。 ⛳️座右铭&a…

云原生周刊 | 使用 ChatGPT 协助解决 Prometheus 告警

开源项目推荐 kubernetes-chatgpt-bot 这是一个适用于 Slack 的 ChatGPT 机器人&#xff0c;只要有监控告警发送到 Slack 频道中&#xff0c;你就可以通过机器人向 ChatGPT 咨询如何解决这个告警&#xff0c;ChatGPT 将会给出一个较为详细的解决方案。 Copacetic Copacetic …

WEB文件管理器和上传器:GleamTech FileUltimate 8.5.1.0

GleamTech FileUltimate适用于 ASP.NET Core、MVC 和 WebForms 的FileUltimate文件管理器和上传器 将文件管理器快速集成到您的 ASP.NET 应用程序或站点中。 使用访问控制浏览和管理文件。 接受具有高级上传功能的文件&#xff08;上传器也可作为独立组件使用&#xff09;。 提…

应用程序进程启动过程

1 应用程序进程简介 想要启动一个应用程序&#xff0c;首先要保证这个应用程序所需要的应用程序进程已经启动。 AMS 在启动应用程序时会检查这个应用程序所需要的应用程序进程是否已经存在&#xff0c;如果不存在就会请求 Zygote 进程启动需要的应用程序进程。 在 Zygote进程启…

2023牛客寒假算法基础集训营1--鸡玩炸蛋人(带权并查集) 诈骗题?

题目如下&#xff1a; 示例1 输入 6 4 1 2 2 3 1 3 4 6 0 0 0 0 0 0输出 14示例2 输入 6 4 1 2 2 3 1 3 4 6 0 0 0 0 2 0输出 1题目链接 题解 or 思路&#xff1a; 首先如果我们理解题意了&#xff0c;这个题是顶级诈骗。 因为是无向图&#xff0c;我们需要记录图中 环…

算法第十三期——BFS-双向广搜

双向广搜 应用场景&#xff1a;有确定的起点s和终点t&#xff1b;把从起点到终点的单向搜索&#xff0c;变换为分别从起点出发和从终点出发的“相遇”问题。操作&#xff1a;从起点s(正向搜索&#xff09;和终点t(逆向搜索&#xff09;同时开始搜索&#xff0c;当两个搜索产生…

编程太难不适合女生学?来看 N 多小姐姐的回应!

某女程序员&#xff1a;我要去互联网公司做程序员&#xff1f;网友&#xff1a;你疯了&#xff1f;程序员很累的... 女生不适合做程序员&#xff0c;还是去做产品经理吧。画外音&#xff1a;我去&#xff0c;产品经理不累吗&#xff1f;并不是女生不适合写代码&#xff0c;也不…

python cairosvg 库专题博客,10分钟掌握 cairosvg

cairosvg 库用于将 SVG 图像转换为其他图片格式。它使用 Cairo 库来绘制 SVG 图像&#xff0c;并支持将 SVG 图像转换为 PNG、PDF、PS、SVG 和 GIF 格式。 python cairosvgPython cairosvg 上手案例cairosvg 直接将 svg 图像转换为二进制数据cairosvg 库函数清单总结Python cai…

趣味三角——第1章——角

平面角是平面内相交但不在一条直线上的两条直线之间的倾角(A plane angle is the inclination to one another of two lines in a plane which meet one another and do not lie in a straight line.)。 ——Euclid(欧几里得), 元素(The Elements)&#xff0c;定义8。 几何实体…

【C++】Hash开散列,unordered_set(map) 的封装以及迭代器的实现

上一篇博客我们使用闭散列的方式实现了 Hash&#xff0c;其实在STL库unordered_set、unordered_map中底层是开散列的方式实现的Hash&#xff0c;所以&#xff0c;本篇博客就再使用开散列的方式实现Hash&#xff0c;并将unordered_set、unordered_map进行封装。 目录 一、开散…

C 数据结构1 —— 线性表-顺序表\单链表\双链表

文章目录1. 线性表1.1 定义1.2 特点2. 顺序表(顺序存储结构)2.1 定义(存储结构代码描述)2.2 插入元素2.2.1 图形演示2.2.2 代码表示2.3 删除元素2.3.1 图形演示2.3.2 代码表示2.4 完整代码2.5 动态分配数组3. 单链表(链式存储结构)3.1 定义(存储结构代码描述)3.2 单链表的读取3…

COCO_04 展示COCO格式数据集 目标框与分割mask

文章目录1 前言2 绘制GT2.1 绘制目标框与类别2.2 绘制分割mask3 AppendixA. mask polygon格式转化为图片格式参考1 前言 上篇文章介绍了如何制作COCO个数数据集的Dataset与Dataloader&#xff0c;并绘制了dataloader->batch的返回的信息&#xff0c;https://blog.csdn.net/…

【打卡】医学搜索Query相关性判断学习赛

入坑传送门 赛事介绍 文本匹配拥有广泛的应用场景&#xff0c;可以用于去除重复问题和文本相似度中。在本次学习中我们将学习&#xff1a; 如何计算文本之间的统计距离如何训练词向量 & 无监督句子编码BERT模型搭建和训练 上述步骤都是一个NLP算法工程师必备的基础&…

【GD32F427开发板试用】02-ADC规则组连续采样

本篇文章来自极术社区与兆易创新组织的GD32F427开发板评测活动&#xff0c;更多开发板试用活动请关注极术社区网站。作者&#xff1a;Stark_GS ADC 简介及特点 器件中集成了一个 12 位 2.6 MSPS 多通道 ADC。 一共有19个多路复用通道&#xff1a;16个外部通道&#xff0c;1个…