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