记录:298
场景:使用Spring Boot应用Apache CXF发布Web Services服务,实现跨系统之间交互接口。
版本:
- JDK 1.8
- Spring Boot 2.6.3
- Apache CXF 3.5.1
名词:
以下摘自官网。
Apache CXF:Apache CXF is an open source services framework. CXF helps you build and develop services using frontend programming APIs, like JAX-WS and JAX-RS. These services can speak a variety of protocols such as SOAP, XML/HTTP, RESTful HTTP, or CORBA and work over a variety of transports such as HTTP, JMS or JBI.
一、基础
1.Apache CXF官网地址
https://cxf.apache.org/
其中,源码下载和相应包下载均在官网中有入口。
2.Apache CXF基础
Apache CXF发布的Web Services服务,一般在跨系统之间交互接口的场景中使用。
实际开发中可以关注以下几点。
(1)服务提供方和服务调用方需约定交互的数据格式
使用普通字符串,比较容易理解,一目了然。
使用XML格式,需要约定XML格式中每个属性值代表什么业务意义。
使用JSON格式,需要约定JSON格式中每个属性值代表什么业务意义。
(2)服务提供方和服务调用方都需解析数据
服务提供方和服务调用方都需开发解析工具去解析数据,推荐约定标准化格式。
本例XML解析使用:jdom2。
本例JSON解析使用:fastjson。
(3)服务提供方需提供Web Services发布地址
服务调用方需要根据发布的Web Services地址去调用服务的具体方法。
本例地址格式举例:
格式:http://主机IP:主机端口/微服务名/CXFServlet拦截的路径/发布服务地址?wsdl
举例:
http://127.0.0.1:18080/example-cxf/WebServices/cityInfoWS?wsdl
二、部署
使用Apache CXF发布的Web Services服务功能,常用方式有以下两种。
方式一:Spring Web应用中集成Apache CXF框架。打包成war包,放入Tomcat容器中运行。
方式二:Spring Web应用中集成Apache CXF框架。打包成可执行jar包,内嵌Tomcat容器中运行。
三、微服务
微服务工程名:example-106-cxf。微服务以Spring Boot 2.6.3为基础构建。
1.微服务example-106-cxf
在微服务example-106-cxf中,集成Apache CXF框架。
1.1创建微服务工程
微服务工程名:example-106-cxf。
1.2.pom.xml依赖
在pom.xml中引入核心依赖。
- <dependency>
- <groupId>org.springframework.bootgroupId>
- <artifactId>spring-bootartifactId>
- <version>2.6.3version>
- dependency>
- <dependency>
- <groupId>org.springframework.bootgroupId>
- <artifactId>spring-boot-starter-webartifactId>
- <version>2.6.3version>
- dependency>
- <dependency>
- <groupId>org.apache.cxfgroupId>
- <artifactId>cxf-spring-boot-starter-jaxwsartifactId>
- <version>3.5.1version>
- dependency>
- <dependency>
- <groupId>com.alibabagroupId>
- <artifactId>fastjsonartifactId>
- <version>1.2.83version>
- dependency>
- <dependency>
- <groupId>org.jdomgroupId>
- <artifactId>jdom2artifactId>
- <version>2.0.6version>
- dependency>
1.3.启动类
包名全路径:com.hub.example。
启动类名称:CxfExampleApplication 。
注解@SpringBootApplication:代表CxfExampleApplication 是微服务启动类。
1.4 WebServices发布实现类
WebServices发布实现类,即WebServices的请求入口实现类。
本例WebServices发布后,对外暴露3个方法。
方法一:入参和返回值都是普通字符串。
方法二:入参和返回值都是XML格式字符串。
方法三:入参和返回值都是JSON格式字符串。
1.4.1接口ICityInformation
包路径:com.hub.example.webservice.ICityInformation
@WebService标记是发布WebService的接口,targetNamespace指定命名空间。
本例指定命名空间:http://www.hub.com
当不指定命名空间时,会生成默认空间,是包名的倒序。
如果本例没有指定,则默认:webservice.example.hub.com
- import javax.jws.WebService;
- @WebService(targetNamespace = "http://www.hub.com")
- public interface ICityInformation {
- /**
- * 入参: 两个普通字符串
- * 返回: 普通字符串
- */
- String getCityInfo(String cityName, String cityNo);
- /**
- * 入参: XML格式字符串
- * 返回: XML格式字符串
- */
- String getCityInfoXml(String xmlStr);
- /**
- * 入参: XML格式字符串
- * 返回: XML格式字符串
- */
- String getCityInfoJson(String jsonStr);
- }
1.4.2实现类CityInformationImpl
类CityInformationImpl实现ICityInformation接口方法。
@Component标记该类需Spring的IOC容器加载。
- import com.hub.example.service.ICityInfoService;
- import com.hub.example.webservice.ICityInformation;
- import lombok.extern.slf4j.Slf4j;
- import org.springframework.beans.factory.annotation.Autowired;
- import org.springframework.stereotype.Component;
- import javax.jws.WebService;
- @Slf4j
- @WebService(targetNamespace = "http://www.hub.com")
- @Component
- public class CityInformationImpl implements ICityInformation {
- @Autowired
- private ICityInfoService cityInfo;
- @Override
- public String getCityInfo(String cityName, String cityNo) {
- log.info("WebService的getCityInfo方法,接收入参,cityName:" + cityName + ",cityNo=" + cityNo);
- String result = cityInfo.getCityInfo(cityName, cityNo);
- log.info("WebService的getCityInfo方法,返回,result:" + result);
- return result;
- }
- @Override
- public String getCityInfoXml(String xmlStr) {
- log.info("WebService的getCityInfoXml方法,接收入参,xmlStr:" + xmlStr);
- String result = cityInfo.getCityInfoXml(xmlStr);
- log.info("WebService的getCityInfoXml方法,返回,result:" + result);
- return result;
- }
- @Override
- public String getCityInfoJson(String jsonStr) {
- log.info("WebService的getCityInfoJson方法,接收入参,jsonStr:" + jsonStr);
- String result = cityInfo.getCityInfoJson(jsonStr);
- log.info("WebService的getCityInfoJson方法,返回,result:" + result);
- return result;
- }
- }
1.5配置类
1.5.1 CXF发布服务的核心配置
CxfConfiguration主要做三件事。
(1)注入CXF框架的org.apache.cxf.Bus接口和注入需要发布实现类对象CityInformationImpl。
(2)在ServletRegistrationBean注册CXFServlet对象,专门拦截WebServices请求。因为这类请求需要交给CXFServlet处理。
(3)发布CXF具体服务和配置服务地址。
1.5.2 CXF发布服务的地址组成
在CxfConfiguration配置类,就能确定需要发布的WebServices全路径。
本例:
http://127.0.0.1:18080/example-cxf/WebServices/cityInfoWS?wsdl
- import com.hub.example.webservice.impl.CityInformationImpl;
- import org.apache.cxf.Bus;
- import org.apache.cxf.jaxws.EndpointImpl;
- import org.apache.cxf.transport.servlet.CXFServlet;
- import org.springframework.beans.factory.annotation.Autowired;
- 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 CxfConfiguration {
- @Autowired
- private Bus bus;
- /**
- * 注入对外提供服务的实现类
- */
- @Autowired
- private CityInformationImpl cityInformationImpl;
- /**
- * 配置CXFServlet,拦截/WebServices请求
- * 注意: /WebServices作为http请求url前缀专门给CXFServlet使用
- **/
- @Bean
- public ServletRegistrationBean
configCXFServlet() { - return new ServletRegistrationBean
(new CXFServlet(), "/WebServices/*"); - }
- /**
- * 发布CXF服务,使用org.apache.cxf.jaxws包的EndpointImpl发布
- */
- @Bean
- public Endpoint endpointCityInfo() {
- EndpointImpl endpoint = new EndpointImpl(bus, cityInformationImpl);
- endpoint.publish("/cityInfoWS");
- return endpoint;
- }
- }
1.6业务类
1.6.1业务Service类
包名全路径:com.hub.example.service。
接口:com.hub.example.service.ICityInfoService
实现类:com.hub.example.service.impl.CityInfoServiceImpl
笔者习惯,在WebServices发布类中,只做跟发布类相关的事情,业务实现内容独立成类,注入到WebServices实现类中。提升代码简洁和可读性。
1.6.1.1接口ICityInfoService
接口ICityInfoService。
- public interface ICityInfoService {
- String getCityInfo(String cityName, String cityNo);
- String getCityInfoXml(String xmlStr);
- String getCityInfoJson(String jsonStr);
- }
1.6.1.2实现类CityInfoServiceImpl
在CityInfoServiceImpl实现类,对三个方法具体实现。
方法getCityInfo:处理普通字符串。
方法getCityInfoXml:处理XML字符串。
方法getCityInfoJson:处理Json字符串。
- @Slf4j
- @Service
- public class CityInfoServiceImpl implements ICityInfoService {
- @Override
- public String getCityInfo(String cityName, String cityNo) {
- String uuid = UUID.randomUUID().toString().toUpperCase().replace("-", "");
- return cityName + ":" + cityNo + ":" + uuid;
- }
- @Override
- public String getCityInfoXml(String xmlStr) {
- Map
result = XlmUtils.parseXml(xmlStr); - String jsonStr = JSON.toJSONString(result);
- CityBO cityBO = JSONObject.parseObject(jsonStr, CityBO.class);
- String cityName = cityBO.getCityName();
- log.info("正在生成地区: " + cityName + "的UUID信息.");
- String uuid = UUID.randomUUID().toString().toUpperCase().replace("-", "");
- return XlmUtils.buildXml("0", "执行成功", uuid);
- }
- @Override
- public String getCityInfoJson(String jsonStr) {
- CityBO cityBO = JSONObject.parseObject(jsonStr, CityBO.class);
- ResultMap resultMap = new ResultMap();
- resultMap.put("cityName", cityBO.getCityName());
- resultMap.put("cityNo", cityBO.getCityNo());
- String uuid = UUID.randomUUID().toString().toUpperCase().replace("-", "");
- resultMap.put("randomUUID", uuid);
- return JSON.toJSONString(resultMap);
- }
- }
1.7工具类XlmUtils
工具类XlmUtils,辅助解析XML和生成XML。
- public class XlmUtils {
- public static Map
parseXml(String xmlStr) { - if (xmlStr == null) return null;
- if (!Objects.equals("", xmlStr)) {
- // 对xmlStr做首尾多余空格处理
- xmlStr = xmlStr.trim();
- }
- StringReader sr = new StringReader(xmlStr);
- InputSource input = new InputSource(sr);
- SAXBuilder sb = new SAXBuilder();
- Map
result = new HashMap<>(); - try {
- Document doc = sb.build(input);
- //data元素
- Element dataElement = doc.getRootElement();
- //row元素(所有row),一个data元素包含多个row元素
- List
rowElementList = dataElement.getChildren(); - for (Element rowElement : rowElementList) {
- //一个row元素包含多个column元素
- List
columnElementList = rowElement.getChildren(); - for (Element columnElement : columnElementList) {
- //从遍历出的column元素中取值
- String key = columnElement.getAttributeValue("Name").trim();
- String value = columnElement.getText() == null ? "" : columnElement.getText().trim();
- result.put(key, value);
- }
- }
- } catch (Exception e) {
- e.printStackTrace();
- }
- return result;
- }
- public static String buildXml(String rtnCode, String rtnMsg, String uuid) {
- String xml = "\n" +
- "\n" +
- "
\n"
+ - "
" + rtnCode + "\n" + - "
" + rtnMsg + "\n" + - "
" + uuid + "\n" + - " \n" +
- "";
- return xml;
- }
- }
1.8配置文件
在\src\main\resources\目录下添加application.yml。
1.8.1 application.yml
application.yml内容如下:
- server:
- port: 18080
- servlet:
- context-path: /example-cxf
2.实体包example-200-entity
微服务使用的实体对象均集中放在example-200-entity模块中,每个微服务只需在pom.xml中引用此模块依赖即可。
2.1引用实体包
引用实体包。
- <dependency>
- <groupId>com.hubgroupId>
- <artifactId>example-200-entityartifactId>
- <version>${hub.example.version}version>
- dependency>
2.2实体类
本例使用实体包括CityVO、CityBO、ResultMap。
2.2.1 CityVO类
CityVO,使用在对http请求入参的JSON格式数据封装。
- import lombok.Data;
- @Data
- public class CityVO {
- private String cityName;
- private String cityNo;
- }
2.2.2 CityBO类
CityBO,使用在对http请求入参的JSON格式数据封装。
- import lombok.Data;
- @Data
- public class CityBO {
- private String cityName;
- private String cityNo;
- }
2.2.3 ResultMap类
ResultMap使用在对http请求结果包装。可以便利的转换为JSON数据。
- public class ResultMap extends HashMap
{ - public ResultMap() {
- put("code", 0);
- put("message", "success");
- }
- public static ResultMap error(int code, String msg) {
- ResultMap resultMap = new ResultMap();
- resultMap.put("code", code);
- resultMap.put("msg", msg);
- return resultMap;
- }
- }
四、应用
1.验证微服务example-106-cxf
WS地址:
http://127.0.0.1:18080/example-cxf/WebServices/cityInfoWS?wsdl
当服务启动
1.1验证
1.1.1工具
本例使用SoapUI工具,发起请求。也可以使用其它工具,或者直接写一个客户端。
1.1.2判断服务发布成功(在浏览器中)
在Tomcat容器启动成功后,可以把WebServices地址直接在浏览器访问一下,能生成如下信息则发布成功(本例)。
在页面中可以看到服务端发布的具体方法、命名空间等信息。

1.1.3判断服务发布成功(在Linux操作系统中)
在生产环境中,服务一般部署在Linux操作系统中,可以使用curl指令验证,同样也会返回如上截图信息。
命令:
curl http://127.0.0.1:18080/example-cxf/WebServices/cityInfoWS?wsdl
1.1.4在SoapUI工具查看发布的方法
在SoapUI工具查看发布的方法列表。

1.2验证getCityInfo方法
1.2.1入参
参数0:杭州西湖
参数1:310012
1.2.2返回值
返回值:杭州西湖区:310012:19F011E63F9C4A069C205E4456AB676F
1.2.3结果

1.3验证getCityInfoXml方法
1.3.1入参
- "1.0" encoding="UTF-8"?>
- <data>
- <row>
- <column Name="cityName">杭州西湖区column>
- <column Name="cityNo">310012column>
- row>
- data>
1.3.2返回值
- "1.0" encoding="UTF-8"?>
- <data>
- <row>
- <column Name="code">0column>
- <column Name="message">执行成功column>
- <column Name="randomUUID">04A09D40FDDA418A91D3728A97D9621Ccolumn>
- row>
- data>
1.3.3结果
使用XML时,XML字符串需要使用包裹起来。

1.4验证getCityInfoJson方法
1.4.1入参
- {
- "cityName":"杭州西湖区",
- "cityNo":"310012"
- }
1.4.2返回值
- {
- "cityNo": "310012",
- "code": 0,
- "cityName": "杭州西湖区",
- "randomUUID": "8CF23D119D88457EB3C73670C774CB7F",
- "message": "success"
- }
1.4.3结果

以上,感谢。
2022年10月19日