Web Services 是特殊的网络服务,它允许位于不同地点的计算机程序通过互联网交流和共享数据。这些服务可以通过多种方式实现,其中包括使用SOAP协议和遵循REST原则。
在SOAP的实现中,Spring Web Services(Spring WS)
和Java API for XML Web Services(JAX-WS)
是两种主要的技术选择。Spring WS 是专为简化SOAP Web Services的开发而设计的,提供了一种创建文档驱动、基于SOAP的服务的方式,特别适合需要深度集成和复杂配置的企业级应用。相比之下,JAX-WS 是遵循WS-*标准的更为基础和轻量级的Java API,适用于需要标准Java支持且配置较少的场景。
本文主要展示这两种技术在Spring Boot环境中实现方式。
本文两种实现方式
-
@Endpoint 注解实现 SOAP Web Services -
@WebService 注解实现 SOAP Web Services
版本
-
Java:8 -
Spring Boot:2.3.12.RELEASE
@Endpoint
@Endpoint注解来自Spring Web Services(Spring WS)项目,它专门用于处理SOAP Web服务的开发。Spring WS 不依赖于企业Java的标准,而是建立在Spring框架的基础之上,提供更灵活的配置和更好的集成。
@Endpoint示例参考此篇文件:https://www.baeldung.com/spring-boot-soap-web-service。但有几个点需要改动。
1. Maven依赖。
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web-services</artifactId>
</dependency>
<dependency>
<groupId>wsdl4j</groupId>
<artifactId>wsdl4j</artifactId>
</dependency>
2. 在src/main/resources目录下创建一个名为 countries.xsd 的XSD文件(XML Schema Definition),用于定义SOAP消息的结构。这个Schema将作为生成Java类的基础,用于请求和响应的数据结构。
<?xml version="1.0" encoding="UTF-8"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:tns="http://www.baeldung.com/springsoap/gen"
targetNamespace="http://www.baeldung.com/springsoap/gen" 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="population" type="xs:int"/>
<xs:element name="capital" type="xs:string"/>
<xs:element name="currency" type="tns:currency"/>
</xs:sequence>
</xs:complexType>
<xs:simpleType name="currency">
<xs:restriction base="xs:string">
<xs:enumeration value="GBP"/>
<xs:enumeration value="EUR"/>
<xs:enumeration value="PLN"/>
</xs:restriction>
</xs:simpleType>
</xs:schema>
3. 配置jaxb maven插件,将定义的 XSD 文件生成对应的 Java 类。
<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>
<configuration>
<sources>
<source>src/main/resources/countries.xsd</source>
</sources>
<outputDirectory>src/main/java/endpoint</outputDirectory>
<clearOutputDir>false</clearOutputDir>
</configuration>
</plugin>
source路径自己定义,相对路径绝对路径都可以。
控制台输入命令:
mvn package
则会生成对应的java类,拷贝到对应的目录下即可。
4. 添加 SOAP Web 服务端点。
@Component
@Endpoint
public class CountryEndpoint {
private static final String NAMESPACE_URI = "http://www.baeldung.com/springsoap/gen";
@PayloadRoot(namespace = NAMESPACE_URI, localPart = "getCountryRequest")
@ResponsePayload
public JAXBElement<GetCountryResponse> getCountry(@RequestPayload JAXBElement<GetCountryRequest> request) {
GetCountryResponse response = new GetCountryResponse();
Country country = new Country();
country.setCapital("12");
country.setName(request.getValue().getName());
response.setCountry(country);
return new JAXBElement<>(new QName(NAMESPACE_URI, GetCountryResponse.class.getSimpleName()), GetCountryResponse.class, response);
}
}
注解含义:
-
@Endpoint:将类作为 Web 服务端点注册到 Spring WS。(端点可理解为处理特定类型的请求处理器) -
@PayloadRoot:标记具体处理SOAP请求的方法。根据命名空间和 localPart 属性定义处理程序方法。 -
namespace:命名空间URI,用于标识特定的XML文档或元素集。 -
localPart:本地名称,指定在给定命名空间中的特定元素。
-
-
@ResponsePayload:处理将Java对象转换回SOAP消息的注解。 -
@RequestPayload:该注解将SOAP请求参数反序列化为java对象。
JAXBElement:相当于一个包装器,用来包含单个XML元素的信息。上述例子中创建JAXBElement是确保响应有正确的命令空间和本地名称。
namespace和localPart对应关系如下图:
5. SOAP Web 服务配置 Bean。
WebServiceConfig类主要目的是配置和发布一个基于 SOAP 协议的 Web 服务。
@EnableWs //启用Spring Web Services的配置支持
@Configuration
public class WebServiceConfig extends WsConfigurerAdapter {
@Bean
public ServletRegistrationBean<MessageDispatcherServlet> messageDispatcherServlet(ApplicationContext applicationContext) {
//定义一个servlet,用于处理soap消息
MessageDispatcherServlet servlet = new MessageDispatcherServlet();
servlet.setApplicationContext(applicationContext);
servlet.setTransformWsdlLocations(true);
return new ServletRegistrationBean<>(servlet, "/ws/*");
}
@Bean(name = "countries")
public DefaultWsdl11Definition defaultWsdl11Definition(XsdSchema countriesSchema) {
//公开一个标准的WSDL 1.1文档
DefaultWsdl11Definition wsdl11Definition = new DefaultWsdl11Definition();
wsdl11Definition.setPortTypeName("CountriesPort");
//定义服务的访问地址,即客户端访问Web服务的端点
wsdl11Definition.setLocationUri("/ws");
wsdl11Definition.setTargetNamespace("http://www.baeldung.com/springsoap/gen");
//关联xsd文件
wsdl11Definition.setSchema(countriesSchema);
return wsdl11Definition;
}
@Bean
public XsdSchema countriesSchema() {
return new SimpleXsdSchema(new ClassPathResource("countries.xsd"));
}
}
应用启动成功后,打开url来验证wsdl文件是否成功发布: http://localhost:18889/ws/countries.wsdl
。(我本地应用端口非8080)
WSDL 文件如下:
<?xml version="1.0" encoding="UTF-8" standalone="no"?><wsdl:definitions xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/" xmlns:sch="http://www.baeldung.com/springsoap/gen" xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/" xmlns:tns="http://www.baeldung.com/springsoap/gen" targetNamespace="http://www.baeldung.com/springsoap/gen">
<wsdl:types>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" elementFormDefault="qualified" targetNamespace="http://www.baeldung.com/springsoap/gen">
<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="population" type="xs:int"/>
<xs:element name="capital" type="xs:string"/>
<xs:element name="currency" type="tns:currency"/>
</xs:sequence>
</xs:complexType>
<xs:simpleType name="currency">
<xs:restriction base="xs:string">
<xs:enumeration value="GBP"/>
<xs:enumeration value="EUR"/>
<xs:enumeration value="PLN"/>
</xs:restriction>
</xs:simpleType>
</xs:schema>
</wsdl:types>
<wsdl:message name="getCountryResponse">
<wsdl:part element="tns:getCountryResponse" name="getCountryResponse">
</wsdl:part>
</wsdl:message>
<wsdl:message name="getCountryRequest">
<wsdl:part element="tns:getCountryRequest" name="getCountryRequest">
</wsdl:part>
</wsdl:message>
<wsdl:portType name="CountriesPort">
<wsdl:operation name="getCountry">
<wsdl:input message="tns:getCountryRequest" name="getCountryRequest">
</wsdl:input>
<wsdl:output message="tns:getCountryResponse" name="getCountryResponse">
</wsdl:output>
</wsdl:operation>
</wsdl:portType>
<wsdl:binding name="CountriesPortSoap11" type="tns:CountriesPort">
<soap:binding style="document" transport="http://schemas.xmlsoap.org/soap/http"/>
<wsdl:operation name="getCountry">
<soap:operation soapAction=""/>
<wsdl:input name="getCountryRequest">
<soap:body use="literal"/>
</wsdl:input>
<wsdl:output name="getCountryResponse">
<soap:body use="literal"/>
</wsdl:output>
</wsdl:operation>
</wsdl:binding>
<wsdl:service name="CountriesPortService">
<wsdl:port binding="tns:CountriesPortSoap11" name="CountriesPortSoap11">
<soap:address location="http://localhost:18889/ws"/>
</wsdl:port>
</wsdl:service>
</wsdl:definitions>
文件内容:
types(类型):定义web服务之间传递的数据类型。
message(消息):定义web服务中用于通信的消息结构。
portType(端口类型):定义web服务接口,包含一组操作,每个操作定义了输入和输出得消息结构。
binding(绑定):定义web服务的通信协议和消息格式。
service(服务):定义web服务的实际访问地址。
6. 用 postman 测试 SOAP 项目。
@WebService
@WebService注解来自JAX-WS(Java API for XML Web Services)标准,它是一个专门用于创建SOAP(Simple Object Access Protocol)Web服务的API。这种方法通常用于实现符合WS-标准的Web服务,并且是Java EE规范的一部分。
1. Maven依赖。
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web-services</artifactId>
</dependency>
<dependency>
<groupId>org.apache.cxf</groupId>
<artifactId>cxf-spring-boot-starter-jaxws</artifactId>
<version>3.4.4</version>
</dependency>
2. 定义webservices接口。
@WebService(name = "PersonnelInfo",
targetNamespace = "http://service.manage.com/")
public interface PersonnelInfoService {
@WebMethod
String syncPersonnelInfo(@WebParam(name = "xmlData", targetNamespace = "http://service.manage.com/") String jsonData);
}
@WebService:标记一个接口或类作为Web服务的服务端点。
-
name:指定服务的名称。 -
targetNamespace:定义了这个Web服务所使用的XML命名空间。
@WebMethod 和 @WebParam 注解用于定义 Web 服务中的方法和参数。
3. 定义webservices实现类。
//endpointInterface:服务接口全路径
@Slf4j
@Component
@WebService(name = "PersonnelInfo",
targetNamespace = "http://service.manage.com/",
endpointInterface = "com.manage.service.PersonnelInfoService")
public class PersonnelInfoServiceImpl implements PersonnelInfoService {
@Override
public String syncPersonnelInfo(@WebParam(name = "xmlData", targetNamespace = "http://service.manage.com/") String xmlData) {
String result = "success";
log.info("三方信息调用入参:{}", xmlData);
}
}
4. 发布webservices服务。
@Configuration
public class CxfConfig {
@Autowired
private Bus bus;
@Autowired
private PersonnelInfoService personnelInfoService;
@Bean(name = "wsBean")
public ServletRegistrationBean dispatcherServlet() {
//注册servlet,用于处理web服务请求
ServletRegistrationBean wbsServlet = new ServletRegistrationBean(new CXFServlet(), "/ws/*");
return wbsServlet;
}
/**
* JAX-WS
* 站点服务
**/
@Bean
public Endpoint endpoint() {
//发布Web服务的类,将请求都交给personnelInfoService类处理
EndpointImpl endpoint = new EndpointImpl(bus, personnelInfoService);
//将当前端点发布到URL路径/data上
endpoint.publish("/data");
return endpoint;
}
}
输入下面url检查程序发布是否正常: http://localhost:8080/ws/countries.wsdl
5. 用 postman 测试 SOAP 项目。
总结
@Endpoint
和 @WebService
这两种方式都可以实现接收 XML 请求的需求。其中一个依赖于 JAX-WS 标准,更适合定制化和特殊需求的 Web 服务实现;另一个依赖于 JAX-WS 标准,适用于遵循规范和标准化的项目。具体可根据实际需求和开发栈来选择合适的实现方式。
参考资料
-
https://www.baeldung.com/spring-boot-soap-web-service