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>
总结
以上为个人经验,希望能给大家一个参考,也希望大家多多支持代码网。
发表评论