1、什么是Spring Web Service?
首先,不知道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 作为服务发布,那么它将是最佳实践。
2、为什么Spring-WS要用约定优先开发
众所周知,创建Web服务时,有两种开发样式:(约定滞后)Contract Last和(约定优先)Contract First。当使用Contract Last方法时,将从Java代码开始,然后从中生成Web服务契约(WSDL)。当使用契约优先时,首先要使用WSDL契约,然后使用Java来实现所述契约。
什么意思呢,也就是WSDL的诞生的问题,如何产生。
Spring-WS仅支持契约优先的开发风格。
原因总结来看有以下几点:
- 可以支持更复杂的对象定义,java有局限
- 程序更加健壮
- 性能更优越,Java转Xml相较而言,差一点
- 重用性好
- 扩展性强,采用配置文件,改动小
3、采用Springboot + Spring-WS实现一个简单应用
应用场景描述:
公司人事部门,完成员工对于假期的请求。员工发起,假期请求。填写起始日期时间,姓名等信息,公司完成确认。
- 搭建一个Springboot应用
pom文件
<?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>
2.采用约定优先开发风格,完成对象的定义
xsd文件
<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>
3.应用配置文件
application.properties
server.port=8848 logging.level.web=DEBUG spring.webservices.path=/webservices spring.webservices.servlet.init.transformWsdlLocations=true
4.Endpoint
开发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 2020/9/18 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 + "]"); } } }
5.实现服务
服务接口
package com.example.demo.service; import java.util.Date; /** * @author 小隐乐乐 * @since 2020/9/18 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 2020/9/18 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 + "] "); } }
6.采用配置生成,生成WSDL,发布WSDL服务
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 2020/9/18 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; } }
7.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>
4、应用测试
采用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>
测试完毕,一个简单的应用接口,完成开发。