编辑pom.xml
并将此依赖项添加到您的项目中。
<dependency>
<groupId>wsdl4j</groupId>
<artifactId>wsdl4j</artifactId>
</dependency>
当我们遵循合同优先的方法来开发服务时,我们需要首先为我们的服务创建域(方法和参数)。 为简单起见,我们将请求和响应都保留在相同的?XSD?中,但在实际的企业用例中,我们将有多个 XSD 相互导入以形成最终定义。
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:tns="https://www.howtodoinjava.com/xml/school"
targetNamespace="https://www.howtodoinjava.com/xml/school" elementFormDefault="qualified">
<xs:element name="StudentDetailsRequest">
<xs:complexType>
<xs:sequence>
<xs:element name="name" type="xs:string"/>
</xs:sequence>
</xs:complexType>
</xs:element>
<xs:element name="StudentDetailsResponse">
<xs:complexType>
<xs:sequence>
<xs:element name="Student" type="tns:Student"/>
</xs:sequence>
</xs:complexType>
</xs:element>
<xs:complexType name="Student">
<xs:sequence>
<xs:element name="name" type="xs:string"/>
<xs:element name="standard" type="xs:int"/>
<xs:element name="address" type="xs:string"/>
</xs:sequence>
</xs:complexType>
</xs:schema>
将以上文件放置在项目的resources
文件夹中。
我们将使用jaxb2-maven-plugin
有效地生成域类。 现在,我们需要将以下 Maven 插件添加到项目的pom.xml
文件的插件部分。
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>jaxb2-maven-plugin</artifactId>
<version>1.6</version>
<executions>
<execution>
<id>xjc</id>
<goals>
<goal>xjc</goal>
</goals>
</execution>
</executions>
<configuration>
<schemaDirectory>${project.basedir}/src/main/resources/</schemaDirectory>
<outputDirectory>${project.basedir}/src/main/java</outputDirectory>
<clearOutputDir>false</clearOutputDir>
</configuration>
</plugin>
该插件使用?XJC?工具作为代码生成引擎。 XJC 将 XML 模式文件编译为完全注解的 Java 类。
现在执行上面的 maven 插件以从 XSD 生成 Java 代码。
StudentEndpoint
类将处理对服务的所有传入请求,并将调用委派给数据存储库的finder
方法。
package com.example.howtodoinjava.springbootsoapservice;
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;
import org.springframework.ws.server.endpoint.annotation.ResponsePayload;
import com.howtodoinjava.xml.school.StudentDetailsRequest;
import com.howtodoinjava.xml.school.StudentDetailsResponse;
@Endpoint
public class StudentEndpoint
{
private static final String NAMESPACE_URI = "https://www.howtodoinjava.com/xml/school";
private StudentRepository StudentRepository;
@Autowired
public StudentEndpoint(StudentRepository StudentRepository) {
this.StudentRepository = StudentRepository;
}
@PayloadRoot(namespace = NAMESPACE_URI, localPart = "StudentDetailsRequest")
@ResponsePayload
public StudentDetailsResponse getStudent(@RequestPayload StudentDetailsRequest request) {
StudentDetailsResponse response = new StudentDetailsResponse();
response.setStudent(StudentRepository.findStudent(request.getName()));
return response;
}
}
这里有一些关于注解的细节:
如前所述,我们将使用硬编码的数据作为此演示的后端,让我们添加一个名为StudentRepository.java
并带有 Spring?@Repository
注解的类。 它只会将数据保存在HashMap
中,并且还会提供一种称为findStudent()
的查找器方法。
package com.example.howtodoinjava.springbootsoapservice;
import java.util.HashMap;
import java.util.Map;
import javax.annotation.PostConstruct;
import org.springframework.stereotype.Component;
import org.springframework.util.Assert;
import com.howtodoinjava.xml.school.Student;
@Component
public class StudentRepository {
private static final Map<String, Student> students = new HashMap<>();
@PostConstruct
public void initData() {
Student student = new Student();
student.setName("Sajal");
student.setStandard(5);
student.setAddress("Pune");
students.put(student.getName(), student);
student = new Student();
student.setName("Kajal");
student.setStandard(5);
student.setAddress("Chicago");
students.put(student.getName(), student);
student = new Student();
student.setName("Lokesh");
student.setStandard(6);
student.setAddress("Delhi");
students.put(student.getName(), student);
student = new Student();
student.setName("Sukesh");
student.setStandard(7);
student.setAddress("Noida");
students.put(student.getName(), student);
}
public Student findStudent(String name) {
Assert.notNull(name, "The Student's name must not be null");
return students.get(name);
}
}
创建带有@Configuration
注解的类以保存 bean 定义。
package com.example.howtodoinjava.springbootsoapservice;
import org.springframework.boot.web.servlet.ServletRegistrationBean;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.ClassPathResource;
import org.springframework.ws.config.annotation.EnableWs;
import org.springframework.ws.config.annotation.WsConfigurerAdapter;
import org.springframework.ws.transport.http.MessageDispatcherServlet;
import org.springframework.ws.wsdl.wsdl11.DefaultWsdl11Definition;
import org.springframework.xml.xsd.SimpleXsdSchema;
import org.springframework.xml.xsd.XsdSchema;
@EnableWs
@Configuration
public class Config extends WsConfigurerAdapter
{
@Bean
public ServletRegistrationBean messageDispatcherServlet(ApplicationContext applicationContext)
{
MessageDispatcherServlet servlet = new MessageDispatcherServlet();
servlet.setApplicationContext(applicationContext);
servlet.setTransformWsdlLocations(true);
return new ServletRegistrationBean(servlet, "/service/*");
}
@Bean(name = "studentDetailsWsdl")
public DefaultWsdl11Definition defaultWsdl11Definition(XsdSchema countriesSchema)
{
DefaultWsdl11Definition wsdl11Definition = new DefaultWsdl11Definition();
wsdl11Definition.setPortTypeName("StudentDetailsPort");
wsdl11Definition.setLocationUri("/service/student-details");
wsdl11Definition.setTargetNamespace("https://www.howtodoinjava.com/xml/school");
wsdl11Definition.setSchema(countriesSchema);
return wsdl11Definition;
}
@Bean
public XsdSchema countriesSchema()
{
return new SimpleXsdSchema(new ClassPathResource("school.xsd"));
}
}
Config
类扩展了WsConfigurerAdapter,它配置了注解驱动的 Spring-WS 编程模型。
MessageDispatcherServlet?– Spring-WS 使用它来处理 SOAP 请求。 我们需要向该 servlet 注入ApplicationContext
,以便 Spring-WS 找到其他 bean。 它还声明了请求的 URL 映射。
DefaultWsdl11Definition
使用XsdSchema
公开了标准的 WSDL 1.1。 Bean 名称studentDetailsWsdl
将是将公开的 wsdl 名称。 它可以在 http:// localhost:8080 / service / studentDetailsWsdl.wsdl 下找到。 这是在 Spring 公开合约优先的 wsdl 的最简单方法。
此配置还在内部使用 WSDL 位置 servlet 转换servlet.setTransformWsdlLocations( true )
。 如果我们看到导出的 WSDL,则soap:address
将具有localhost
地址。 同样,如果我们改为从分配给已部署机器的面向公众的 IP 地址访问 WSDL,我们将看到该地址而不是localhost
。 因此,端点 URL 根据部署环境是动态的。
?7. Spring Boot SOAP Web 服务演示
使用mvn clean install
进行 maven 构建,然后使用java -jar target\spring-boot-soap-service-0.0.1-SNAPSHOT.jar
命令启动应用程序。 这将在默认端口8080
中启动一台 tomcat 服务器,并将在其中部署应用程序。
1)现在转到http://localhost:8080/service/studentDetailsWsdl.wsdl
,查看 WSDL 是否正常运行。
WSDL 已生成
2)一旦成功生成了 WSDL,就可以使用该 WSDL 在 SOAP ui 中创建一个项目并测试该应用程序。 样品请求和响应如下。
请求:
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:sch="https://www.howtodoinjava.com/xml/school">
<soapenv:Header/>
<soapenv:Body>
<sch:StudentDetailsRequest>
<sch:name>Sajal</sch:name>
</sch:StudentDetailsRequest>
</soapenv:Body>
</soapenv:Envelope>
响应:
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/">
<SOAP-ENV:Header/>
<SOAP-ENV:Body>
<ns2:StudentDetailsResponse xmlns:ns2="https://www.howtodoinjava.com/xml/school">
<ns2:Student>
<ns2:name>Sajal</ns2:name>
<ns2:standard>5</ns2:standard>
<ns2:address>Pune</ns2:address>
</ns2:Student>
</ns2:StudentDetailsResponse>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>
SOAP UI 示例
在运行此示例之前,我们需要准备好一个 SOAP 服务,该服务将从该客户端代码中调用。?
运行此 SOAP 服务器项目后,将从http://localhost:8080/service/studentDetailsWsdl.wsdl
获取 WSDL。 将 WSDL 下载为studentDetailsWsdl.wsdl
,稍后将其放置在客户端项目的resources/wsdl
文件夹中,该文件夹将在下一步创建以生成客户端代理代码。
maven-jaxb2-plugin
插件 – 用于生成 JAXB 存根CommandLineRunner
?– 测试客户端代码WebServiceTemplate
创建 Spring 客户端仅从具有Web Services
依赖关系的?SPRING 初始化器站点创建一个 spring boot 项目。 选择依赖项并提供适当的 Maven GAV 坐标后,以压缩格式下载项目。 解压缩,然后将 eclipse 中的项目导入为 maven 项目。
Spring boot 项目生成
现在使用maven-jaxb2-plugin
?maven 插件生成?JAXB 注解的存根类。 为此,将此 maven 插件添加到项目的pom.xml
中。
pom.xml
<plugin>
<groupId>org.jvnet.jaxb2.maven2</groupId>
<artifactId>maven-jaxb2-plugin</artifactId>
<version>0.13.2</version>
<executions>
<execution>
<goals>
<goal>generate</goal>
</goals>
</execution>
</executions>
<configuration>
<generatePackage>com.example.howtodoinjava.schemas.school</generatePackage>
<generateDirectory>${project.basedir}/src/main/java</generateDirectory>
<schemaDirectory>${project.basedir}/src/main/resources/wsdl</schemaDirectory>
<schemaIncludes>
<include>*.wsdl</include>
</schemaIncludes>
</configuration>
</plugin>
此插件将在项目的src
目录的com.example.howtodoinjava.springbootsoapclient
包中生成类,并且此插件将检查类的生成时间戳,以便仅在WSDL
中发生任何更改时才生成这些类。
WebServiceTemplate
创建 SOAP 客户端创建一个名为SOAPConnector.java
的类,该类将充当对 Web 服务的所有请求的通用 Web 服务客户端。
SOAPConnector.java
package com.example.howtodoinjava.springbootsoapclient;
import org.springframework.ws.client.core.support.WebServiceGatewaySupport;
public class SOAPConnector extends WebServiceGatewaySupport {
public Object callWebService(String url, Object request){
return getWebServiceTemplate().marshalSendAndReceive(url, request);
}
}
SOAPConnector
类是对WebServiceGatewaySupport
的扩展,它基本上是通过getWebServiceTemplate()
方法提供的WebServiceTemplate
内部实现注入一个接口。WebServiceTemplate
来调用 SOAP 服务。Marshaller
和Unmarshaller
的 spring bean,它们将由配置类提供,我们将在下面看到。现在,我们需要创建一个用@Configuration
注解的配置类,该类将具有SOAPConnector
所需的必需的 bean 定义,以使其正常工作。
Config.java
package com.example.howtodoinjava.springbootsoapclient;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.oxm.jaxb.Jaxb2Marshaller;
@Configuration
public class Config {
@Bean
public Jaxb2Marshaller marshaller() {
Jaxb2Marshaller marshaller = new Jaxb2Marshaller();
// this is the package name specified in the <generatePackage> specified in
// pom.xml
marshaller.setContextPath("com.example.howtodoinjava.schemas.school");
return marshaller;
}
@Bean
public SOAPConnector soapConnector(Jaxb2Marshaller marshaller) {
SOAPConnector client = new SOAPConnector();
client.setDefaultUri("http://localhost:8080/service/student-details");
client.setMarshaller(marshaller);
client.setUnmarshaller(marshaller);
return client;
}
}
WebServiceGatewaySupport
需要Marshaller
和Unmarshaller
,它们是Jaxb2Marshaller
类的实例。com.example.howtodoinjava.schemas.school
作为 JAXB 类的基本包。 它将使用此包创建 JAXB 上下文。Jaxb2Marshaller
?bean 作为SOAPConnector
?bean 的Marshaller/Unmarshaller
。CommandLineRunner
测试为简单起见,我们将创建一个?Spring Boot 命令行运行程序,该加载程序将加载 spring 上下文并调用处理器方法,并将命令行参数传递给该方法。 实时地,我们需要用一些其他代码替换此命令行运行程序,这些代码将更适合企业。
我们需要在SpringBootApplication
类中添加此命令行运行器 bean,如下。
SpringBootSoapClientApplication.java
package com.example.howtodoinjava.springbootsoapclient;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import com.example.howtodoinjava.schemas.school.StudentDetailsRequest;
import com.example.howtodoinjava.schemas.school.StudentDetailsResponse;
@SpringBootApplication
public class SpringBootSoapClientApplication {
public static void main(String[] args) {
SpringApplication.run(SpringBootSoapClientApplication.class, args);
}
@Bean
CommandLineRunner lookup(SOAPConnector soapConnector) {
return args -> {
String name = "Sajal";//Default Name
if(args.length>0){
name = args[0];
}
StudentDetailsRequest request = new StudentDetailsRequest();
request.setName(name);
StudentDetailsResponse response =(StudentDetailsResponse) soapConnector.callWebService("http://localhost:8080/service/student-details", request);
System.out.println("Got Response As below ========= : ");
System.out.println("Name : "+response.getStudent().getName());
System.out.println("Standard : "+response.getStudent().getStandard());
System.out.println("Address : "+response.getStudent().getAddress());
};
}
}
在这里,我们从命令行获取搜索参数,并创建StudentDetailsRequest
对象,并使用SOAPConnector
调用 SOAP Web 服务。
打开application.properties
并添加以下配置
application.properties
server.port = 9090
logging.level.org.springframework.ws=TRACE
在这里,我们用server.port = 9090
将默认端口覆盖为9090
,因为您已经注意到我们的示例 SOAP 服务在默认端口8080
中运行,因为两个 Java 进程不能在同一端口中运行。
另外,我们正在通过logging.level.org.springframework.ws=TRACE
为org.springframework.ws
软件包启用TRACE
日志记录。 这将在控制台中打印 SOAP 负载。
这就是我们使用 Spring Boot 消费 SOAP 服务所需要做的一切,现在是时候进行测试了。
现在使用 maven 命令mvn clean install
来构建应用程序。 我们可以从命令提示符下通过命令java -jar target\spring-boot-soap-client-0.0.1-SNAPSHOT.jar Lokesh
调用命令行运行程序。
请注意,我们在此处传递了一个命令行参数Lokesh
,该参数将在CommandLineRunner
?bean 的查找方法中使用。 如果没有传递任何名称,我们将在该方法中传递一个默认名称。
调用命令行运行程序后,我们应该看到 SOAP 服务输出,并且响应已正确解组到 JAXB 对象StudentDetailsResponse
。 同样,我们可以在 TRACE 日志中看到完整的 SOAP 请求/响应,如下所示。
2017-10-09 23:20:45.548 TRACE 9204 --- [ main] o.s.ws.client.MessageTracing.received : Received response [<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"><SOAP-ENV:Header/><SOAP-ENV:Body><ns2:StudentDetailsResponse xmlns:ns2="https://www.howtodoinjava.com/xml/school"><ns2:Student><ns2:name>Sajal</ns2:name><ns2:standard>5</ns2:standard><ns2:address>Pune</ns2:address></ns2:Student></ns2:StudentDetailsResponse></SOAP-ENV:Body></SOAP-ENV:Envelope>] for request [<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"><SOAP-ENV:Header/><SOAP-ENV:Body><ns2:StudentDetailsRequest xmlns:ns2="https://www.howtodoinjava.com/xml/school"><ns2:name>Sajal</ns2:name></ns2:StudentDetailsRequest></SOAP-ENV:Body></SOAP-ENV:Envelope>]
Got Response As below ========= :
Name : Lokesh
Standard : 6
Address : Delhi