当前位置: 代码网 > it编程>编程语言>Java > SpringBoot集成WebService(wsdl)实践

SpringBoot集成WebService(wsdl)实践

2025年09月24日 Java 我要评论
pom.xml <dependency> <groupid>com.fasterxml.jackson.dataformat</gro

pom.xml

        <dependency>
            <groupid>com.fasterxml.jackson.dataformat</groupid>
            <artifactid>jackson-dataformat-xml</artifactid>
        </dependency>
        <dependency>
            <groupid>org.apache.cxf</groupid>
            <artifactid>cxf-spring-boot-starter-jaxws</artifactid>
            <!-- 对版本没要求,建议跟我一样 -->
            <version>3.4.4</version>
        </dependency>

创建入口

applicationcontextutils.java

bean调用工具

import org.springframework.beans.beansexception;
import org.springframework.context.applicationcontext;
import org.springframework.context.applicationcontextaware;
import org.springframework.stereotype.component;

/**
 * 创建日期:2024-07-01
 */
@component
public class applicationcontextutils implements applicationcontextaware {
    //构造函数私有化,防止其它人实例化该对象
    private applicationcontextutils() {
    }

    private static applicationcontext applicationcontext;

    @override
    public void setapplicationcontext(applicationcontext applicationcontext) throws beansexception {
        applicationcontextutils.applicationcontext = applicationcontext;
    }

    //通过name获取 bean.(推荐,因为bean的name是唯一的,出现重名的bean启动会报错。)
    public static object getbean(string name) {
        return applicationcontext.getbean(name);
    }

    //通过class获取bean.(确保bean的name不会重复。因为可能会出现在不同包的同名bean导致获取到2个实例)
    public static <t> t getbean(class<t> clazz) {
        return applicationcontext.getbean(clazz);
    }

    //通过name,以及clazz返回指定的bean(这个是最稳妥的)
    public static <t> t getbean(string name, class<t> clazz) {
        return applicationcontext.getbean(name, clazz);
    }

    public static <t> map<string, t> getbeansoftype(class<t> clazz) {
        return applicationcontext.getbeansoftype(clazz);
    }

}

jacksonutils.java

jackson 工具类

import com.fasterxml.jackson.annotation.jsoninclude;
import com.fasterxml.jackson.databind.deserializationfeature;
import com.fasterxml.jackson.databind.objectmapper;
import com.fasterxml.jackson.databind.serializationfeature;
import com.fasterxml.jackson.dataformat.xml.xmlmapper;
import com.fasterxml.jackson.datatype.jsr310.javatimemodule;

/**
 * jackson 工具类
 */
public abstract class jacksonutils {
    public static final objectmapper json = new objectmapper();
    public static final objectmapper xml = new xmlmapper();

    static {
        // json 配置
        json.setserializationinclusion(jsoninclude.include.non_null);
        json.disable(serializationfeature.fail_on_empty_beans);
        json.disable(deserializationfeature.fail_on_unknown_properties);
        json.registermodule(new javatimemodule());//处理java8新日期时间类型
        // xml 配置
        xml.setserializationinclusion(jsoninclude.include.non_null);
        xml.disable(serializationfeature.fail_on_empty_beans);
        xml.disable(deserializationfeature.fail_on_unknown_properties);
        xml.registermodule(new javatimemodule());//处理java8新日期时间类型
    }

}

iwebservice.java

统一入口

/**
 * 统一 post 调用
 * 创建日期:2024-07-01
 */
public interface iwebservice<t> {

    object handle(t req);

}

webserviceentry.java

import lombok.extern.slf4j.slf4j;
import org.springframework.stereotype.service;

import javax.jws.webmethod;
import javax.jws.webparam;
import javax.jws.webservice;

/**
 * 创建日期:2024-07-01
 */
@slf4j
@service
@webservice
public class webserviceentry {

    /**
     * 通过实现了 iwebservice 接口的 bean name 反射调用 handle 方法
     *
     * @param service   bean name
     * @param parameter xml 字符串请求参数
     */
    @webmethod
    @suppresswarnings("unchecked")
    public <t> string invoke(@webparam(name = "service") string service, @webparam(name = "parameter") string parameter) throws jsonprocessingexception {
        iwebservice<t> webservice = (iwebservice<t>) applicationcontextutils.getbean(service);

        // 通过缓存获取 iwebservice 实现类的 handle 函数泛型类型入参,这样就不用每次请求都通过反射去获取入参,提升了程序性能。
        class<t> parametertype = (class<t>) webservicetypecache.getparametertype(service);
        // 使用 jackson-xml 将 xml 字符串转换为 java 对象
        t reqobject = jacksonutils.xml.readvalue(parameter, parametertype);

        r<?> r;
        try {
            r = r.ok(webservice.handle(reqobject));
        } catch (exception e) {
            string message = e.getmessage();
            log.error(message, e);
            r = r.err(message);
        }
        return jacksonutils.xml.writevalueasstring(r);
    }

}

webservicetypecache.java

启动的时候把 iwebservice实现类 handle函数的泛型入参写入缓存,在请求的时候直接通过缓存获取泛型入参的类型,减少每次请求的时候都使用反射获取泛型入参,提升程序性能。

import org.springframework.boot.applicationarguments;
import org.springframework.boot.applicationrunner;
import org.springframework.stereotype.component;

import java.lang.reflect.parameterizedtype;
import java.lang.reflect.type;
import java.util.hashmap;
import java.util.map;

/**
 * 启动的时候把 iwebservice实现类 handle函数的泛型入参写入缓存,在请求的时候直接通过缓存获取泛型入参的类型,
 * 减少每次请求的时候都使用反射获取泛型入参,提升程序性能。
 *
 * @since 2024-08-09
 */
@component
public class webservicetypecache implements applicationrunner {
    /**
     * 只能在启动的时候 put,运行的时候 get。不能在运行的时候 put,因为 hashmap 不是线程安全的。
     */
    private static final map<string, class<?>> typecache = new hashmap<>();

    @override
    public void run(applicationarguments args) throws exception {
        map<string, iwebservice> beans = applicationcontextutils.getbeansoftype(iwebservice.class);
        //循环map,foreach(key,value) 是最现代的方式,使用起来简洁明了。也可以用 for (map.entry<string, iwebservice> entry : beans.entryset()){}。
        beans.foreach((bean, type) -> {
            // aopproxyutils.ultimatetargetclass 解决spring boot 使用 @transactional 事务注解的问题。
            class<?> beanclass = aopproxyutils.ultimatetargetclass(type);
            // 获取 iwebservice 实现类的泛型类型
            type[] genericinterfaces = beanclass.getgenericinterfaces();
            for (type genericinterface : genericinterfaces) {
                if (genericinterface instanceof parameterizedtype) {
                    parameterizedtype parameterizedtype = (parameterizedtype) genericinterface;
                    type[] actualtypearguments = parameterizedtype.getactualtypearguments();

                    if (actualtypearguments.length > 0) {
                        class<?> parametertype = (class<?>) actualtypearguments[0];
                        //把泛型入参放入缓存。防止每次请求都通过反射获取入参,影响程序性能。
                        typecache.put(bean, parametertype);
                    }
                }
            }
        });
    }

    /**
     * 通过缓存获取 iwebservice 实现类 handle 函数的 泛型入参
     *
     * @param servicename iwebservice实现类的 bean name
     */
    public static class<?> getparametertype(string servicename) {
        return typecache.get(servicename);
    }

}

webserviceconfig.java

配置类

有些依赖千万不要导错,所以我依赖都粘贴进来了。防止导错包。

import org.apache.cxf.bus;
import org.apache.cxf.bus.spring.springbus;
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 javax.xml.ws.endpoint;

/**
 * 创建日期:2024-07-01
 */
@configuration
public class webserviceconfig {

    @bean(name = "cxfservlet")
    public servletregistrationbean<?> cxfservlet() {
        //urlmappings默认是:services
        return new servletregistrationbean<>(new cxfservlet(), "/services/*");
    }

    @bean(name = bus.default_bus_id)
    public springbus springbus() {
        return new springbus();
    }

    @bean
    public endpoint helloserviceendpoint() {
        endpointimpl endpoint = new endpointimpl(springbus(), new webserviceentry());
        //services后面的uri地址
        endpoint.publish("/webserviceentry");
        return endpoint;
    }

}

webmvcconfig.java

web的配置类,因为增加了xml依赖,springboot会默认把json放到xml后面,因此要手动改回默认json。

import org.springframework.context.annotation.configuration;
import org.springframework.http.mediatype;
import org.springframework.web.servlet.config.annotation.contentnegotiationconfigurer;
import org.springframework.web.servlet.config.annotation.webmvcconfigurer;

@configuration
public class webmvcconfig implements webmvcconfigurer {

    @override
    public void configurecontentnegotiation(contentnegotiationconfigurer configurer) {
        //引入 jackson-dataformat-xml 后,原本默认返回json变成了默认返回xml。因此这里要设置默认返回json
        configurer.defaultcontenttype(mediatype.application_json);
    }

}

实现iwebservice接口

如:webserviceimpl

@service("hello")
public class hello implements iwebservice<helloreq> {

    @override
    public hellores handle(helloreq req) {
        string name = req.getname();
        list<work> works = req.getworks();

        if (!stringutils.hastext(name)) {
            throw new runtimeexception("name 不能为空");
        }
        if (!collectionutils.isempty(works)) {
            for (work work : works) {
                string workname = work.getworkname();
                log.info("workname={}", workname);
            }
        }

        hellores res = new hellores();
        res.setname(name);
        res.setage(18);
        return res;
    }

}

helloreq.java

import com.fasterxml.jackson.dataformat.xml.annotation.jacksonxmlelementwrapper;
import com.fasterxml.jackson.dataformat.xml.annotation.jacksonxmlproperty;
import com.fasterxml.jackson.dataformat.xml.annotation.jacksonxmlrootelement;
import lombok.data;

import java.util.list;

@data
@jacksonxmlrootelement(localname = "params")
public class helloreq {

    @jacksonxmlproperty(localname = "name")
    private string name;

    @jacksonxmlelementwrapper(localname = "works")
    @jacksonxmlproperty(localname = "work")
    private list<work> works;

}

hellores.java

import com.fasterxml.jackson.dataformat.xml.annotation.jacksonxmlproperty;
import lombok.data;

@data
public class hellores {

    @jacksonxmlproperty(localname = "name")
    private string name;

    @jacksonxmlproperty(localname = "age")
    private integer age;

}

work.java

import com.fasterxml.jackson.dataformat.xml.annotation.jacksonxmlproperty;
import com.fasterxml.jackson.dataformat.xml.annotation.jacksonxmlrootelement;
import lombok.data;

@data
@jacksonxmlrootelement(localname = "work")
public class work {

    @jacksonxmlproperty(localname = "workname")
    private string workname;

}

统一返回类

import com.fasterxml.jackson.dataformat.xml.annotation.jacksonxmlproperty;
import com.fasterxml.jackson.dataformat.xml.annotation.jacksonxmlrootelement;
import lombok.allargsconstructor;
import lombok.data;
import lombok.noargsconstructor;

/**
 * 统一返回类
 * 创建日期:2024-07-01
 */
@data
@noargsconstructor(access = lombok.accesslevel.private)
@allargsconstructor(access = lombok.accesslevel.private)
@jacksonxmlrootelement(localname = "r")
public class r<t> {
    @jacksonxmlproperty(localname = "code")
    private integer code;
    @jacksonxmlproperty(localname = "message")
    private string message;
    @jacksonxmlproperty(localname = "data")
    private t data;

    public static <t> r<t> ok() {
        return ok(null);
    }

    public static <t> r<t> ok(t data) {
        return new r<>(200, "success", data);
    }

    public static <t> r<t> err(string message) {
        return err(400, message);
    }

    public static <t> r<t> err(integer code, string message) {
        return new r<>(code, message, null);
    }

}

启动springboot

访问

http://localhost:8080/services/webserviceentry?wsdl
  • 会出现如下所示界面

用soapui去调用接口

ws = "http://ws.bsjkt.bsoft.com/"这里每个人可能不一样
service = bean name
parameter = xml 请求参数
  • 入参:
<soapenv:envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:ws="http://ws.springbootwebservicedemo.fu.com/">
    <soapenv:header/>
    <soapenv:body>
        <ws:invoke>
            <service>hello</service>
            <parameter>
                <![cdata[
            <params><name>哈哈</name><works><work><workname>java</workname></work>
                </works>
            </params>
        ]]>
    </parameter>
</ws:invoke>
</soapenv:body>
</soapenv:envelope>
  • 出参:
<soap:envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
    <soap:body>
        <ns2:invokeresponse xmlns:ns2="http://ws.springbootwebservicedemo.fu.com/">
            <return><r><code>200</code><message>success</message><data><name>哈哈</name><age>18</age></data></r></return>
        </ns2:invokeresponse>
    </soap:body>
</soap:envelope>

总结

以上为个人经验,希望能给大家一个参考,也希望大家多多支持代码网。

(0)

相关文章:

版权声明:本文内容由互联网用户贡献,该文观点仅代表作者本人。本站仅提供信息存储服务,不拥有所有权,不承担相关法律责任。 如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 2386932994@qq.com 举报,一经查实将立刻删除。

发表评论

验证码:
Copyright © 2017-2025  代码网 保留所有权利. 粤ICP备2024248653号
站长QQ:2386932994 | 联系邮箱:2386932994@qq.com