Webservic 服务注册发布及参数封装-实际项目应用

1.需要用的java环境变量的rt.jar 中xml工具;
2.cxf包,主要是针对webservice的发布;

1.首先引入依赖:

java 复制代码
<dependency>
	<groupId>org.apache.cxf</groupId>
	<artifactId>cxf-spring-boot-starter-jaxws</artifactId>
	<version>3.5.6<version>
</dependency>

2.创建cxf的配置类:

java 复制代码
package com.zfsw.spzx.openapi.service.web.config;


import com.zfsw.spzx.openapi.service.XXXXWebService;
import org.apache.cxf.Bus;
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 org.springframework.context.annotation.DependsOn;

import javax.annotation.Resource;
import javax.xml.ws.Endpoint;

@Configuration
@DependsOn("coreApplicationContextAware")
public class CxfConfig {

    @Resource
    private Bus cxfBus;

    @Resource
    private XXXXWebService webService;

    /**
     * 1. 注册CXF Servlet(替代web.xml配置)
     */
    @Bean
    public ServletRegistrationBean<CXFServlet> cxfServlet() {
        ServletRegistrationBean<CXFServlet> servlet = new ServletRegistrationBean<>(new CXFServlet(), "/ws/*");
        servlet.setLoadOnStartup(1);
        return servlet;
    }


    /**
     * 2. 发布WebService端点
     */
    @Bean
    public Endpoint userServiceEndpoint() {
        EndpointImpl endpoint = new EndpointImpl(cxfBus, webService);
        endpoint.publish("/xxxxService");
        return endpoint;
    }
}

3.入口方法:

java 复制代码
@RequestMapping(path = "/getKPIValue", method = RequestMethod.POST, consumes = {"application/xml"}, produces = {"application/xml"})
@ApiOperation(value = "XXXX-运行指标统计业务")
@PassToken
@PassResultAdvice
public Object getKPIValue(@RequestBody String param) throws JAXBException, UnsupportedEncodingException {
	log.info("XXXX系统请求参数:{}", param);
	return xxxxWebService.getKPIValue(param);
}

4.xml解析类工具

java 复制代码
package com.zfsw.spzx.openapi.service.util;

import com.zfsw.spzx.openapi.service.dto.XXXXInfosReq;
import com.zfsw.spzx.openapi.service.dto.XXXXInfosResp;
import org.springframework.util.StringUtils;

import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Marshaller;
import javax.xml.bind.Unmarshaller;
import java.io.StringReader;
import java.io.StringWriter;
import java.io.UnsupportedEncodingException;
import java.util.HashSet;
import java.util.Set;

public class JaxbXmlParser {
    private static final Set<Class<?>> REGISTERED_CLASSES = new HashSet<>();
    private static JAXBContext globalContext;
    static {
        // 预注册常见类
        registerClass(XXXXInfosReq.class);
        registerClass(XXXXInfosReq.Api.class);
        registerClass(XXXXInfosResp.class);
        registerClass(XXXXInfosResp.ApiData.class);
        registerClass(XXXXInfosResp.Corporation.class);
    }
    /**
     * 注册JAXB类(线程安全)
     */
    public static synchronized void registerClass(Class<?> clazz) {
        if (REGISTERED_CLASSES.add(clazz)) {
            globalContext = null;
        }
    }

    /**
     * 批量注册类
     */
    public static void registerClasses(Class<?>... classes) {
        for (Class<?> clazz : classes) {
            registerClass(clazz);
        }
    }

    /**
     * 获取或创建JAXBContext(包含所有注册的类)
     */
    private static synchronized JAXBContext getGlobalContext() throws JAXBException {
        if (globalContext == null) {
            globalContext = JAXBContext.newInstance(
                    REGISTERED_CLASSES.toArray(new Class<?>[0])
            );
        }
        return globalContext;
    }

    /**
     * 获取XML声明中的编码信息
     */
    public static String getEncodingFromXmlDeclaration(String xml) {
        if (xml == null) {
            return null;
        }
        // 匹配XML声明中的编码属性
        java.util.regex.Pattern pattern = java.util.regex.Pattern.compile(
                "<\\?xml[^>]*encoding=[\"']([^\"']+)[\"']");
        java.util.regex.Matcher matcher = pattern.matcher(xml);

        if (matcher.find()) {
            return matcher.group(1);
        }

        return null; // 没有找到编码声明
    }

    /**
     * 核心方法:清理 XML 字符串
     * @param xmlStr 原始 XML 字符串
     * @return 清理后的 XML 字符串(无首尾空白、无注释、标签间空白简化)
     */
    public static String cleanXml(String xmlStr) {
        if (!StringUtils.hasText(xmlStr)) {
            return "";
        }

        // 步骤1:移除所有 XML 注释(<!-- 任意内容 -->,包括多行注释)
        String noCommentXml = removeXmlComments(xmlStr);

        // 步骤2:清理首尾所有空白字符(换行、空格、制表符、回车等)
        String trimXml = trimAllWhitespace(noCommentXml);

        // 步骤3:可选:清理标签之间的多余空白(保留标签内文本/属性的空白)

        return simplifyWhitespaceBetweenTags(trimXml);
    }

    /**
     * 移除 XML 注释(<!-- ... -->)
     * 正则说明:
     * <!--    匹配注释开始
     * [\\s\\S]*?  匹配任意字符(\\s=空白,\\S=非空白,*?=非贪婪匹配,避免匹配到最后一个-->)
     * -->      匹配注释结束
     */
    private static String removeXmlComments(String xmlStr) {
        // 正则匹配所有 XML 注释(包括多行)
        String commentRegex = "<!--[\\s\\S]*?-->";
        return xmlStr.replaceAll(commentRegex, "");
    }

    /**
     * 清理字符串首尾的所有空白字符(换行、回车、制表符、空格等)
     * 比 String.trim() 更彻底(trim() 仅处理半角空格、\n、\r)
     */
    private static String trimAllWhitespace(String xmlStr) {
        if (xmlStr == null) {
            return null;
        }
        int len = xmlStr.length();
        int st = 0;
        char[] val = xmlStr.toCharArray();
        // 跳过开头的空白字符
        while ((st < len) && Character.isWhitespace(val[st])) {
            st++;
        }
        // 跳过结尾的空白字符
        while ((st < len) && Character.isWhitespace(val[len - 1])) {
            len--;
        }
        return ((st > 0) || (len < xmlStr.length())) ? xmlStr.substring(st, len) : xmlStr;
    }

    /**
     * 简化标签之间的多余空白(仅保留单个空格,避免换行/多空格干扰)
     * 说明:不修改标签内的文本/属性空白(如 <Time> 2025-11-11 11:11:11 </Time> 内的空格保留)
     * 正则说明:
     * >           匹配标签结束符
     * \\s+        匹配一个或多个空白字符(换行/空格/制表符)
     * <           匹配标签开始符
     * 替换为 > <   保留标签间单个空格
     */
    private static String simplifyWhitespaceBetweenTags(String xmlStr) {
        String whitespaceRegex = ">(\\s+)<";
        return xmlStr.replaceAll(whitespaceRegex, "><");
    }

    /**
     * 将XML字符串解析为对象
     */
    public static <T> T parseXmlToObject(String xmlString, Class<T> clazz) throws JAXBException, UnsupportedEncodingException {
        // 注册目标类
        registerClass(clazz);
        JAXBContext jaxbContext = getGlobalContext();
        Unmarshaller unmarshaller = jaxbContext.createUnmarshaller();
        String originalEncoding = getEncodingFromXmlDeclaration(xmlString);
        String realXmlString = cleanXml(xmlString);
        if (StringUtils.hasText(originalEncoding)) {
            // 修改编码声明
            realXmlString = new String(realXmlString.getBytes(originalEncoding),originalEncoding);
        }
        StringReader reader = new StringReader(realXmlString);
        return clazz.cast(unmarshaller.unmarshal(reader));
    }

    /**
     * 将对象序列化为XML字符串
     */
    public static String objectToXml(Object obj,String encoding) throws JAXBException {
        JAXBContext jaxbContext = getGlobalContext();
        Marshaller marshaller = jaxbContext.createMarshaller();
        if(StringUtils.hasText(encoding)){
            // 设置编码
            marshaller.setProperty(Marshaller.JAXB_ENCODING, encoding);
        }
        // 格式化输出
        marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);

        StringWriter writer = new StringWriter();
        marshaller.marshal(obj, writer);
        return writer.toString();
    }
}

5.入参:

1)标准请求参数(示例)

java 复制代码
<?xml version="1.0" encoding="gb2312"?>
<info>
<!--传入的地域代码可以为一个或多个-->
	<CorporationCode>地域代码1,地域代码2,地域代码3....</CorporationCode>
<!--传入的指标名称可以为一个或多个--> 
<Time>时间值</Time>
<api name="指标名称1"></api>
<api name="指标名称2"></api>	
<api name="指标名称n"></api>
</info>

2)自定入参数:

java 复制代码
@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
@XmlRootElement(name = "info")
@XmlAccessorType(XmlAccessType.FIELD)
public class XXXXInfosReq {
    @XmlElement(name = "CorporationCode")
    private String corporationCodes;
    @XmlElement(name = "Time")
    private String time;
    @XmlElement(name = "api")
    private List<Api> apis;

    private LocalDateTime dateTime;
    
    @XmlAccessorType(XmlAccessType.FIELD)
    @Data
    @AllArgsConstructor
    @NoArgsConstructor
    public static class Api {
        @XmlAttribute(name = "name")
        private String name;
    }
}

6.出参

1).标准返回参数(示例)

java 复制代码
<?xml version="1.0" encoding="gb2312"?>
<return>
	<status>success/failure</status>
	<message>执行的结果提示</message>
	<!-- 若status的值为failure,reason节点才存在 -->
	<reason>出错的原因</reason>
<Corporation id="地域代码1">
<api name="指标名称1">
    <value>xx</value>
</api>
<api name="指标名称2">
    <value>xx</value>
</api>	
<api name="指标名称n">
    <value>xx</value>
</api>
</Corporation>
<Corporation id="地域代码2">
<api name="指标名称1">
    <value>xx</value>
</api>
<api name="指标名称2">
    <value>xx</value>
</api>	
<api name="指标名称n">
    <value>xx</value>
</api>
</Corporation>
...
<Corporation id="地域代码N">
<api name="指标名称1">
    <value>xx</value>
</api>
<api name="指标名称2">
    <value>xx</value>
</api>	
<api name="指标名称n">
    <value>xx</value>
</api>
</Corporation>
</return>

2)自定义返参数:

java 复制代码
@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
@XmlRootElement(name = "return")
@XmlAccessorType(XmlAccessType.FIELD)
public class XXXXInfosResp {
    public static final String SUCCESS_STATUS = "success";
    public static final String FAIL_STATUS = "fail";
    @XmlElement(name = "status")
    private String status;

    @XmlElement(name = "message")
    private String message;

    @XmlElement(name = "reason")
    private String reason;

    @XmlElement(name = "Corporation")
    private List<Corporation> corporations;


    @XmlAccessorType(XmlAccessType.FIELD)
    @Data
    @Builder
    @AllArgsConstructor
    @NoArgsConstructor
    public static class Corporation {
        @XmlAttribute(name = "id")
        private String id;

        @XmlElement(name = "api")
        private List<ApiData> apis;
    }

    @XmlAccessorType(XmlAccessType.FIELD)
    @Data
    @Builder
    @AllArgsConstructor
    @NoArgsConstructor
    public static class ApiData  {
        @XmlAttribute(name = "name")
        private String name;

        @XmlElement(name = "value")
        private String value;
    }
}

7.访问ws服务地址:

http://127.0.0.1:62380/ws/xxxxService?wsdl

如下图,表示服务端发布成功:

请求接口地址:

http://127.0.0.1:62380/v1/getKPIValue

返回如下图,则表示成功:

相关推荐
BIBI20492 分钟前
Windows 上配置 Nacos Server 3.x.x 使用 MySQL 5.7
java·windows·spring boot·后端·mysql·nacos·配置
一雨方知深秋5 分钟前
面向对象高级语法 1-- 继承、多态
java·方法重写·继承extends·子类构造器调用父类构造器·兄弟构造器this·对象、行为多态·解耦合父类变量为形参接子类对象
月明长歌9 分钟前
【码道初阶】Leetcode771 宝石与石头:Set 判成员 vs List 判成员(同题两种写法的差距)
java·数据结构·leetcode·list·哈希算法·散列表
xiaoyustudiowww10 分钟前
Jakarta EE 12(JAVA EE12)平台包含规范版本
java·java-ee
wniuniu_10 分钟前
ceph的参数
java·数据库·ceph
AC赳赳老秦16 分钟前
DeepSeek-Coder vs Copilot:嵌入式开发场景适配性对比实战
java·前端·后端·struts·mongodb·copilot·deepseek
Coder_Boy_21 分钟前
基于SpringAI的智能平台基座开发-(十一)
人工智能·spring·langchain·langchain4j
Oxye23 分钟前
服务器内存不足导致程序没完全起起来,报错Required type must not be null
java·开发语言
2501_9167665425 分钟前
【Java】static关键字与类的加载顺序
java·开发语言
9号达人29 分钟前
支付配置时好时坏?异步方法里的对象引用坑
java·后端·面试