前言
在一些业务场景中,可能会出现系统为用户提供一系列选择,然后用户自定义好执行流程后交给系统执行。由于用户的组合多种多样,且执行时前后具有依赖关系,因此无法使用常用的方法,如前端向后台发送任务、执行后返回,再继续下一个任务,而是前端构建好一个流程,再交给后台统一执行,避免前后端为完成一个大任务多次地向后台发送请求。针对地理模型计算流程而言,就是这样的场景。
下面以实现地理模型计算流程为例,阐述其技术思路。
1. 发布单个地理模型的WSDL文件;【本篇博客主讲内容】
2. 根据用户提供的流程拼接WSDL文件,形成一个流程WSDL文件;
3. 发布流程WSDL文件;
4. 通过HTTP SOAP方法调用并执行发布的流程WSDL文件(实际是通过该文件逐一调用单个WSDL文件)
0、术语介绍
- Web Service是一个平台独立的,低耦合的,自包含的、基于可编程的web的应用程序,可使用开放的XML(标准通用标记语言下的一个子集)标准来描述、发布、发现、协调和配置这些应用程序,用于开发分布式的交互操作的应用程序。
- WSDL:Web服务描述语言(Web Services Description Language)是为描述Web服务发布的XML格式。WSDL描述Web服务的公共接口。这是一个基于XML的关于如何与Web服务通讯和使用的服务描述;也就是描述与目录中列出的Web服务进行交互时需要绑定的协议和信息格式。
- Apache CXF:是一个开源的 Services 框架,CXF 可以帮助利用Frontend编程API来构建和开发 Services,像 JAX-WS。Apache CXF 的前身叫 Apache CeltiXfire,继承了 Celtix 和 XFire 两大开源项目的精华,提供了对 JAX-WS 全面的支持,并且提供了多种 Binding 、DataBinding、Transport 以及各种 Format 的支持,并且可以根据实际项目的需要,采用代码优先(Code First)或者 WSDL 优先(WSDL First)来轻松地实现 Web Services 的发布和使用。
1、发布地理模型的Web Service
1.1、创建maven多模块项目框架
由于地理模型有其适用的领域,如数字土壤制图有其里面常用的方法、数字地形分析有其常用的方法,因此需要将不同地理模型领域分为各个模块,易于管理。
多模块项目通常由一个父模块和若干个子模块构成,每个模块都对应着一个pom.xml。它们之间通过继承和聚合(也称作多模块)相互关联。多模块适用于一些比较大的项目,通过合理的模块拆分,实现代码的复用,便于维护和管理。
以IDEA为例,以应用spring-boot框架的前提构建maven多模块项目。
图片1:用spring initializr创建父项目模块
图片2:备用构建链接
图片3:填写项目基本信息
图片4:选择开发工具与spring web service依赖
图片5:填写项目名称与位置、模块名称
图片6:初始项目结构(含父模块)
图片7:用Maven创建主模块
图片8:填写主模块信息
图片9:将父模块中的src迁移到主模块中
图片10:用图片7的方式创建空的子模块
1.2、使用spring-boot+CXF实现Web Service的架构
当前一个空的多模块项目就创建好了,接下来需要往里面配置web service的相关依赖。但在此之前仍需要稍微了解一下多模块配置的相关信息。
多模块项目中,父模块打包类型必须是pom,同时以给出所有的子模块,其中每个module,都是另外一个maven项目。子pom可以继承父pom中的各项配置,因此父pom可以对子pom进行统一的配置和依赖管理。
一般在项目最顶层的父pom中使用dependencyManagement元素,这让所有子模块都能够引用一个依赖而不用显式的列出版本号。maven会沿着父子层次向上走,直到找到一个拥有dependencyManagement元素的项目,然后它就会使用在这个dependencyManagement元素中指定的版本号。
官方推荐使用application.yml文件代替application.properties。优点是yml文件可以使用结构化的编写。
接着1.1节创建的多模块项目配置开发依赖。
图片11:主模块中需要的资源文件
图片12:主模块中程序入口的配置
图片13:主模块中spring-boot总配置
图片14:主模块中pom文件的依赖配置
图片15:子模块xml的bean初始化与cxf导入
图片16:父模块pom文件中子模块的统一依赖配置
图片17:父模块pom文件中的基础依赖配置
webservice父模块pom文件通用配置
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<packaging>pom</packaging>
<modules>
<module>ws-egc</module>
<module>ws-hsm</module>
</modules>
<!--spring-boot框架-->
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.4.0</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>org.egc</groupId>
<artifactId>webservice</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>webservice</name>
<description>Demo project for Spring Boot</description>
<properties>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
<boot.version>2.2.7.RELEASE</boot.version>
<log4j2.version>2.11.1</log4j2.version>
<cxf.version>3.3.6</cxf.version>
<wsdl4j.version>1.6.3</wsdl4j.version>
<commons-lang3.version>3.5</commons-lang3.version>
<commons-exec.version>1.3</commons-exec.version>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.8</java.version>
</properties>
<dependencyManagement>
<dependencies>
<!-- https://mvnrepository.com/artifact/org.apache.geronimo.specs/geronimo-jaxws_2.2_spec -->
<dependency>
<groupId>org.apache.geronimo.specs</groupId>
<artifactId>geronimo-jaxws_2.2_spec</artifactId>
<version>1.2</version>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<!-- 否则报错 java.lang.NoSuchMethodError:javax.validation.BootstrapConfiguration.getClockProvider -->
<dependency>
<groupId>javax</groupId>
<artifactId>javaee-api</artifactId>
<version>8.0</version>
<scope>provided</scope>
</dependency>
<!-- 帮助开发人员消除Java的冗长代码 -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.16.16</version>
<scope>provided</scope>
</dependency>
<!--spring-boot-->
<!--配置spring的框架-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<!--用于ConfigurationProperties-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<optional>true</optional>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
<!--排除默认的logback-->
<exclusions>
<exclusion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-logging</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-log4j2</artifactId>
</dependency>
<!-- CXF SOAP webservice -->
<!--配置cxf框架-->
<dependency>
<groupId>org.apache.cxf</groupId>
<artifactId>cxf-spring-boot-starter-jaxws</artifactId>
<version>${cxf.version}</version>
</dependency>
<!-- CXF REST webservice -->
<dependency>
<groupId>org.apache.cxf</groupId>
<artifactId>cxf-spring-boot-starter-jaxrs</artifactId>
<version>${cxf.version}</version>
</dependency>
<!-- cxf-rt-rs-service-description -->
<dependency>
<groupId>org.apache.cxf</groupId>
<artifactId>cxf-rt-rs-service-description</artifactId>
<version>${cxf.version}</version>
</dependency>
<dependency>
<groupId>org.apache.cxf</groupId>
<artifactId>cxf-rt-features-logging</artifactId>
<version>${cxf.version}</version>
</dependency>
<dependency>
<groupId>org.apache.cxf</groupId>
<artifactId>cxf-rt-rs-extension-providers</artifactId>
<version>${cxf.version}</version>
</dependency>
</dependencies>
<build>
<plugins>
<!--添加spring-boot的插件-->
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
图片18:将该框架托管到码云上
1.3、在子模块中创建具体服务
在子模块中的实现遵循接口-实现的模式,避免把所有东西都放在同一个文件,增强灵活性。
图片19:子模块的结构
DomainASerive接口
import javax.jws.WebMethod;
import javax.jws.WebParam;
import javax.jws.WebResult;
import javax.jws.WebService;
import javax.xml.ws.BindingType;
import javax.xml.ws.soap.SOAPBinding;
//注入SOAP绑定类型
@BindingType(SOAPBinding.SOAP11HTTP_BINDING)
//注入标识该接口为@WebService类型,同时指定目标的命名空间
@WebService(targetNamespace = "http://test.org/ws/DomainAService")
public interface DomainAService {
//为每个方法都注入标识为@WebMethod类型,同时指定action标识
@WebMethod(action = "http://test.org/ws/DomainAService/geoModelOne")
//注入返回的结果为result
@WebResult(name = "result")
//为每一个参数都指定@WebParam,为的是能够填写访问时的名字,否则默认名字为arg0, arg1,不以利于阅读
String geoModelOne(@WebParam(name = "inputPath")String inputPath,
@WebParam(name = "outputPath")String outputPath);
}
DomainASeriveImp实现
import org.egc.ws.hsm.DomainAService;
import org.springframework.stereotype.Service;
import javax.jws.WebService;
import javax.xml.ws.BindingType;
import javax.xml.ws.soap.SOAPBinding;
//spring-boot注入,标识这是一个Service类型的Bean
@Service
//注入SOAP绑定类型
@BindingType(SOAPBinding.SOAP11HTTP_BINDING)
//与接口相同注入标识该接口为@WebService类型,定义命名空间的同时定义服务的名称
@WebService(serviceName = "DomainAService", targetNamespace = "http://test.org/ws/DomainAService")
public class DomainAServiceImpl implements DomainAService {
@Override
public String geoModelOne(String inputPath, String outputPath) {
return null;
}
}
图片20:ws-hsm模块的services.xml配置
图片21:启动服务
图片22:通过浏览器查看服务列表
图片23:服务节点的wsdl
1.4、通过SOAPUI测试发布的web service
SOAPUI是一个开源测试工具,通过soap/http来检查、调用、实现Web Service的功能/负载/符合性测试。其能根据WSDL生成SOAP数据包,手工填入参数后可以直接进行性能测试,同时能够捕捉SOAP请求和响应、创建测试请求、测试用例并且提供仿真的服务。
图片24:使用SOAPUI模拟客户端测试wsdl是否正常
至此,单个web service文件测试完毕,完成了地理模型wsdl的实现。
接下来就是怎么把这些单个的wsdl文件拼接起来,实现思路2:构建流程WSDL文件。
1.5、参考资料
1、【IDEA无法访问spring-boot模板网站】
2、【创建springboot多模块项目】
3、【dependencyManagement的用法】
4、【application.yml与application.properties的区别】
5、【application.yml与application-dev.yml的区别】
6、【SpringBoot整合CXF框架开发】
7、【通过java代码执行exe文件】
8、【markdown文件插入图片】
9、【Apache CXF 简介】
10、【用idea创建项目并上传到码云】
11、【SoapUI的使用】