若依框架整合 CXF 实现 WebService 改造流程(后端)

从 Restful 迁移到 SOAP 风格,首当其冲的是数据格式与接口定义差异带来的适配难题。Restful 以 JSON 格式为主,强调资源的轻量化交互,而 SOAP 采用复杂的 XML 协议,需严格遵循 WSDL 规范进行接口描述。

一、环境准备与依赖引入

若依框架本身基于 Spring Boot 构建,而在整合 CXF 时,由于使用的是 Spring 2.5.15 版本,为确保兼容性,只能选用 CXF 3 系列版本。在项目的pom.xml文件中添加以下依赖:

xml 复制代码
<dependency>
    <groupId>org.apache.cxf</groupId>
    <artifactId>cxf-spring-boot-starter-jaxws</artifactId>
    <version>3.x</version> 
</dependency>
<dependency>
    <groupId>org.apache.cxf</groupId>
    <artifactId>cxf-rt-features-logging</artifactId>
    <version>3.x</version>
</dependency>

二、notice 接口改造实践

在若依框架中,notice模块的增删改查接口通常以 Restful 风格实现,通过@GetMapping、@PostMapping等注解映射 HTTP 请求。为减少迁移工作量,可直接复制原有的NoticeController类,通过添加 JAX-WS 相关注解,将其转换为 SOAP 风格的 WebService 接口,同时保留原接口定义和业务逻辑。

2.1 复制并改造NoticeController

将原有的NoticeController类复制一份,重命名为SysNoticeSoapService,删除原有的@RestController、@RequestMapping等 Restful 风格注解,并添加 JAX-WS 注解,同时保留若依框架原有的权限控制、日志记录等逻辑。改造后的代码如下:

java 复制代码
package com.ruoyi.web.soap.system;
import com.ruoyi.common.annotation.Log;
import com.ruoyi.common.annotation.WebServicePath;
import com.ruoyi.common.core.controller.BaseController;
import com.ruoyi.common.core.domain.AjaxResult;
import com.ruoyi.common.core.page.TableDataInfo;
import com.ruoyi.common.enums.BusinessType;
import com.ruoyi.system.domain.SysNotice;
import com.ruoyi.system.service.ISysNoticeService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.stereotype.Component;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import javax.jws.WebParam;
import javax.jws.WebService;
@WebService(targetNamespace = "http://localhost/notice")
@WebServicePath("notice")
@Component("noticeService")
public class SysNoticeSoapService extends BaseController {
    @Autowired
    private ISysNoticeService noticeService;
    // 保留若依框架权限控制注解,确保SOAP接口调用权限与原Restful接口一致
    @PreAuthorize("@ss.hasPermi('system:notice:list')")
    public TableDataInfo selectNoticeList(@Validated @WebParam(name = "notice") SysNotice notice) {
        return getDataTable(noticeService.selectNoticeList(notice));
    }
    @PreAuthorize("@ss.hasPermi('system:notice:query')")
    public AjaxResult selectNoticeById(@WebParam(name = "noticeId") Long noticeId) {
        return success(noticeService.selectNoticeById(noticeId));
    }
    /**
     * 新增通知公告,保留日志记录注解,便于追溯操作
     */
    @PreAuthorize("@ss.hasPermi('system:notice:add')")
    @Log(title = "通知公告", businessType = BusinessType.INSERT)
    public AjaxResult insertNotice(@Validated @WebParam(name = "notice") SysNotice notice) {
        notice.setCreateBy(getUsername());
        return toAjax(noticeService.insertNotice(notice));
    }
    /**
     * 修改通知公告
     */
    @PreAuthorize("@ss.hasPermi('system:notice:edit')")
    @Log(title = "通知公告", businessType = BusinessType.UPDATE)
    public AjaxResult updateNotice(@Validated @WebParam(name = "notice") SysNotice notice) {
        notice.setUpdateBy(getUsername());
        return toAjax(noticeService.updateNotice(notice));
    }
    /**
     * 删除通知公告
     */
    @PreAuthorize("@ss.hasPermi('system:notice:remove')")
    @Log(title = "通知公告", businessType = BusinessType.DELETE)
    public AjaxResult deleteNoticeById(@PathVariable @WebParam(name = "noticeId") long noticeId) {
        return toAjax(noticeService.deleteNoticeById(noticeId));
    }
}

在上述代码中,@WebService注解定义了服务的命名空间;@WebParam注解明确了方法参数在 SOAP 消息中的 XML 表示。同时,代码中保留了若依框架特有的@PreAuthorize权限控制注解和@Log日志记录注解,使得改造后的 SOAP 接口在安全管控和操作审计方面与原 Restful 接口保持一致。此外,@WebServicePath("notice")注解,确保 CXF 能够正确识别并发布服务。

2.2 批量发布配置实现

通过自定义@WebServicePath注解标记需要发布的服务类,并在配置类中扫描所有带有@WebService注解的 Bean,结合@WebServicePath指定的路径实现批量发布。具体代码如下:

ini 复制代码
package com.ruoyi.web.core.config;
import com.ruoyi.common.annotation.WebServicePath;
import org.apache.cxf.bus.spring.SpringBus;
import org.apache.cxf.jaxws.EndpointImpl;
import org.springframework.aop.support.AopUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Bean;
import javax.jws.WebService;
import java.util.Map;
@Configuration
public class CxfConfig {
    @Autowired
    private SpringBus bus;
    @Autowired
    private ApplicationContext applicationContext;
    @Bean
    public void publishAllSoapServices() {
        // 扫描所有带 @WebService 注解的 Bean
        Map<String, Object> webServiceBeans = applicationContext.getBeansWithAnnotation(WebService.class);
        for (Map.Entry<String, Object> entry : webServiceBeans.entrySet()) {
            Object bean = entry.getValue();
            Class<?> beanClass = bean.getClass();
            // 只发布带 @WebServicePath 的 Bean
            Class<?> targetClass = AopUtils.getTargetClass(bean);
            WebServicePath soapPublish = targetClass.getAnnotation(WebServicePath.class);
            if (soapPublish == null) {
                continue;
            }
            String path = soapPublish.value();
            EndpointImpl endpoint = new EndpointImpl(bus, bean);
            endpoint.publish(path);
            System.out.println("已发布SOAP服务: " + path + " -> " + beanClass.getName());
        }
    }
}

2.3 自定义注解WebServicePath的实现

上述批量发布逻辑的核心在于自定义注解WebServicePath,其作用是为 WebService 服务类指定发布路径,实现路径配置与代码的解耦。该注解的代码实现如下:

java 复制代码
package com.ruoyi.common.annotation;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.lang.annotation.ElementType;
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface WebServicePath {
    String value(); // SOAP服务发布路径,如 "/services/${notice}"
}

2.3 实现思路解析

  • 注解标记:通过@WebService注解标识该类为 WebService 服务类,@WebServicePath注解指定服务发布的路径(如notice),实现服务路径与类的解耦。
  • 包扫描:利用 Spring 的ApplicationContext获取所有带有@WebService注解的 Bean,无需手动注入每个服务类,减少配置冗余。
  • 批量发布:遍历扫描到的 Bean,通过AopUtils.getTargetClass获取真实类,判断是否带有@WebServicePath注解,若有则通过EndpointImpl发布服务,发布路径为@WebServicePath注解的值。

三、为何不在服务层处理 WebService 改造

在若依框架的 WebService 改造过程中,选择新增业务层而非直接在服务层进行处理,主要受WSDL 规范约束前后端兼容性需求两方面因素影响。

WSDL作为 WebService 的接口描述标准,对接口类型有严格要求。在 Restful 风格的服务中,接口返回值类型灵活多样,如AjaxResult、TableDataInfo等封装了业务状态和数据的自定义类型。但在 SOAP 服务中,WSDL 要求接口返回值类型必须与定义完全对应,无法像 Restful 那样通过框架灵活转换。若在服务层直接改造,需对原有接口返回值类型进行大量调整,甚至可能需要修改业务逻辑以适配 WSDL 规范,这将大幅增加开发工作量和出错风险。

若依框架前端系统已深度适配 Restful 接口返回的数据格式,如通过AjaxResult解析业务状态码和数据内容。而 SOAP 服务的返回格式与 Restful 存在本质差异,若在服务层改变返回值类型,前端接收的数据格式将完全不同,这意味着前端代码需同步进行大规模重构,包括数据解析、页面渲染逻辑等,无疑会显著延长项目工期。

因此,通过新增SysNoticeSoapService这一业务层,保留原有的接口定义和业务逻辑,仅在该层添加 JAX-WS 注解进行协议转换,既能满足 WSDL 规范要求,又能最大限度减少对原有服务层和前端系统的影响,实现从 Restful 到 SOAP 风格的高效迁移。

相关推荐
好奇的菜鸟14 分钟前
如何在IntelliJ IDEA中设置数据库连接全局共享
java·数据库·intellij-idea
tan180°16 分钟前
MySQL表的操作(3)
linux·数据库·c++·vscode·后端·mysql
DuelCode1 小时前
Windows VMWare Centos Docker部署Springboot 应用实现文件上传返回文件http链接
java·spring boot·mysql·nginx·docker·centos·mybatis
优创学社21 小时前
基于springboot的社区生鲜团购系统
java·spring boot·后端
why技术1 小时前
Stack Overflow,轰然倒下!
前端·人工智能·后端
幽络源小助理1 小时前
SpringBoot基于Mysql的商业辅助决策系统设计与实现
java·vue.js·spring boot·后端·mysql·spring
猴哥源码1 小时前
基于Java+springboot 的车险理赔信息管理系统
java·spring boot
YuTaoShao2 小时前
【LeetCode 热题 100】48. 旋转图像——转置+水平翻转
java·算法·leetcode·职场和发展
ai小鬼头3 小时前
AIStarter如何助力用户与创作者?Stable Diffusion一键管理教程!
后端·架构·github
Dcs3 小时前
超强推理不止“大”——手把手教你部署 Mistral Small 3.2 24B 大模型
java