WebService是一个比较旧的远程调用通信框架,现在企业项目中用的比较少,因为它逐步被SpringCloud所取代,它的优势就是能够跨语言平台通信,所以还有点价值,下面来看看如何在SpringBoot项目中使用WebService
我们模拟从WebService客户端发送请求给WebService服务端暴露的下载文件服务,并获取服务端返回的文件保存到本地
SpringBoot2.7.3
Jdk17
在SpringBoot中整合WebService的服务端,需要通过一个配置文件将服务接口暴露出去给客户端调用
服务端POM
4.0.0 org.springframework.boot spring-boot-starter-parent 2.7.3 com.example webservice 0.0.1-SNAPSHOT webservice webservice 17 org.springframework.boot spring-boot-starter-web org.springframework.boot spring-boot-devtools runtime true org.projectlombok lombok true org.springframework.boot spring-boot-starter-test test org.apache.cxf cxf-rt-transports-http 3.5.1 org.apache.cxf cxf-rt-frontend-jaxws 3.5.1 cn.hutool hutool-all 5.8.12 com.alibaba fastjson 2.0.16 org.springframework.boot spring-boot-maven-plugin org.projectlombok lombok
服务端YML
server: # 必须配置端口,客户端需要 port: 7001
该文件为WebService服务暴露配置文件
package com.example.webservice.config; import com.example.webservice.service.FileCxfService; import com.example.webservice.service.impl.FileCxfServiceImpl; import org.apache.cxf.Bus; import org.apache.cxf.bus.spring.SpringBus; import org.apache.cxf.jaxws.EndpointImpl; import org.apache.cxf.transport.servlet.CXFServlet; import org.springframework.boot.web.servlet.ServletRegistrationBean; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import javax.xml.ws.Endpoint; @Configuration public class FileCxfConfig { @Bean(name = Bus.DEFAULT_BUS_ID) public SpringBus springBus() { return new SpringBus(); } @Bean(name = "downloadFileBean") public ServletRegistrationBean dispatcherServlet() { ServletRegistrationBean wbsServlet = new ServletRegistrationBean(new CXFServlet(), "/file/*"); return wbsServlet; } @Bean public FileCxfService fileCxfService() { return new FileCxfServiceImpl(); } @Bean public Endpoint endpointPurchase(SpringBus springBus, FileCxfService fileCxfService) { EndpointImpl endpoint = new EndpointImpl(springBus(), fileCxfService()); endpoint.publish("/download"); System.err.println("服务发布成功!地址为:http://localhost:7001/file/download?wsdl"); return endpoint; } }
该类指定了暴露的服务接口,注意类中的注解都很重要,不能丢,具体可以看说明
package com.example.webservice.service; import javax.jws.WebMethod; import javax.jws.WebParam; import javax.jws.WebResult; import javax.jws.WebService; import javax.xml.ws.BindingType; @BindingType(value = "http://www.w3.org/2003/05/soap/bindings/HTTP/") @WebService(serviceName = "FileCxfService", // 与接口中指定的name一致 targetNamespace = "http://webservice.example.com" // 与接口中的命名空间一致,一般是接口的包名倒 ) public interface FileCxfService { @WebMethod(operationName = "downloadFile") @WebResult(name = "String") String downloadFile(@WebParam(name = "params", targetNamespace = "http://webservice.example.com") String params, @WebParam(name = "token", targetNamespace = "http://webservice.example.com") String token); }
该类指定了暴露的服务接口的具体实现,注意类中的注解都很重要,不能丢,具体可以看说明
package com.example.webservice.service.impl; import cn.hutool.core.codec.Base64; import com.alibaba.fastjson2.JSONObject; import com.example.webservice.pojo.FileDto; import com.example.webservice.service.FileCxfService; import javax.jws.WebService; @WebService(serviceName = "FileCxfService", // 与接口中指定的name一致 targetNamespace = "http://webservice.example.com" // 与接口中的命名空间一致,一般是接口的包名倒 ) public class FileCxfServiceImpl implements FileCxfService { @Override public String downloadFile(String params, String token) { //下载文件 System.err.println("params : " + params); FileDto fileDto = JSONObject.parseObject(params, FileDto.class); System.err.println("fileDto : " + fileDto); String data = null; try { data = Base64.encode("C:\\Users\\YIQI\\Desktop\\ebook\\Java70.pdf"); } catch (Exception e) { e.printStackTrace(); } System.err.println(data); return data; } }
该类为参数实体类,用于接受客户端传来的参数
package com.example.webservice.pojo; import lombok.AllArgsConstructor; import lombok.Data; import lombok.ToString; @Data @AllArgsConstructor @ToString public class FileDto { private String fileId; private String newFileId; private String bucketName; }
在SpringBoot中整合WebService的客户端,需要指定服务端暴露的服务接口
客户端POM
4.0.0 org.springframework.boot spring-boot-starter-parent 2.7.3 com.example webclient 0.0.1-SNAPSHOT webclient webclient 17 org.springframework.boot spring-boot-starter-web org.springframework.boot spring-boot-devtools runtime true org.projectlombok lombok true org.springframework.boot spring-boot-starter-test test org.apache.cxf cxf-rt-transports-http 3.5.1 org.apache.cxf cxf-rt-frontend-jaxws 3.5.1 cn.hutool hutool-all 5.8.12 com.alibaba fastjson 2.0.16 org.springframework.boot spring-boot-maven-plugin org.projectlombok lombok
客户端YML
server: # 可以不配置 port: 1000
这个文件和服务端的FileCxfService保持一致,用于指定客户端请求的方式
package com.example.webclient.service; import javax.jws.WebParam; import javax.jws.WebService; @WebService(name = "FileCxfService", // 暴露服务名称 targetNamespace = "http://webservice.example.com"// 命名空间,一般是接口的包名倒序 ) public interface FileCxfService { String downloadFile(@WebParam(name = "data", targetNamespace = "http://webservice.example.com") String data, @WebParam(name = "token", targetNamespace = "http://webservice.example.com") String token); }
这个类是客户端的主类,里面有发送WebService请求的方法
package com.example.webclient.client; import cn.hutool.core.codec.Base64; import com.alibaba.fastjson2.JSONObject; import com.example.webclient.pojo.FileDto; import com.example.webclient.service.FileCxfService; import com.example.webclient.util.ConvertBASE64; import javax.xml.bind.DatatypeConverter; import javax.xml.namespace.QName; import javax.xml.ws.Service; import java.net.URL; public class FileCxfClient { public static void main(String[] args) throws Exception { // 创建wsdl的url URL url = new URL("http://localhost:7001/file/download?wsdl"); // 指定命名空间和服务名称 QName qName = new QName("http://webservice.example.com", "FileCxfService"); Service service = Service.create(url, qName); // 通过getPort方法返回指定接口 FileCxfService myServer = service.getPort(FileCxfService.class); // 调用方法返回数据 FileDto fileDto = new FileDto(); fileDto.setFileId("1"); fileDto.setNewFileId("1"); fileDto.setBucketName("book"); String params = JSONObject.toJSONString(fileDto); Long st = System.currentTimeMillis(); String file = myServer.downloadFile(params, "TOKEN:ABC"); // 解析文件到本地 // 可以解析成字节数组,如果服务端返回的也是字节数组的话 byte[] decode= Base64.decode(file); // 也可以将Base64写入到本地文件中 ConvertBASE64.decoderBase64File(file, "C:\\Users\\YIQI\\Desktop\\ebook\\demo70.pdf"); System.err.println("decode : " + decode.toString()); System.err.println("result get file success!"); System.err.println("cost time : " + (System.currentTimeMillis() - st) / 1000 + " s."); } }
这个类是封装请求参数的实体类,和服务端的FileDto保持一致
package com.example.webclient.pojo; import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor; import lombok.ToString; @Data @AllArgsConstructor @NoArgsConstructor @ToString public class FileDto { private String fileId; private String newFileId; private String bucketName; }
该类是Base64工具类,可以完成Base64字符串和文件的互换
package com.example.webclient.util; import cn.hutool.core.codec.Base64Decoder; import cn.hutool.core.codec.Base64Encoder; import cn.hutool.core.io.FileUtil; import com.alibaba.fastjson2.JSONObject; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; public class ConvertBASE64 { /** * 将文件转成base64编码字符串 * * @param path * @return * @throws Exception */ public static String encodeBase64File(String path) throws Exception { File file = new File(path); FileInputStream inputFile = new FileInputStream(file); byte[] buffer = new byte[(int) file.length()]; inputFile.read(buffer); inputFile.close(); return new Base64Encoder().encode(buffer); } /** * 将base64编码字符串转成文件 * * @param base64Code * @param targetPath * @throws Exception */ public static void decoderBase64File(String base64Code, String targetPath) throws Exception { byte[] buffer = Base64Decoder.decode(base64Code); FileOutputStream out = new FileOutputStream(targetPath); out.write(buffer); out.close(); } /** * 将base64字节装成文件 * * @param base64Code * @param targetPath * @throws Exception */ public static void toFile(String base64Code, String targetPath) throws Exception { byte[] buffer = base64Code.getBytes(); FileOutputStream out = new FileOutputStream(targetPath); out.write(buffer); out.close(); } public static String toJson(Object obj) { return JSONObject.toJSONString(obj); } public static Object toObject(String JSONString, Class cls) { return JSONObject.parseObject(JSONString, cls); } public static void writeByteArrayToFile(File desFile, byte[] data) throws IOException { FileUtil.writeBytes(data, desFile); } public static byte[] readFileToByteArray(File srcFile) throws IOException { return FileUtil.readBytes(srcFile); } public static String encode(String string) { return new String(Base64Encoder.encode(string.getBytes())); } public static void main(String[] args) { try { String a = encodeBase64File("C:\\Users\\YIQI\\Desktop\\工作文件\\bg2.jpg"); // String base64Code = encodeBase64File("D:/0101-2011-qqqq.tif"); System.out.println(a); decoderBase64File(a, "C:\\Users\\YIQI\\Desktop\\工作文件\\bg3.jpg"); // toFile(base64Code, "D:\\three.txt"); } catch (Exception e) { e.printStackTrace(); } } }
先启动服务端,可以看到对外发布的服务
在启动客户端给服务端发送请求的方法,可以看到服务端返回的数据
因为我把从服务端获取的文件写入到了本地,所以可以在文件目录中看到该文件