首先,不知道Web service的小伙伴,还需要,去了解下Web service 的相关知识再来看这篇文章。本文基于官方文档,作为基础。
Spring Web Services(Spring-WS)是Spring社区的产品,致力于创建文档驱动的Web服务。Spring Web Services旨在促进约定优先SOAP服务的开发,从而允许使用多种处理XML有效负载的方式之一来创建灵活的Web服务。该产品基于Spring本身,这意味着您可以将诸如依赖项注入之类的Spring概念用作Web服务的组成部分。
人们使用Spring-WS的原因有很多,但是大多数人在找到了遵循Web服务最佳实践所缺乏的替代SOAP堆栈之后才开始使用它。Spring-WS使最佳实践变得容易。这包括诸如WS-I基本概要文件,合同优先开发之类的实践,以及合同与实施之间的松散耦合。
如果你们项目里,还在使用Web Service 作为服务发布,那么它将是最佳实践。
众所周知,创建Web服务时,有两种开发样式:(约定滞后)Contract Last和(约定优先)Contract First。当使用Contract Last方法时,将从Java代码开始,然后从中生成Web服务契约(WSDL)。当使用契约优先时,首先要使用WSDL契约,然后使用Java来实现所述契约。
什么意思呢,也就是WSDL的诞生的问题,如何产生。
Spring-WS仅支持契约优先的开发风格。
原因总结来看有以下几点:
可以支持更复杂的对象定义,java有局限
程序更加健壮
性能更优越,Java转Xml相较而言,差一点
重用性好
扩展性强,采用配置文件,改动小
应用场景描述:
公司人事部门,完成员工对于假期的请求。员工发起,假期请求。填写起始日期时间,姓名等信息,公司完成确认。
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.3.4.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.example</groupId>
<artifactId>demo</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>demo</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>1.8</java.version>
<jaxen.version>1.1.4</jaxen.version>
<jdom.version>2.0.1</jdom.version>
<joda-time.version>2.10.6</joda-time.version>
<log4j.version>1.2.16</log4j.version>
<sourcesDir>${project.basedir}/target/generated-sources/axis</sourcesDir>
<classesDir>${project.basedir}/target/classes</classesDir>
<wsdl>${project.basedir}/../airline.wsdl</wsdl>
<wsdl4j.version>1.6.1</wsdl4j.version>
<xmlschema.version>2.1.0</xmlschema.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web-services</artifactId>
</dependency>
<dependency>
<groupId>org.jdom</groupId>
<artifactId>jdom</artifactId>
<version>${jdom.version}</version>
</dependency>
<dependency>
<groupId>jaxen</groupId>
<artifactId>jaxen</artifactId>
<version>${jaxen.version}</version>
</dependency>
<dependency>
<groupId>org.apache.ws.xmlschema</groupId>
<artifactId>xmlschema-core</artifactId>
<version>${xmlschema.version}</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>wsdl4j</groupId>
<artifactId>wsdl4j</artifactId>
<version>${wsdl4j.version}</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.springframework.ws</groupId>
<artifactId>spring-ws-support</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
<exclusions>
<exclusion>
<groupId>org.junit.vintage</groupId>
<artifactId>junit-vintage-engine</artifactId>
</exclusion>
</exclusions>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>jaxb2-maven-plugin</artifactId>
<version>2.5.0</version>
<executions>
<execution>
<id>xjc</id>
<goals>
<goal>xjc</goal>
</goals>
</execution>
</executions>
<configuration>
<sources>${project.basedir}/src/main/resources/hr.xsd</sources>
<packageName>com.example.demo.schema</packageName>
<target>2.1</target>
</configuration>
</plugin>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>build-helper-maven-plugin</artifactId>
<version>1.1</version>
<executions>
<execution>
<id>add-source</id>
<phase>process-sources</phase>
<goals>
<goal>add-source</goal>
</goals>
<configuration>
<sources>
<source>target/generated-sources/xjc</source>
</sources>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>
采用约定优先开发风格,完成对象的定义
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:hr="http://mycompany.com/hr/schemas"
elementFormDefault="qualified"
targetNamespace="http://mycompany.com/hr/schemas">
<xs:element name="HolidayRequest">
<xs:complexType>
<xs:all>
<xs:element name="Holiday" type="hr:HolidayType"/>
<xs:element name="Employee" type="hr:EmployeeType"/>
</xs:all>
</xs:complexType>
</xs:element>
<xs:complexType name="HolidayType">
<xs:sequence>
<xs:element name="StartDate" type="xs:date"/>
<xs:element name="EndDate" type="xs:date"/>
</xs:sequence>
</xs:complexType>
<xs:complexType name="EmployeeType">
<xs:sequence>
<xs:element name="Number" type="xs:integer"/>
<xs:element name="FirstName" type="xs:string"/>
<xs:element name="LastName" type="xs:string"/>
</xs:sequence>
</xs:complexType>
</xs:schema>
application.properties
server.port=8848
logging.level.web=DEBUG
spring.webservices.path=/webservices
spring.webservices.servlet.init.transformWsdlLocations=true
开发WebService实现Endpoint,HolidayEndpoint
package com.example.demo.endpoint;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import com.example.demo.service.HumanResourceService;
import org.jdom2.Element;
import org.jdom2.Namespace;
import org.jdom2.filter.Filters;
import org.jdom2.xpath.XPathExpression;
import org.jdom2.xpath.XPathFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.ws.server.endpoint.annotation.Endpoint;
import org.springframework.ws.server.endpoint.annotation.PayloadRoot;
import org.springframework.ws.server.endpoint.annotation.RequestPayload;
/**
* @author 小隐乐乐
* @since 2021/1/14 22:44
*/
@Endpoint
public class HolidayEndpoint {
private static final String NAMESPACE_URI = "http://mycompany.com/hr/schemas";
private XPathExpression<Element> startDateExpression;
private XPathExpression<Element> endDateExpression;
private XPathExpression<Element> firstNameExpression;
private XPathExpression<Element> lastNameExpression;
private HumanResourceService humanResourceService;
@Autowired
public HolidayEndpoint(HumanResourceService humanResourceService) {
this.humanResourceService = humanResourceService;
Namespace namespace = Namespace.getNamespace("hr", NAMESPACE_URI);
XPathFactory xPathFactory = XPathFactory.instance();
startDateExpression = xPathFactory.compile("//hr:StartDate", Filters.element(), null, namespace);
endDateExpression = xPathFactory.compile("//hr:EndDate", Filters.element(), null, namespace);
firstNameExpression = xPathFactory.compile("//hr:FirstName", Filters.element(), null, namespace);
lastNameExpression = xPathFactory.compile("//hr:LastName", Filters.element(), null, namespace);
}
@PayloadRoot(namespace = NAMESPACE_URI, localPart = "HolidayRequest")
public void handleHolidayRequest(@RequestPayload Element holidayRequest) throws Exception {
Date startDate = parseDate(startDateExpression, holidayRequest);
Date endDate = parseDate(endDateExpression, holidayRequest);
String name = firstNameExpression.evaluateFirst(holidayRequest).getText() + " "
+ lastNameExpression.evaluateFirst(holidayRequest).getText();
humanResourceService.bookHoliday(startDate, endDate, name);
}
private Date parseDate(XPathExpression<Element> expression, Element element) throws ParseException {
Element result = expression.evaluateFirst(element);
if (result != null) {
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd");
return dateFormat.parse(result.getText());
} else {
throw new IllegalArgumentException("Could not evaluate [" + expression + "] on [" + element + "]");
}
}
}
package com.example.demo.service;
import java.util.Date;
/**
* @author 小隐乐乐
* @since 2021/1/14 23:00
*/
public interface HumanResourceService {
/**
* 请假.
*
* @param startDate 假期开始时间
* @param endDate 假期结束时间
* @param name 请假人
*/
void bookHoliday(Date startDate, Date endDate, String name);
}
package com.example.demo.service.impl;
import com.example.demo.service.HumanResourceService;
import org.springframework.stereotype.Service;
import java.util.Date;
/**
* @author 小隐乐乐
* @since 2021/1/14 23:02
*/
@Service
public class HumanResourceServiceImpl implements HumanResourceService {
@Override
public void bookHoliday(Date startDate, Date endDate, String name) {
System.out.println("Booking holiday for [" + startDate + "-" + endDate + "] for [" + name + "] ");
}
}
package com.example.demo.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.ClassPathResource;
import org.springframework.ws.wsdl.wsdl11.DefaultWsdl11Definition;
import org.springframework.xml.xsd.commons.CommonsXsdSchemaCollection;
/**
* @author 小隐乐乐
* @since 2021/1/14 23:34
*/
@Configuration
public class EndpointConfig {
@Bean
public DefaultWsdl11Definition holiday() {
DefaultWsdl11Definition definition = new DefaultWsdl11Definition();
definition.setPortTypeName("HumanResource");
definition.setLocationUri("/webservices/holidayService/");
definition.setTargetNamespace("http://mycompany.com/hr/definitions");
definition.setSchemaCollection(holidayXsd());
return definition;
}
@Bean
public CommonsXsdSchemaCollection holidayXsd() {
CommonsXsdSchemaCollection collection = new CommonsXsdSchemaCollection(new ClassPathResource("/hr.xsd"));
collection.setInline(true);
return collection;
}
}
maven插件
用于生成Schema实体
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>jaxb2-maven-plugin</artifactId>
<version>2.5.0</version>
<executions>
<execution>
<id>xjc</id>
<goals>
<goal>xjc</goal>
</goals>
</execution>
</executions>
<configuration>
<sources>${project.basedir}/src/main/resources/hr.xsd</sources>
<packageName>com.example.demo.schema</packageName>
<target>2.1</target>
</configuration>
</plugin>
采用SoapUi完成接口测试。
服务发布地址:http://localhost:8848/webservices/holidayService/holiday.wsdl
服务请求地址:http://localhost:8848/webservices/holidayService
请求报文
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:sch="http://mycompany.com/hr/schemas">
<soapenv:Header/>
<soapenv:Body>
<sch:HolidayRequest>
<!--You may enter the following 2 items in any order-->
<sch:Holiday>
<sch:StartDate>2008-09-29</sch:StartDate>
<sch:EndDate>2014-09-19</sch:EndDate>
</sch:Holiday>
<sch:Employee>
<sch:Number>100</sch:Number>
<sch:FirstName>verrantque per auras</sch:FirstName>
<sch:LastName>per auras</sch:LastName>
</sch:Employee>
</sch:HolidayRequest>
</soapenv:Body>
</soapenv:Envelope>
测试完毕,一个简单的应用接口,完成开发。