Spring Boot 3.x 构建 SOAP WebService:5步实现国家查询服务(附完整代码)
📅 2026/7/9 22:29:58
👁️ 阅读次数
📝 编程学习
Spring Boot 3.x 构建 SOAP WebService:5步实现国家查询服务(附完整代码)
在当今企业级应用开发中,SOAP协议仍然是金融、电信等关键领域的主流选择。Spring Boot 3.x与Spring Web Services的深度整合,让开发者能够快速构建符合WS-I标准的Web服务。本文将手把手带你实现一个完整的国家信息查询服务,涵盖从XSD定义到端点测试的全流程。
1. 环境准备与项目初始化
首先创建一个基础的Spring Boot 3.x项目,添加以下关键依赖到pom.xml:
<dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web-services</artifactId> </dependency> <dependency> <groupId>wsdl4j</groupId> <artifactId>wsdl4j</artifactId> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.codehaus.mojo</groupId> <artifactId>jaxb2-maven-plugin</artifactId> <version>3.1.0</version> <executions> <execution> <id>xjc</id> <goals> <goal>xjc</goal> </goals> </execution> </executions> </plugin> </plugins> </build>关键配置说明:
spring-boot-starter-web-services提供SOAP支持wsdl4j用于WSDL处理jaxb2-maven-plugin实现XSD到Java类的自动转换
2. 定义XSD架构与WSDL
在src/main/resources/schemas目录下创建countries.xsd:
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" targetNamespace="http://example.com/countries" xmlns:tns="http://example.com/countries" elementFormDefault="qualified"> <xs:element name="getCountryRequest"> <xs:complexType> <xs:sequence> <xs:element name="name" type="xs:string"/> </xs:sequence> </xs:complexType> </xs:element> <xs:element name="getCountryResponse"> <xs:complexType> <xs:sequence> <xs:element name="country" type="tns:country"/> </xs:sequence> </xs:complexType> </xs:element> <xs:complexType name="country"> <xs:sequence> <xs:element name="name" type="xs:string"/> <xs:element name="capital" type="xs:string"/> <xs:element name="population" type="xs:int"/> <xs:element name="currency" type="tns:currency"/> </xs:sequence> </xs:complexType> <xs:simpleType name="currency"> <xs:restriction base="xs:string"> <xs:enumeration value="USD"/> <xs:enumeration value="EUR"/> <xs:enumeration value="CNY"/> </xs:restriction> </xs:simpleType> </xs:schema>执行mvn compile命令后,JAXB会在target/generated-sources目录生成对应的Java类,包括:
GetCountryRequestGetCountryResponseCountry等
3. 实现数据仓库与端点
创建内存型国家数据仓库:
@Component public class CountryRepository { private static final Map<String, Country> countries = new HashMap<>(); @PostConstruct public void initData() { Country china = new Country(); china.setName("China"); china.setCapital("Beijing"); china.setPopulation(1412000000); china.setCurrency(Currency.CNY); countries.put(china.getName(), china); Country usa = new Country(); usa.setName("USA"); usa.setCapital("Washington"); usa.setPopulation(331000000); usa.setCurrency(Currency.USD); countries.put(usa.getName(), usa); } public Country findCountry(String name) { return countries.get(name); } }实现服务端点:
@Endpoint public class CountryEndpoint { private static final String NAMESPACE_URI = "http://example.com/countries"; private final CountryRepository countryRepository; @Autowired public CountryEndpoint(CountryRepository countryRepository) { this.countryRepository = countryRepository; } @PayloadRoot(namespace = NAMESPACE_URI, localPart = "getCountryRequest") @ResponsePayload public GetCountryResponse getCountry(@RequestPayload GetCountryRequest request) { GetCountryResponse response = new GetCountryResponse(); response.setCountry(countryRepository.findCountry(request.getName())); return response; } }关键注解解析:
@Endpoint标记为SOAP端点@PayloadRoot定义处理的消息类型@ResponsePayload声明返回值为响应体
4. 服务配置与WSDL暴露
配置WebService基础设置:
@EnableWs @Configuration public class WebServiceConfig extends WsConfigurerAdapter { @Bean public ServletRegistrationBean<MessageDispatcherServlet> messageDispatcherServlet(ApplicationContext context) { MessageDispatcherServlet servlet = new MessageDispatcherServlet(); servlet.setApplicationContext(context); servlet.setTransformWsdlLocations(true); return new ServletRegistrationBean<>(servlet, "/ws/*"); } @Bean(name = "countries") public DefaultWsdl11Definition defaultWsdl11Definition(XsdSchema countriesSchema) { DefaultWsdl11Definition wsdl = new DefaultWsDL11Definition(); wsdl.setPortTypeName("CountriesPort"); wsdl.setLocationUri("/ws"); wsdl.setTargetNamespace("http://example.com/countries"); wsdl.setSchema(countriesSchema); return wsdl; } @Bean public XsdSchema countriesSchema() { return new SimpleXsdSchema(new ClassPathResource("schemas/countries.xsd")); } }启动应用后,可通过以下URL访问WSDL:
http://localhost:8080/ws/countries.wsdl5. 服务测试与验证
使用cURL测试服务:
curl --header "content-type: text/xml" \ --data @request.xml \ http://localhost:8080/ws其中request.xml内容为:
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:gs="http://example.com/countries"> <soapenv:Header/> <soapenv:Body> <gs:getCountryRequest> <gs:name>China</gs:name> </gs:getCountryRequest> </soapenv:Body> </soapenv:Envelope>预期响应示例:
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"> <SOAP-ENV:Header/> <SOAP-ENV:Body> <ns2:getCountryResponse xmlns:ns2="http://example.com/countries"> <ns2:country> <ns2:name>China</ns2:name> <ns2:capital>Beijing</ns2:capital> <ns2:population>1412000000</ns2:population> <ns2:currency>CNY</ns2:currency> </ns2:country> </ns2:getCountryResponse> </SOAP-ENV:Body> </SOAP-ENV:Envelope>对于更复杂的场景,可以添加SOAP头处理:
@PayloadRoot(namespace = NAMESPACE_URI, localPart = "getCountryRequest") @ResponsePayload public GetCountryResponse getCountry( @RequestPayload GetCountryRequest request, @SoapHeader("{" + SECURITY_NS + "}authentication") SoapHeaderElement auth) { // 验证头信息 if (!validateHeader(auth)) { throw new AuthenticationException("Invalid credentials"); } // ...原有处理逻辑 }6. 高级配置与优化
对于生产环境,建议添加以下增强配置:
WS-Security配置:
@Bean public Wss4jSecurityInterceptor securityInterceptor() { Wss4jSecurityInterceptor interceptor = new Wss4jSecurityInterceptor(); interceptor.setSecurementActions("UsernameToken"); interceptor.setSecurementUsername("admin"); interceptor.setSecurementPassword("secret"); return interceptor; } @Override public void addInterceptors(List<EndpointInterceptor> interceptors) { interceptors.add(securityInterceptor()); }性能优化建议:
- 启用JAXB编译缓存:
<plugin> <groupId>org.codehaus.mojo</groupId> <artifactId>jaxb2-maven-plugin</artifactId> <configuration> <clearOutputDir>false</clearOutputDir> </configuration> </plugin>- 配置HTTP压缩:
server.compression.enabled=true server.compression.mime-types=text/xml,application/soap+xml7. 异常处理与调试
实现自定义SOAP错误处理:
@Endpoint public class CountryEndpoint { // ...其他方法 @ExceptionHandler public void handleException(Exception ex, SoapMessage response) { TransformerFactory.newInstance().newTransformer() .transform(new DOMSource(createFault(ex)), new StreamResult(response.getSoapBody().getResult())); } private org.w3c.dom.Document createFault(Exception ex) { // 构建详细的SOAP Fault XML } }调试技巧:
- 启用SOAP消息日志:
logging.level.org.springframework.ws.client.MessageTracing.sent=TRACE logging.level.org.springframework.ws.client.MessageTracing.received=TRACE- 使用SoapUI工具进行可视化测试
- 通过WireShark抓包分析原始SOAP报文
编程学习
技术分享
实战经验