SpringBoot集成WebService(wsdl)實踐
更新時間:2025年09月24日 14:19:20 作者:Meta39
文章介紹了Spring Boot項目中通過緩存IWebService接口實現(xiàn)類的泛型入?yún)㈩愋?減少反射調(diào)用提升性能的實現(xiàn)方案,包含依賴配置、工具類封裝、接口定義及soapUI調(diào)用參數(shù)說明
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>
創(chuàng)建入口
ApplicationContextUtils.java
bean調(diào)用工具
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component;
/**
* 創(chuàng)建日期:2024-07-01
*/
@Component
public class ApplicationContextUtils implements ApplicationContextAware {
//構造函數(shù)私有化,防止其它人實例化該對象
private ApplicationContextUtils() {
}
private static ApplicationContext applicationContext;
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
ApplicationContextUtils.applicationContext = applicationContext;
}
//通過name獲取 Bean.(推薦,因為bean的name是唯一的,出現(xiàn)重名的bean啟動會報錯。)
public static Object getBean(String name) {
return applicationContext.getBean(name);
}
//通過class獲取Bean.(確保bean的name不會重復。因為可能會出現(xiàn)在不同包的同名bean導致獲取到2個實例)
public static <T> T getBean(Class<T> clazz) {
return applicationContext.getBean(clazz);
}
//通過name,以及Clazz返回指定的Bean(這個是最穩(wěn)妥的)
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
統(tǒng)一入口
/**
* 統(tǒng)一 post 調(diào)用
* 創(chuàng)建日期: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;
/**
* 創(chuàng)建日期:2024-07-01
*/
@Slf4j
@Service
@WebService
public class WebServiceEntry {
/**
* 通過實現(xiàn)了 IWebService 接口的 bean name 反射調(diào)用 handle 方法
*
* @param service bean name
* @param parameter XML 字符串請求參數(shù)
*/
@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 實現(xiàn)類的 handle 函數(shù)泛型類型入?yún)?,這樣就不用每次請求都通過反射去獲取入?yún)ⅲ嵘顺绦蛐阅堋?
Class<T> parameterType = (Class<T>) WebServiceTypeCache.getParameterType(service);
// 使用 Jackson-XML 將 XML 字符串轉(zhuǎn)換為 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實現(xiàn)類 handle函數(shù)的泛型入?yún)懭刖彺?,在請求的時候直接通過緩存獲取泛型入?yún)⒌念愋停瑴p少每次請求的時候都使用反射獲取泛型入?yún)?,提升程序性能?/p>
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實現(xiàn)類 handle函數(shù)的泛型入?yún)懭刖彺?,在請求的時候直接通過緩存獲取泛型入?yún)⒌念愋停?
* 減少每次請求的時候都使用反射獲取泛型入?yún)ⅲ嵘绦蛐阅堋?
*
* @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);
//循環(huán)map,forEach(key,value) 是最現(xiàn)代的方式,使用起來簡潔明了。也可以用 for (Map.Entry<String, IWebService> entry : beans.entrySet()){}。
beans.forEach((bean, type) -> {
// AopProxyUtils.ultimateTargetClass 解決Spring Boot 使用 @Transactional 事務注解的問題。
Class<?> beanClass = AopProxyUtils.ultimateTargetClass(type);
// 獲取 IWebService 實現(xiàn)類的泛型類型
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];
//把泛型入?yún)⒎湃刖彺?。防止每次請求都通過反射獲取入?yún)?,影響程序性能?
typeCache.put(bean, parameterType);
}
}
}
});
}
/**
* 通過緩存獲取 IWebService 實現(xiàn)類 handle 函數(shù)的 泛型入?yún)?
*
* @param serviceName IWebService實現(xiàn)類的 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;
/**
* 創(chuàng)建日期: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);
}
}
實現(xiàn)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;
}
統(tǒng)一返回類
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;
/**
* 統(tǒng)一返回類
* 創(chuàng)建日期: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
- 會出現(xiàn)如下所示界面

用soapUI去調(diào)用接口
ws = "http://ws.bsjkt.bsoft.com/"這里每個人可能不一樣 service = bean name parameter = XML 請求參數(shù)
- 入?yún)ⅲ?/strong>
<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>
總結(jié)
以上為個人經(jīng)驗,希望能給大家一個參考,也希望大家多多支持腳本之家。
相關文章
Java Socket+mysql實現(xiàn)簡易文件上傳器的代碼
最近在做一個小項目,項目主要需求是實現(xiàn)一個文件上傳器,通過客戶端的登陸,把本地文件上傳到服務器的數(shù)據(jù)庫(本地的)。下面通過本文給大家分享下實現(xiàn)代碼,感興趣的朋友一起看看吧2016-10-10
猜你不知道Spring Boot的幾種部署方式(小結(jié))
這篇文章主要介紹了猜你不知道Spring Boot的幾種部署方式(小結(jié)),文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧2019-07-07

