• 基于Spring Boot应用Apache CXF发布Web Services服务


    记录:298

    场景:使用Spring Boot应用Apache CXF发布Web Services服务,实现跨系统之间交互接口。

    版本:

    1. JDK 1.8
    2. Spring Boot 2.6.3
    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中引入核心依赖。

    1. <dependency>
    2. <groupId>org.springframework.bootgroupId>
    3. <artifactId>spring-bootartifactId>
    4. <version>2.6.3version>
    5. dependency>
    6. <dependency>
    7. <groupId>org.springframework.bootgroupId>
    8. <artifactId>spring-boot-starter-webartifactId>
    9. <version>2.6.3version>
    10. dependency>
    11. <dependency>
    12. <groupId>org.apache.cxfgroupId>
    13. <artifactId>cxf-spring-boot-starter-jaxwsartifactId>
    14. <version>3.5.1version>
    15. dependency>
    16. <dependency>
    17. <groupId>com.alibabagroupId>
    18. <artifactId>fastjsonartifactId>
    19. <version>1.2.83version>
    20. dependency>
    21. <dependency>
    22. <groupId>org.jdomgroupId>
    23. <artifactId>jdom2artifactId>
    24. <version>2.0.6version>
    25. 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

    1. import javax.jws.WebService;
    2. @WebService(targetNamespace = "http://www.hub.com")
    3. public interface ICityInformation {
    4. /**
    5. * 入参: 两个普通字符串
    6. * 返回: 普通字符串
    7. */
    8. String getCityInfo(String cityName, String cityNo);
    9. /**
    10. * 入参: XML格式字符串
    11. * 返回: XML格式字符串
    12. */
    13. String getCityInfoXml(String xmlStr);
    14. /**
    15. * 入参: XML格式字符串
    16. * 返回: XML格式字符串
    17. */
    18. String getCityInfoJson(String jsonStr);
    19. }

    1.4.2实现类CityInformationImpl

    类CityInformationImpl实现ICityInformation接口方法。

    @Component标记该类需Spring的IOC容器加载。

    1. import com.hub.example.service.ICityInfoService;
    2. import com.hub.example.webservice.ICityInformation;
    3. import lombok.extern.slf4j.Slf4j;
    4. import org.springframework.beans.factory.annotation.Autowired;
    5. import org.springframework.stereotype.Component;
    6. import javax.jws.WebService;
    7. @Slf4j
    8. @WebService(targetNamespace = "http://www.hub.com")
    9. @Component
    10. public class CityInformationImpl implements ICityInformation {
    11. @Autowired
    12. private ICityInfoService cityInfo;
    13. @Override
    14. public String getCityInfo(String cityName, String cityNo) {
    15. log.info("WebService的getCityInfo方法,接收入参,cityName:" + cityName + ",cityNo=" + cityNo);
    16. String result = cityInfo.getCityInfo(cityName, cityNo);
    17. log.info("WebService的getCityInfo方法,返回,result:" + result);
    18. return result;
    19. }
    20. @Override
    21. public String getCityInfoXml(String xmlStr) {
    22. log.info("WebService的getCityInfoXml方法,接收入参,xmlStr:" + xmlStr);
    23. String result = cityInfo.getCityInfoXml(xmlStr);
    24. log.info("WebService的getCityInfoXml方法,返回,result:" + result);
    25. return result;
    26. }
    27. @Override
    28. public String getCityInfoJson(String jsonStr) {
    29. log.info("WebService的getCityInfoJson方法,接收入参,jsonStr:" + jsonStr);
    30. String result = cityInfo.getCityInfoJson(jsonStr);
    31. log.info("WebService的getCityInfoJson方法,返回,result:" + result);
    32. return result;
    33. }
    34. }

    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
    1. import com.hub.example.webservice.impl.CityInformationImpl;
    2. import org.apache.cxf.Bus;
    3. import org.apache.cxf.jaxws.EndpointImpl;
    4. import org.apache.cxf.transport.servlet.CXFServlet;
    5. import org.springframework.beans.factory.annotation.Autowired;
    6. import org.springframework.boot.web.servlet.ServletRegistrationBean;
    7. import org.springframework.context.annotation.Bean;
    8. import org.springframework.context.annotation.Configuration;
    9. import javax.xml.ws.Endpoint;
    10. @Configuration
    11. public class CxfConfiguration {
    12. @Autowired
    13. private Bus bus;
    14. /**
    15. * 注入对外提供服务的实现类
    16. */
    17. @Autowired
    18. private CityInformationImpl cityInformationImpl;
    19. /**
    20. * 配置CXFServlet,拦截/WebServices请求
    21. * 注意: /WebServices作为http请求url前缀专门给CXFServlet使用
    22. **/
    23. @Bean
    24. public ServletRegistrationBean configCXFServlet() {
    25. return new ServletRegistrationBean(new CXFServlet(), "/WebServices/*");
    26. }
    27. /**
    28. * 发布CXF服务,使用org.apache.cxf.jaxws包的EndpointImpl发布
    29. */
    30. @Bean
    31. public Endpoint endpointCityInfo() {
    32. EndpointImpl endpoint = new EndpointImpl(bus, cityInformationImpl);
    33. endpoint.publish("/cityInfoWS");
    34. return endpoint;
    35. }
    36. }

    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。

    1. public interface ICityInfoService {
    2. String getCityInfo(String cityName, String cityNo);
    3. String getCityInfoXml(String xmlStr);
    4. String getCityInfoJson(String jsonStr);
    5. }

    1.6.1.2实现类CityInfoServiceImpl

    在CityInfoServiceImpl实现类,对三个方法具体实现。

    方法getCityInfo:处理普通字符串。

    方法getCityInfoXml:处理XML字符串。

    方法getCityInfoJson:处理Json字符串。

    1. @Slf4j
    2. @Service
    3. public class CityInfoServiceImpl implements ICityInfoService {
    4. @Override
    5. public String getCityInfo(String cityName, String cityNo) {
    6. String uuid = UUID.randomUUID().toString().toUpperCase().replace("-", "");
    7. return cityName + ":" + cityNo + ":" + uuid;
    8. }
    9. @Override
    10. public String getCityInfoXml(String xmlStr) {
    11. Map result = XlmUtils.parseXml(xmlStr);
    12. String jsonStr = JSON.toJSONString(result);
    13. CityBO cityBO = JSONObject.parseObject(jsonStr, CityBO.class);
    14. String cityName = cityBO.getCityName();
    15. log.info("正在生成地区: " + cityName + "的UUID信息.");
    16. String uuid = UUID.randomUUID().toString().toUpperCase().replace("-", "");
    17. return XlmUtils.buildXml("0", "执行成功", uuid);
    18. }
    19. @Override
    20. public String getCityInfoJson(String jsonStr) {
    21. CityBO cityBO = JSONObject.parseObject(jsonStr, CityBO.class);
    22. ResultMap resultMap = new ResultMap();
    23. resultMap.put("cityName", cityBO.getCityName());
    24. resultMap.put("cityNo", cityBO.getCityNo());
    25. String uuid = UUID.randomUUID().toString().toUpperCase().replace("-", "");
    26. resultMap.put("randomUUID", uuid);
    27. return JSON.toJSONString(resultMap);
    28. }
    29. }

    1.7工具类XlmUtils

    工具类XlmUtils,辅助解析XML和生成XML。

    1. public class XlmUtils {
    2. public static Map parseXml(String xmlStr) {
    3. if (xmlStr == null) return null;
    4. if (!Objects.equals("", xmlStr)) {
    5. // 对xmlStr做首尾多余空格处理
    6. xmlStr = xmlStr.trim();
    7. }
    8. StringReader sr = new StringReader(xmlStr);
    9. InputSource input = new InputSource(sr);
    10. SAXBuilder sb = new SAXBuilder();
    11. Map result = new HashMap<>();
    12. try {
    13. Document doc = sb.build(input);
    14. //data元素
    15. Element dataElement = doc.getRootElement();
    16. //row元素(所有row),一个data元素包含多个row元素
    17. List rowElementList = dataElement.getChildren();
    18. for (Element rowElement : rowElementList) {
    19. //一个row元素包含多个column元素
    20. List columnElementList = rowElement.getChildren();
    21. for (Element columnElement : columnElementList) {
    22. //从遍历出的column元素中取值
    23. String key = columnElement.getAttributeValue("Name").trim();
    24. String value = columnElement.getText() == null ? "" : columnElement.getText().trim();
    25. result.put(key, value);
    26. }
    27. }
    28. } catch (Exception e) {
    29. e.printStackTrace();
    30. }
    31. return result;
    32. }
    33. public static String buildXml(String rtnCode, String rtnMsg, String uuid) {
    34. String xml = "\n" +
    35. "\n" +
    36. " \n" +
    37. " " + rtnCode + "\n" +
    38. " " + rtnMsg + "\n" +
    39. " " + uuid + "\n" +
    40. " \n" +
    41. "";
    42. return xml;
    43. }
    44. }

    1.8配置文件

    在\src\main\resources\目录下添加application.yml。

    1.8.1 application.yml

    application.yml内容如下:

    1. server:
    2. port: 18080
    3. servlet:
    4. context-path: /example-cxf

    2.实体包example-200-entity

    微服务使用的实体对象均集中放在example-200-entity模块中,每个微服务只需在pom.xml中引用此模块依赖即可。

    2.1引用实体包

    引用实体包。

    1. <dependency>
    2. <groupId>com.hubgroupId>
    3. <artifactId>example-200-entityartifactId>
    4. <version>${hub.example.version}version>
    5. dependency>

    2.2实体类

    本例使用实体包括CityVO、CityBO、ResultMap。

    2.2.1 CityVO类

    CityVO,使用在对http请求入参的JSON格式数据封装。

    1. import lombok.Data;
    2. @Data
    3. public class CityVO {
    4. private String cityName;
    5. private String cityNo;
    6. }

    2.2.2 CityBO类

    CityBO,使用在对http请求入参的JSON格式数据封装。

    1. import lombok.Data;
    2. @Data
    3. public class CityBO {
    4. private String cityName;
    5. private String cityNo;
    6. }

    2.2.3 ResultMap类

    ResultMap使用在对http请求结果包装。可以便利的转换为JSON数据。

    1. public class ResultMap extends HashMap {
    2. public ResultMap() {
    3. put("code", 0);
    4. put("message", "success");
    5. }
    6. public static ResultMap error(int code, String msg) {
    7. ResultMap resultMap = new ResultMap();
    8. resultMap.put("code", code);
    9. resultMap.put("msg", msg);
    10. return resultMap;
    11. }
    12. }

    应用

    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. "1.0" encoding="UTF-8"?>
    2. <data>
    3. <row>
    4. <column Name="cityName">杭州西湖区column>
    5. <column Name="cityNo">310012column>
    6. row>
    7. data>

     1.3.2返回值

    1. "1.0" encoding="UTF-8"?>
    2. <data>
    3. <row>
    4. <column Name="code">0column>
    5. <column Name="message">执行成功column>
    6. <column Name="randomUUID">04A09D40FDDA418A91D3728A97D9621Ccolumn>
    7. row>
    8. data>

    1.3.3结果

    使用XML时,XML字符串需要使用包裹起来。

    1.4验证getCityInfoJson方法

    1.4.1入参

    1. {
    2. "cityName":"杭州西湖区",
    3. "cityNo":"310012"
    4. }

    1.4.2返回值

    1. {
    2. "cityNo": "310012",
    3. "code": 0,
    4. "cityName": "杭州西湖区",
    5. "randomUUID": "8CF23D119D88457EB3C73670C774CB7F",
    6. "message": "success"
    7. }

     1.4.3结果

     以上,感谢。

    2022年10月19日

  • 相关阅读:
    DELMIA弧焊虚拟仿真:带变位机的机器人弧焊焊接程序自动生成方法
    FigDraw 12. SCI 文章绘图之相关性矩阵图(Correlation Matrix)
    【算法题解】2022河南萌新联赛第(三)场:河南大学
    使用OpenSSL的反弹shell
    Excel怎么转换成PDF?这两招轻松解决
    Java 9 的模块(Module)系统
    STM32启动模式详解
    <计算机网络自顶向下> Internet Protocol(未完成)
    QFluentWidgets: 基于 C++ Qt 的 Fluent Design 组件库
    iOS 修改文字大小以适配lable高度宽度
  • 原文地址:https://blog.csdn.net/zhangbeizhen18/article/details/127417394