Java客户端调用SOAP方式的WebService服务实现方式分析

简介

在多系统交互中,有时候需要以Java作为客户端来调用SOAP方式的WebService服务,本文通过分析不同的调用方式,以Demo的形式,帮助读者在生产实践中选择合适的调用方式。

本文JDK环境为JDK17。

结论

推荐使用Axis2或者Jaxws,以无客户端的形式来调用WebService。

有客户端,推荐Maven插件。

有客户端调用

主要时利用wsdl文档,自动生成对应的Java代码来实现

建议在pom文件中,配置对应的Maven插件来实现WebService客户端代码的自动生成。

JDK wsimport命令生成(不推荐)

简介

主要是利用jdk的自带工具wsimport工具实现,执行命令如下:

wsimport -s C:\tmp\com -p com.example.demo5.wsdl -encoding utf-8 http://www.webxml.com.cn/WebServices/IpAddressSearchWebService.asmx?wsdl

优点

通常装有JDK的电脑或者服务器都可以直接运行,方便生成。

缺点

在实际的运用中wsimport命令会有很多问题,首先只有JDK1.8才支持这个命令,即使能使用,仍然存在一些问题。其次,在JDK17以上没有自带这个工具,可能要安装插件才能使用,但是笔者安装了一些插件仍然无法使用。

ApacheCXF自动生成(不推荐)

简介

ApacheCXF通过安装也可以自动生成对应的WebService客户端代码。具体操作可见链接。

缺点

需要额外安装ApacheCXF插件。

Maven插件自动生成(推荐)

简介

通过spring.io网址的Demo示例,可以配置pom的maven插件,自动生成代码。

demo获取链接如下:

Getting Started | Consuming a SOAP web service (spring.io)

pom配置示例如下:

<build>
		<plugins>
			<plugin>
				<groupId>org.springframework.boot</groupId>
				<artifactId>spring-boot-maven-plugin</artifactId>
			</plugin>
			<!-- tag::wsdl[] -->
			<plugin>
				<groupId>com.sun.xml.ws</groupId>
				<artifactId>jaxws-maven-plugin</artifactId>
				<version>3.0.0</version>
				<executions>
					<execution>
						<goals>
							<goal>wsimport</goal>
						</goals>
					</execution>
				</executions>
				<configuration>
					<packageName>com.example.consumingwebservice.wsdl</packageName>
					<wsdlUrls>
<!--						<wsdlUrl>http://localhost:8080/ws/countries.wsdl</wsdlUrl>-->
						<wsdlUrl>http://www.webxml.com.cn/WebServices/IpAddressSearchWebService.asmx?wsdl</wsdlUrl>
					</wsdlUrls>
					<sourceDestDir>${sourcesDir}</sourceDestDir>
					<destDir>${classesDir}</destDir>
					<extension>true</extension>
				</configuration>
			</plugin>
			<!-- end::wsdl[] -->
		</plugins>
	</build>

生成代码如下:

调用方式:

@RequestMapping(value = "/{ip}", method = RequestMethod.GET)
    public ArrayOfString searchIp(@PathVariable("ip") String ip) {
        IpAddressSearchWebServiceSoap ipAddressSearchWebServiceSoap = new IpAddressSearchWebService().getIpAddressSearchWebServiceSoap();
        ArrayOfString response = ipAddressSearchWebServiceSoap.getCountryCityByIp(ip);
        return response;
    }

优点

操作简单,改动小。

缺点

唯一的缺点,也是有客户端调用普遍存在的,自动生成代码后,需要重新部署一次。

Springboot集成Git插件实现

通过Springboot集成git插件,可以通过接口的形式来修改maven的wsdlUrls配置,然后推送到git服务,最后触发Jenkins自动部署。

以Git推送代码的形式来实现代码的自动生成,其缺点是,每次根据一份wsdl文件生成完代码,需要重启一次服务,但是笔者通过自动配置的形式可以做到一键部署。

其中触发Jenkins自动部署,可以通过git的配置实现,通过访问特定的url实现。配置好的git部署链接如下:

http://192.168.22.22:8080/job/demo_test/build?token=1987654567890hjkoijghfvgjjnmkjkmk

其中token的值可以自动定义,这样在借助代码的形式就可以做到一键部署。

其实现代码如下:

package com.example.consumingwebservice;

import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardOpenOption;
import java.util.Date;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.eclipse.jgit.api.Git;
import org.eclipse.jgit.lib.Repository;
import org.eclipse.jgit.transport.CredentialsProvider;
import org.eclipse.jgit.transport.HttpConfig;
import org.eclipse.jgit.transport.UsernamePasswordCredentialsProvider;

public class GitUtil {
    //private static Log log = LogFactory.getLog(GitUtil.class);

    private GitUtil() {
    }

    public static Git getGit(String uri, CredentialsProvider credentialsProvider, String localDir) throws Exception {
        Git git = null;
        if (new File(localDir).exists() ) {
            git = Git.open(new File(localDir));
        } else {
            git = Git.cloneRepository().setCredentialsProvider(credentialsProvider).setURI(uri)
                    .setDirectory(new File(localDir)).call();
        }
        //设置一下post内存,否则可能会报错Error writing request body to server
        git.getRepository().getConfig().setInt(HttpConfig.HTTP, null, HttpConfig.POST_BUFFER_KEY, 512*1024*1024);
        return git;
    }

    public static CredentialsProvider getCredentialsProvider(String username, String password) {
        return new UsernamePasswordCredentialsProvider(username, password);
    }

    public static Repository getRepository(Git git) {
        return git.getRepository();
    }

    public static void pull(Git git, CredentialsProvider credentialsProvider) throws Exception {
        git.pull().setRemote("origin").setCredentialsProvider(credentialsProvider).call();
    }

    public static void push(Git git, CredentialsProvider credentialsProvider, String filepattern, String message)
            throws Exception {

        git.add().addFilepattern(filepattern).call();
        git.add().setUpdate(true);
        git.commit().setMessage(message).call();
        git.push().setCredentialsProvider(credentialsProvider).call();

    }

    public static void main(String[] args) throws Exception {
        String uri = "http://192.168.9.11/test/webservice.git";
        String username = "343535@qq.com";
        String password = "xdfetrfrr";
        CredentialsProvider credentialsProvider = getCredentialsProvider(username, password);

        String localDir = "C:/tmp/git_test";
        Git git = getGit(uri, credentialsProvider, localDir);
        pull(git, credentialsProvider);
        changeFile(localDir + "/pom.xml");

//        push(git, credentialsProvider, ".", "提交文件");
        push(git, credentialsProvider, "pom.xml", "修改pom文件" + new Date());

    }

    private static final String newText = "    <wsdlUrl>http://www.webxml.com.cn/WebServices/IpAddressSearchWebService.asmx?wsdl</wsdlUrl>\r\n                    </wsdlUrls>";

    protected static void changeFile(String filePath) {
        try {
            // 读取文本文件的内容
            Path path = Paths.get(filePath);
            String content = Files.readString(path);
            System.out.println(content);
            // 替换内容
            String modifiedContent = content.replace("</wsdlUrls>", newText);
            // 将修改后的内容写回文本文件
            Files.write(path, modifiedContent.getBytes(), StandardOpenOption.WRITE);
            System.out.println("文本文件内容已成功修改!");
        } catch (IOException e) {
            System.out.println("修改文本文件内容时出现错误:" + e.getMessage());
        }

    }

}

 如要实现流程图的规划,可以后台通过http的get请求上文的git部署链接,实现接口的自动部署。

无客户端调用

也就是不需要按wsdl的格式来生成对应的Java代码,原理时通过构建xml的形式来访问WebService。

这里推荐使用Axis2或者Jaxws的方式来调用,二者各有优劣。

Axis调用(不推荐)

简介

通过pom引入axis依赖,实现无客户端访问,所需依赖如下:

<dependency>
<groupId>axis</groupId>
<artifactId>axis</artifactId>
<version>1.4</version>
</dependency>

代码实现如下:

public static void main(String[] args){
        try {
            String nameSpac = "http://WebXml.com.cn/";
            URL url = new URL("http://ws.webxml.com.cn/WebServices/MobileCodeWS.asmx?wsdl");
            QName sname = new QName(nameSpac, "MobileCodeWS");
            QName pname = new QName(nameSpac, "MobileCodeWSSoap");
            Service service = new Service( url, sname);
            Call call = (Call)service.createCall(pname);
            call.setSOAPActionURI(nameSpac + "getMobileCodeInfo");
            call.setOperationName(new QName(nameSpac, "getMobileCodeInfo")); // 需要请求的方法
            call.addParameter(new QName(nameSpac, "mobileCode"), XMLType.XSD_STRING, ParameterMode.IN);  // 入参
            call.addParameter(new QName(nameSpac, "userID"), XMLType.XSD_STRING, ParameterMode.IN);  // 入参
//            call.addParameter("param3", XMLType.SOAP_STRING, ParameterMode.IN);  // 入参
            String param1 = "15932582632";  // 参数
            String param2 = null;  // 参数
            call.setReturnClass(String.class);  // 设置返回值
            call.setUseSOAPAction(true);
            Object invoke = call.invoke(new Object[]{param1, param2});// 调用获取返回值
//            Object invoke =  call.invoke(new Object[]{});// 调用获取返回值
            System.out.println(invoke);
        }catch (Exception e){
            e.printStackTrace();
        }
    }

优点

较少的代码量,依赖需要少,实现简单

缺点

通过笔者的实验,发现Axis的调用并不稳定,对于不同的接口,有的接口无参数调用可以调通,有参数调用会报错,有的接口有参数调用可以调通(如例),无参数调用会报错。

实际上,这个依赖在2006年便没有维护了,它的功能转移到了Axis2。

Axis2调用(推荐)

简介

通过pom引入axis2依赖,实现无客户端访问,所需依赖如下:

<dependency>
<groupId>org.apache.axis2</groupId>
<artifactId>axis2-jaxws</artifactId>
<version>1.7.0</version>
</dependency>

<dependency>
<groupId>org.apache.axis2</groupId>
<artifactId>axis2-adb-codegen</artifactId>
<version>1.7.0</version>
</dependency>

<dependency>
<groupId>org.apache.axis2</groupId>
<artifactId>axis2-transport-local</artifactId>
<version>1.7.0</version>
</dependency>

<dependency>
<groupId>org.apache.axiom</groupId>
<artifactId>com.springsource.org.apache.axiom</artifactId>
<version>1.2.5</version>
</dependency>

代码实现如下:

import org.apache.axiom.om.OMAbstractFactory;
import org.apache.axiom.om.OMElement;
import org.apache.axiom.om.OMFactory;
import org.apache.axiom.om.OMNamespace;
import org.apache.axis2.AxisFault;
import org.apache.axis2.addressing.EndpointReference;
import org.apache.axis2.client.Options;
import org.apache.axis2.client.ServiceClient;
import org.apache.axis2.transport.http.impl.httpclient3.HttpTransportPropertiesImpl;

/**
 *
 * @ClassName: MobileClientDoc
 * @Description: TODO
 * 方法二: 应用document方式调用 用ducument方式应用现对繁琐而灵活。现在用的比较多。因为真正摆脱了我们不想要的耦合
 * 即使用org.apache.axis2.client.ServiceClient类进行远程调用web服务,不生成客户端
 *
 * @date 2017年11月9日 下午1:27:17
 *
 */
public class SoapAxis2Client {
    private static String requestName = "getCountryCityByIp";

    public static void ipWS() {
        try {
            ServiceClient serviceClient = new ServiceClient();
            //创建服务地址WebService的URL,注意不是WSDL的URL
            String url = "http://www.webxml.com.cn/WebServices/IpAddressSearchWebService.asmx";
            EndpointReference targetEPR = new EndpointReference(url);
            Options options = serviceClient.getOptions();
            options.setTo(targetEPR);
            //确定调用方法(wsdl 命名空间地址 (wsdl文档中的targetNamespace) 和 方法名称 的组合)
            options.setAction("http://WebXml.com.cn/" + requestName);
            //设置密码
            HttpTransportPropertiesImpl.Authenticator auth = new HttpTransportPropertiesImpl.Authenticator();
//            auth.setUsername(username);  //服务器访问用户名
//            auth.setPassword(password); //服务器访问密码
//            options.setProperty(HTTPConstants.AUTHENTICATE, auth);

            OMFactory fac = OMAbstractFactory.getOMFactory();
            /*
             * 指定命名空间,参数:
             * uri--即为wsdl文档的targetNamespace,命名空间
             * perfix--可不填
             */
            OMNamespace omNs = fac.createOMNamespace("http://WebXml.com.cn/", "");
            // 指定方法
            OMElement method = fac.createOMElement(requestName, omNs);
            // 指定方法的参数
            OMElement theIpAddress = fac.createOMElement("theIpAddress", omNs);
            theIpAddress.setText("111.249.198.56");
//            OMElement userID = fac.createOMElement("userID", omNs);
//            userID.setText("");
            method.addChild(theIpAddress);
//            method.addChild(userID);
            method.build();

            //远程调用web服务
            OMElement result = serviceClient.sendReceive(method);
            //值得注意的是,返回结果就是一段由OMElement对象封装的xml字符串。
            String xml = result.cloneOMElement().toString();
            System.out.println(xml);

        } catch (AxisFault axisFault) {
            axisFault.printStackTrace();
        }
    }

    public static void main(String[] args) throws AxisFault {
        ipWS();
    }


}

优点

代码量较少,通过配置xml节点实现系统调用,可以设置灵活的调用方式。经过实验,对各种WebService接口的有参无参调用,都能取得正确的返回结果。

测试结果如下:

<getCountryCityByIpResponse xmlns="http://WebXml.com.cn/"><getCountryCityByIpResult><string>111.249.198.56</string><string>台湾省  </string></getCountryCityByIpResult></getCountryCityByIpResponse>

缺点

所需的pom配置文件较多,且引用不正确较难排查问题,且各个pom之间的版本冲突也需要解决。

Jaxws调用(推荐)

简介

引入对于的pom配置文件

<dependency>
   <groupId>org.apache.axis2</groupId>
	<artifactId>axis2-jaxws</artifactId>
	<version>1.7.0</version>
</dependency>

这里提前说下,下面代码大部分来自于csdn作者——LengYouNuan的文章,但是实在找不到对于作者了,提前声明。

还有它的原始代码并不能正常运行,会有服务器未能识别 HTTP 头 SOAPAction 的值的报错,笔者通过实验和研究,添加了如下配置,才能正常运行:

//这句话很重要,否则报错服务器未能识别 HTTP 头 SOAPAction 的值
dispatch.getRequestContext().put(SOAPACTION_URI_PROPERTY, nameSpace + elementName);
dispatch.getRequestContext().put(SOAPACTION_USE_PROPERTY, true);

由于使用的JDK17,对应的配置和以前不一样了:

public interface BindingProvider {
    String USERNAME_PROPERTY = "jakarta.xml.ws.security.auth.username";
    String PASSWORD_PROPERTY = "jakarta.xml.ws.security.auth.password";
    String ENDPOINT_ADDRESS_PROPERTY = "jakarta.xml.ws.service.endpoint.address";
    String SESSION_MAINTAIN_PROPERTY = "jakarta.xml.ws.session.maintain";
    String SOAPACTION_USE_PROPERTY = "jakarta.xml.ws.soap.http.soapaction.use";
    String SOAPACTION_URI_PROPERTY = "jakarta.xml.ws.soap.http.soapaction.uri";
......

应该主要是javax和jakarta的区别。

完整可运行代码如下:

package com.example.consumingwebservice;



import com.sun.xml.ws.client.BindingProviderProperties;
import com.sun.xml.ws.developer.JAXWSProperties;
import jakarta.xml.soap.*;
import jakarta.xml.ws.Dispatch;
import jakarta.xml.ws.Service;
import org.w3c.dom.Document;

import javax.xml.namespace.QName;
import java.net.URL;
import java.util.HashMap;
import java.util.Map;

import static jakarta.xml.ws.BindingProvider.SOAPACTION_URI_PROPERTY;
import static jakarta.xml.ws.BindingProvider.SOAPACTION_USE_PROPERTY;

/**
 * soap方式调用webservice方式客户端
 *
 * @author LengYouNuan
 * @create 2021-05-31 下午2:35
 */
public class SoapJaxwsClient {
    String nameSpace = ""; //wsdl的命名空间
    String wsdlUrl = ""; //wsdl文档地址
    String serviceName = ""; //服务的名字
    String portName = "";
    String responseName = ""; //@WebResult:注解上的name值
    String elementName = ""; //默认是要访问的方法名 如果@WebMethod属性name有值 则是该值,实际还是以wsdl文档为主
    int timeout = 20000;

    /**
     * @param nameSpace
     * @param wsdlUrl
     * @param serviceName
     * @param portName
     * @param element
     * @param responseName
     */

    public SoapJaxwsClient(String nameSpace, String wsdlUrl,
                           String serviceName, String portName, String element,
                           String responseName) {
        this.nameSpace = nameSpace;
        this.wsdlUrl = wsdlUrl;
        this.serviceName = serviceName;
        this.portName = portName;
        this.elementName = element;
        this.responseName = responseName;
    }

    /**
     * @param nameSpace
     * @param wsdlUrl
     * @param serviceName
     * @param portName
     * @param element
     * @param responseName
     * @param timeOut      毫秒
     */

    public SoapJaxwsClient(String nameSpace, String wsdlUrl,
                           String serviceName, String portName, String element,
                           String responseName, int timeOut) {
        this.nameSpace = nameSpace;
        this.wsdlUrl = wsdlUrl;
        this.serviceName = serviceName;
        this.portName = portName;
        this.elementName = element;
        this.responseName = responseName;
        this.timeout = timeOut;
    }

    public String sendMessage(HashMap<String, String> inMsg) throws Exception {
        // 创建URL对象
        URL url = null;
        try {
            url = new URL(wsdlUrl);
        } catch (Exception e) {
            e.printStackTrace();
            return "创建URL对象异常";
        }
        // 创建服务(Service)
        QName sname = new QName(nameSpace, serviceName);
        Service service = Service.create(url, sname);

        // 创建Dispatch对象
        Dispatch<SOAPMessage> dispatch = null;
        try {
            dispatch = service.createDispatch(new QName(nameSpace, portName), SOAPMessage.class, Service.Mode.MESSAGE);
        } catch (Exception e) {
            e.printStackTrace();
            return "创建Dispatch对象异常";
        }

        // 创建SOAPMessage
        try {
            //这句话很重要,否则报错服务器未能识别 HTTP 头 SOAPAction 的值
            dispatch.getRequestContext().put(SOAPACTION_URI_PROPERTY, nameSpace + elementName);
            dispatch.getRequestContext().put(SOAPACTION_USE_PROPERTY, true);

            SOAPMessage msg = MessageFactory.newInstance(
                    SOAPConstants.SOAP_1_1_PROTOCOL).createMessage();
            msg.setProperty(SOAPMessage.CHARACTER_SET_ENCODING, "UTF-8");

            SOAPEnvelope envelope = msg.getSOAPPart().getEnvelope();


            // 创建SOAPHeader(不是必需)
            // SOAPHeader header = envelope.getHeader();
            // if (header == null)
            // header = envelope.addHeader();
            // QName hname = new QName(nameSpace, "username", "nn");
            // header.addHeaderElement(hname).setValue("huoyangege");


            // 创建SOAPBody
            SOAPBody body = envelope.getBody();
            QName ename = new QName(nameSpace, elementName, "");
            SOAPBodyElement ele = body.addBodyElement(ename);
            // 增加Body元素和值
            for (Map.Entry<String, String> entry : inMsg.entrySet()) {
                ele.addChildElement(new QName(nameSpace, entry.getKey()))
                        .setValue(entry.getValue());
            }

            // 超时设置
            dispatch.getRequestContext().put(BindingProviderProperties.CONNECT_TIMEOUT, timeout);
            dispatch.getRequestContext().put(JAXWSProperties.REQUEST_TIMEOUT, timeout);
            // 通过Dispatch传递消息,会返回响应消息
            SOAPMessage response = dispatch.invoke(msg);

            // 响应消息处理,将响应的消息转换为doc对象
            Document doc = response.getSOAPPart().getEnvelope().getBody()
                    .extractContentAsDocument();
            String ret = doc.getElementsByTagName(responseName).item(0).getTextContent();
            return ret;
        } catch (Exception e) {
            e.printStackTrace();
            throw e;
        }
    }

    public static void main(String[] args) throws Exception {
//        SoapClient soapClient=new SoapClient("http://spring.io/guides/gs-producing-web-service","http://localhost:8080/ws/countries.wsdl",
//                "CountriesPortService","CountriesPortSoap11","getCountry",
//                "getCountryResponse",
//                2000);
        SoapJaxwsClient soapClient = new SoapJaxwsClient("http://WebXml.com.cn/", "http://ws.webxml.com.cn/WebServices/MobileCodeWS.asmx?wsdl",
                "MobileCodeWS", "MobileCodeWSSoap", "getDatabaseInfo",
                "getDatabaseInfoResponse",
                2000);
//        SoapClient soapClient=new SoapClient("http://WebXml.com.cn/","http://www.webxml.com.cn/WebServices/IpAddressSearchWebService.wsdl",
//                "IpAddressSearchWebService","IpAddressSearchWebServiceSoap","getCountryCityByIp",
//                "getCountryCityByIpResult",
//                2000);
        //封装请求参数
        HashMap<String, String> msg = new HashMap<>();
//        msg.put("theIpAddress","111.249.198.56");
//        msg.put("mobileCode","18702750020");
//        msg.put("userID","");
        String s = soapClient.sendMessage(msg);
        System.out.println(s);
    }
}

测试结果:

优点

pom配置简单,无须解决各种版本依赖的问题。

缺点

可以看到Jaxws的调用和Axis2一样,都具有较高的灵活性,都可以自定义xml的节点数据。

所不同的是,它的调用代码稍显繁琐,但如果在生产中,有良好的封装,这应该不是问题。

小结

对于SOAP方式WebService的调用,有客户端的调用,推荐maven插件自动生成代码的形式,唯一的缺点是需要重新部署一次。

对于无客户端的调用,推荐Axis2或者Jaxws的形式,考虑到二者实现其实各有优劣,有需要的读者可以自行甄别选用。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

推出全新的无线通讯模块(1SJ型、2DT-158型、2GT-001型、1YN型、2AE型)助力物联网新发展

相关型号&#xff1a;LBAA0QB1SJ-296 LBAA0XV2DT-158 LBAA0XV2GT-001 LBEE5KL1YN-814 LBEE5PK2AE-564 全新的无线通讯模块&#xff08;1SJ型、2DT-158型、2GT-001型、1YN型、2AE型&#xff09;助力物联网新发展&#xff08;明佳达&#xff09; 1、1SJ型集成LoRaWAN调制解调器…

优劣分析:重启路由器 vs 使用IP代理

目前更换IP主要有两种常见方法&#xff0c;一种是重启路由器&#xff0c;另一种是使用代理IP&#xff0c;那么&#xff0c;这两种方法有什么优缺点呢&#xff1f;下面我们一起来探讨一下。 方法一&#xff1a;重启路由器变换IP 优点 1. 操作简单&#xff1a;只需断开路由器电…

『C + ⒈』‘\‘

&#x1f942;在反斜杠(\)有⒉种最常用的功能如下所示&#x1f44b; #define _CRT_SECURE_NO_WARNINGS 1 #include<stdio.h> int main(void) {int a 10;int b 20;int c 30;if (a 10 &&\b 20 &&\c 30){printf("Your print\n");}else{prin…

解锁AI大模型潜能:预训练、迁移学习与中间件编程的协同艺术

在人工智能的浩瀚星空中&#xff0c;大型预训练模型&#xff08;Large Language Models, LLMs&#xff09;犹如璀璨的星辰&#xff0c;引领着技术革新的浪潮。这些模型通过海量数据的滋养&#xff0c;学会了理解语言、生成文本乃至执行复杂任务的能力。然而&#xff0c;要让这些…

可以拖拽的富文本编辑器(VueDragResize,quill-editor)

该功能实现一个帮助文档的展示和编辑功能&#xff0c;默认进去只能查看帮助文档的内容&#xff0c;点击编辑可以进行富文本编辑器的编辑功能。 出现的问题1.如何隐藏富文本编辑的工具栏并且禁止编辑 //隐藏工具栏this.toolbar this.$refs.myTextEditor.quill.getModule(toolb…

LeetCode之无重复字符的最长子串

1.题目链接 3. 无重复字符的最长子串 - 力扣&#xff08;LeetCode&#xff09;https://leetcode.cn/problems/longest-substring-without-repeating-characters/description/ 2.题目解析 题目主要思路其实是滑动窗口&#xff0c;使用两个指针维护一个动态区间&#xff0c;使…

价格疑云?格行WiFi创始人亲解谜团,性价比之王如何炼成?

随身wifi行业乱象频出&#xff0c;作为行业领跑品牌的格行随身wifi&#xff0c;关于价格问题一直备受质疑。关于设备上的“格行自有格行的骄傲”也被外界认定为是自大&#xff0c;甚至发展的线下一万多家门店也被同行不认可。近日&#xff0c;企业财经专访记者有幸采访了格行随…

实时消息推送系统,写得太好了!

websocket 协议是在 http 协议上的一种补充协议&#xff0c;是 html5 的新特性&#xff0c;是一种持久化的协议。其实 websocket 和 http 关系并不是很大&#xff0c;不过都是属于应用层的协议&#xff0c;接下来我们就开始实战。 websocket 定时推送 本教程基于 springboot …

华为od相关信息分享

2024年OD统一考试&#xff08;D卷&#xff09;完整题库&#xff1a;华为OD机试2024年最新题库&#xff08;Python、JAVA、C合集&#xff09; 问 1.什么是华为od&#xff1f; 答&#xff1a;OD全称是Outsourcing Dispacth&#xff0c;即外包派遣&#xff0c;是华为和外企德科…

HTML 标签简写及全称:表格内容将通过JavaScript动态生成

<!DOCTYPE html> <html lang"zh-CN"><head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.0"><title>HTML 标签简写及全称</title><style>…

2025考研~数据结构试卷

作者主页&#xff1a;知孤云出岫 数据结构试题 [TOC](数据结构试题)数据结构试卷一、选择题&#xff08;每题2分&#xff0c;共20分&#xff09;二、填空题&#xff08;每题3分&#xff0c;共15分&#xff09;三、简答题&#xff08;每题10分&#xff0c;共40分&#xff09;四…

卷技术还是卷应用?李彦宏给出了明确答案

如何理解李彦宏说的“不要卷模型&#xff0c;要卷应用” 引言 7月4日&#xff0c;2024世界人工智能大会在上海世博中心召开。百度创始人兼CEO李彦宏在产业发展主论坛上呼吁&#xff1a;“大家不要卷模型&#xff0c;要卷应用&#xff01;”这句话引起了广泛讨论。李彦宏认为&a…

【python】PyQt5对象类型的判定,对象删除操作详细解读

✨✨ 欢迎大家来到景天科技苑✨✨ &#x1f388;&#x1f388; 养成好习惯&#xff0c;先赞后看哦~&#x1f388;&#x1f388; &#x1f3c6; 作者简介&#xff1a;景天科技苑 &#x1f3c6;《头衔》&#xff1a;大厂架构师&#xff0c;华为云开发者社区专家博主&#xff0c;…

2款一键word生成ppt的AI工具,让职场办公更为简单!

在当下主打异步沟通的职场办公环境中&#xff0c;我们与很多人的沟通&#xff0c;都是通过书面材料来达成的&#xff0c;这就让 Word 或文档编辑软件变得更为重要&#xff0c;与此同时&#xff0c;有时为了凸现书面材料中的重点&#xff0c;我们还要将 word 文档转换为 ppt 来进…

设计模式使用简例(简单工厂+策略模式+模板方法)

直接上代码&#xff0c;方便记忆。主要的要点&#xff0c;已经写到注释中。 一&#xff0c;代码展示 启动类 package com.rojer;import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication;SpringBootAppli…

代码随想录算法训练营第四十八天| 115.不同的子序列、583. 两个字符串的删除操作、 72. 编辑距离

115.不同的子序列 题目链接&#xff1a;115.不同的子序列 文档讲解&#xff1a;代码随想录 状态&#xff1a;不会 思路&#xff1a; dp[i][j] 表示在 s 的前 j 个字符中&#xff0c;t 的前 i 个字符作为子序列出现的次数。 匹配的情况&#xff1a; 1.当 s[j-1] 与 t[i-1] 匹配…

TextView 实现最后一行缩进指定距离

实现图上类似的效果。 指定最大行数为三行&#xff0c;最后一行缩进指定的距离。 如果行数小于三行&#xff0c;则不缩进。 同时文字两端对齐 代码里的 JustifyTextView &#xff08;两端对齐的 Textview &#xff09;详见 Android Textview 多行文本两端对齐_android tex…

混合贪心算法求解地铁线路调度

一、问题描述 城市轨道交通的繁荣发展&#xff0c;带来了车辆资源需求的日益增加。如何兼顾运营服务水平和运营成本&#xff0c;以最少的车底优质地完成运输任务成为一大严峻问题。本题在后续的描述中将由多辆动车和拖车组合而成的车组称为车底。在日常的运营组织中&#xff0…

单链表(C语言详细版)

1. 链表的概念及结构 概念&#xff1a;链表是一种物理存储结构上非连续、非顺序的存储结构&#xff0c;数据元素的逻辑顺序是通过链表中的指针链接次序实现的。 链表的结构跟火车车厢相似&#xff0c;淡季时车次的车厢会相应减少&#xff0c;旺季时车次的车厢会额外增加几节。…

elasticsearch SQL:在Elasticsearch中启用和使用SQL功能

❃博主首页 &#xff1a; 「码到三十五」 &#xff0c;同名公众号 :「码到三十五」&#xff0c;wx号 : 「liwu0213」 ☠博主专栏 &#xff1a; <mysql高手> <elasticsearch高手> <源码解读> <java核心> <面试攻关> ♝博主的话 &#xff1a…