zoukankan      html  css  js  c++  java
  • 30分钟学会使用Spring Web Services基础开发

    时隔一年终于又推出了一篇30分钟系列,上一篇《30分钟学会反向Ajax》是2016年7月的事情了。时光荏苒,岁月穿梭。虽然一直还在从事Java方面的开发工作,但是私下其实更喜欢使用C++。不过今天,我们要再次回归到Java的主题,来谈一谈如何使用——Spring Web Services框架。

    Spring Web Services(下简称ws)本质上是基于SpringBoot的项目,因此如果有对SpringBoot不太了解的同学,回头再来看比较合适。

    ws分为server端与client端两个部分,本文旨在介绍框架搭建的流程与重点。

    一、ws.server端搭建

    建立Server的关键是首先建立xsd文件。xsd文件是xml文件的定义与基础,你希望别人如何访问与获取你的数据都需要在xsd文件中说明。

    countries.xsd

    <xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
        xmlns:tns="http://learnhow.org/ws/schema" targetNamespace="http://learnhow.org/ws/schema"
        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:element name="language" type="tns:language" />
            </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:complexType name="language">
            <xs:sequence>
                <xs:element name="name" type="xs:string" />
            </xs:sequence>
        </xs:complexType>
    </xs:schema>

    users.xsd

    <xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
        xmlns:tns="http://learnhow.org/ws/schema" targetNamespace="http://learnhow.org/ws/schema"
        elementFormDefault="qualified">
        <xs:element name="getUserRequest">
            <xs:complexType>
                <xs:sequence>
                    <xs:element name="name" type="xs:string" />
                </xs:sequence>
            </xs:complexType>
        </xs:element>
    
        <xs:element name="getUserResponse">
            <xs:complexType>
                <xs:sequence>
                    <xs:element name="user" type="tns:user" />
                </xs:sequence>
            </xs:complexType>
        </xs:element>
        
        <xs:complexType name="user">
            <xs:sequence>
                <xs:element name="name" type="xs:string" />
                <xs:element name="gender" type="tns:gender" />
                <xs:element name="age" type="xs:int" />
                <xs:element name="address" type="xs:string" />
            </xs:sequence>
        </xs:complexType>
        
        <xs:simpleType name="gender">
            <xs:restriction base="xs:string">
                <xs:enumeration value="MALE" />
                <xs:enumeration value="FEMALE" />
            </xs:restriction>
        </xs:simpleType>
    </xs:schema>

    这两个文件默认请存放于 src/main/resources 目录下,如下图所示:

    建立完成以后我们可以着手编写pom.xml文件,即建立工程依赖。

    <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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
        <modelVersion>4.0.0</modelVersion>
    
        <groupId>org.learnhow</groupId>
        <artifactId>ws.server</artifactId>
        <version>0.0.1-SNAPSHOT</version>
        <packaging>jar</packaging>
    
        <name>ws.server</name>
        <url>http://maven.apache.org</url>
        <parent>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-parent</artifactId>
            <version>1.5.4.RELEASE</version>
        </parent>
        <properties>
            <maven.compiler.source>1.8</maven.compiler.source>
            <maven.compiler.target>1.8</maven.compiler.target>
            <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        </properties>
    
        <dependencies>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-web-services</artifactId>
            </dependency>
            <dependency>
                <groupId>wsdl4j</groupId>
                <artifactId>wsdl4j</artifactId>
            </dependency>
            <dependency>
                <groupId>junit</groupId>
                <artifactId>junit</artifactId>
                <scope>test</scope>
            </dependency>
        </dependencies>
        <build>
            <plugins>
                <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>
            </plugins>
        </build>
    </project>

    重点是最后的一项maven plugin,它会读取resources目录下的xsd文件并在 src/main/java 目录下建立.java文件。需要注意的是代码的package路径是通过xsd的targetNamespace事先指定的。

    代码文件被maven创建完成以后代表第一段工作顺利完成。下面我们需要人工编写Endpoint类,即建立对外访问的服务接口。通常你提供了几分xsd文件就应该创建几个Endpoint类。Endpoint本质上是接收一个request,然后经过你的业务逻辑再返回一个response。与传统意义上的浏览器不同,后者通常传输json字符串,而前者则是xml。

    CountryEndpoint

    package org.learnhow.ws.server;
    
    import org.learnhow.ws.schema.GetCountryRequest;
    import org.learnhow.ws.schema.GetCountryResponse;
    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;
    
    @Endpoint
    public class CountryEndpoint {
        private static final String NAMESPACE_URI = "http://learnhow.org/ws/schema";
        @Autowired
        private 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;
        }
    }

    UserEndpoint

    package org.learnhow.ws.server;
    
    import org.learnhow.ws.schema.GetUserRequest;
    import org.learnhow.ws.schema.GetUserResponse;
    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;
    
    @Endpoint
    public class UserEndpoint {
        private static final String NAMESPACE_URI = "http://learnhow.org/ws/schema";
        @Autowired
        private UserRepository userRepository;
    
        @PayloadRoot(namespace = NAMESPACE_URI, localPart = "getUserRequest")
        @ResponsePayload
        public GetUserResponse getUser(@RequestPayload GetUserRequest request) {
            GetUserResponse response = new GetUserResponse();
            response.setUser(userRepository.findUser(request.getName()));
            return response;
        }
    }

    很显然你的业务逻辑应该封装在CountryRepository和UserRepository对象里。接下来创建CountryRepository对象。

    CountryRepository

    package org.learnhow.ws.server;
    
    import java.util.HashMap;
    import java.util.Map;
    
    import javax.annotation.PostConstruct;
    
    import org.learnhow.ws.schema.Country;
    import org.learnhow.ws.schema.Currency;
    import org.learnhow.ws.schema.Language;
    import org.springframework.stereotype.Component;
    
    @Component
    public class CountryRepository {
        private static final Map<String, Country> countries = new HashMap<>();
    
        @PostConstruct
        public void initData() {
            Country spain = new Country();
            spain.setName("Spain");
            spain.setCapital("Madrid");
            spain.setCurrency(Currency.EUR);
            spain.setPopulation(46704314);
            Language spanish = new Language();
            spanish.setName("spanish");
            spain.setLanguage(spanish);
    
            Country poland = new Country();
            poland.setName("Poland");
            poland.setCapital("Warsaw");
            poland.setCurrency(Currency.PLN);
            poland.setPopulation(38186860);
            Language polish = new Language();
            polish.setName("polish");
            poland.setLanguage(polish);
    
            Country uk = new Country();
            uk.setName("United Kingdom");
            uk.setCapital("London");
            uk.setCurrency(Currency.GBP);
            uk.setPopulation(63705000);
            Language english = new Language();
            english.setName("english");
            uk.setLanguage(english);
    
            countries.put(spain.getName(), spain);
            countries.put(poland.getName(), poland);
            countries.put(uk.getName(), uk);
        }
        
        public Country findCountry(String name) {
            return countries.get(name);
        }
    }

    UserRepository(略)

    最后是编写configuration,它是整个框架调用的核心。

    package org.learnhow.ws.server;
    
    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 WebServiceConfig extends WsConfigurerAdapter {
        @Bean
        public ServletRegistrationBean messageDispatcherServlet(ApplicationContext applicationContext) {
            MessageDispatcherServlet servlet = new MessageDispatcherServlet();
            servlet.setApplicationContext(applicationContext);
            servlet.setTransformWsdlLocations(true);
            return new ServletRegistrationBean(servlet, "/ws/*");
        }
    
        @Bean(name = "countries")
        public DefaultWsdl11Definition defaultWsdl11DefinitionCountry() {
            DefaultWsdl11Definition wsdl11Definition = new DefaultWsdl11Definition();
            wsdl11Definition.setPortTypeName("CountriesPort");
            wsdl11Definition.setLocationUri("/ws");
            wsdl11Definition.setTargetNamespace("http://learnhow.org/ws/schema");
            wsdl11Definition.setSchema(countriesSchema());
            return wsdl11Definition;
        }
    
        @Bean(name = "users")
        public DefaultWsdl11Definition defaultWsdl11DefinitionUser() {
            DefaultWsdl11Definition wsdl11Definition = new DefaultWsdl11Definition();
            wsdl11Definition.setPortTypeName("CountriesPort");
            wsdl11Definition.setLocationUri("/ws");
            wsdl11Definition.setTargetNamespace("http://learnhow.org/ws/schema");
            wsdl11Definition.setSchema(usersSchema());
            return wsdl11Definition;
        }
    
        @Bean
        public XsdSchema countriesSchema() {
            return new SimpleXsdSchema(new ClassPathResource("countries.xsd"));
        }
    
        @Bean
        public XsdSchema usersSchema() {
            return new SimpleXsdSchema(new ClassPathResource("users.xsd"));
        }
    
    }

    最后一步:编写启动项Application

    package org.learnhow.ws;
    
    import org.springframework.boot.SpringApplication;
    import org.springframework.boot.autoconfigure.SpringBootApplication;
    
    @SpringBootApplication
    public class Application {
        public static void main(String[] args) {
            SpringApplication.run(Application.class, args);
        }
        
    }

    完成的目录结构如下:

    二、测试

    启动application,打开浏览器访问:http://localhost:8080/ws/countries.wsdl与http://localhost:8080/ws/users.wsdl 如果页面分别展示了两份xml文件代表服务器已经可以正常运行了。也可以用SoapUI Pro进一步测试数据的读取和发送是否正常。

    三、ws.client端搭建

    如果说server端是通过xsd产生java与WSDL的过程那么client端就恰恰相反。我们还是使用maven工具通过服务器暴露在外的wsdl文件建立java对象。首先编辑pom.xml文件配置依赖和wsdl访问路径。

    <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 http://maven.apache.org/maven-v4_0_0.xsd">
        <modelVersion>4.0.0</modelVersion>
        <groupId>org.learnhow</groupId>
        <artifactId>ws.client</artifactId>
        <packaging>jar</packaging>
        <version>0.0.1-SNAPSHOT</version>
        
        <properties>
            <maven.compiler.source>1.8</maven.compiler.source>
            <maven.compiler.target>1.8</maven.compiler.target>
        </properties>
        <parent>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-parent</artifactId>
            <version>1.5.4.RELEASE</version>
        </parent>
        <dependencies>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter</artifactId>
            </dependency>
            <dependency>
                <groupId>org.springframework.ws</groupId>
                <artifactId>spring-ws-core</artifactId>
            </dependency>
        </dependencies>
        <build>
            <plugins>
                <plugin>
                    <groupId>org.springframework.boot</groupId>
                    <artifactId>spring-boot-maven-plugin</artifactId>
                </plugin>
                <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>
                        <schemaLanguage>WSDL</schemaLanguage>
                        <generatePackage>ws.wsdl</generatePackage>
                        <schemas>
                            <schema>
                                <url>http://localhost:8080/ws/countries.wsdl</url>
                            </schema>
                            <schema>
                                <url>http://localhost:8080/ws/users.wsdl</url>
                            </schema>
                        </schemas>
                    </configuration>
                </plugin>
            </plugins>
        </build>
    </project>

    这里要注意,如果此时你已经将server的进程停止,也就是wsdl无法访问,pom.xml文件会报错。看上去大概会像这样:

    正常情况下maven会自动帮你在target目录下建立java对象,目录结构如下:

    如果你发现无法正常建立java对象,请首先检查以下两点:

    maven引入的依赖是否完整:国内的网络环境不是很好,有时候通过maven搭建环境经常会在运行时报出各种莫名其妙的错误。其中绝大多数其实都是由于依赖引入不完整造成的。此时你可能需要对Maven Dependencies目录下的jar包逐一检查。

    maven结构错误:maven的版本很多,不同的版本间可能在元素的结构定义上有所差距。如果在<plugins>节点报错可以考虑在外层再包一层<pluginManagement>节点。具体原因我也没有深究,只能说“有时管用”,如果你对maven有深入的了解也希望告诉我。

    如果以上环境你进行的都很顺利,那么恭喜你80%的工作已经完成了。

    下面是编写client客户端代码:

    package org.learnhow.ws.client;
    
    import org.springframework.ws.client.core.support.WebServiceGatewaySupport;
    import org.springframework.ws.soap.client.core.SoapActionCallback;
    
    import ws.wsdl.GetCountryRequest;
    import ws.wsdl.GetCountryResponse;
    
    public class CountryClient extends WebServiceGatewaySupport {
        public static final String URI = "http://localhost:8080/ws";
        public static final String SOAPACTION = "http://learnhow.org/ws/schema/getUserRequest";
    
        public GetCountryResponse getCountry(String countryName) {
            GetCountryRequest request = new GetCountryRequest();
            request.setName(countryName);
    
            GetCountryResponse response = (GetCountryResponse) getWebServiceTemplate().marshalSendAndReceive(URI, request,
                    new SoapActionCallback(SOAPACTION));
    
            return response;
        }
    }

    UserClient(略)

    然后依然是创建configuration供框架调用

    package org.learnhow.ws.client;
    
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.oxm.jaxb.Jaxb2Marshaller;
    
    @Configuration
    public class AppConfiguration {
        @Bean
        public Jaxb2Marshaller marshaller() {
            Jaxb2Marshaller marshaller = new Jaxb2Marshaller();
            marshaller.setContextPath("ws.wsdl");
            return marshaller;
        }
    
        @Bean("country")
        public CountryClient counrtyClient(Jaxb2Marshaller marshaller) {
            CountryClient client = new CountryClient();
            client.setDefaultUri(CountryClient.URI);
            client.setMarshaller(marshaller);
            client.setUnmarshaller(marshaller);
            return client;
        }
    
        @Bean("user")
        public UserClient userClient(Jaxb2Marshaller marshaller) {
            UserClient client = new UserClient();
            client.setDefaultUri(UserClient.URI);
            client.setMarshaller(marshaller);
            client.setUnmarshaller(marshaller);
            return client;
        }
    }

    最后是编写Application启动项

    package org.learnhow.ws;
    
    import org.learnhow.ws.client.CountryClient;
    import org.learnhow.ws.client.UserClient;
    import org.springframework.boot.CommandLineRunner;
    import org.springframework.boot.SpringApplication;
    import org.springframework.boot.autoconfigure.SpringBootApplication;
    import org.springframework.context.annotation.Bean;
    
    import ws.wsdl.GetCountryResponse;
    import ws.wsdl.GetUserResponse;
    
    @SpringBootApplication
    public class Application {
        public static void main(String[] args) {
            SpringApplication.run(Application.class, args);
        }
    
        @Bean
        CommandLineRunner lookup(CountryClient client) {
            return args -> {
                String countryName = "Spain";
    
                if (args.length > 0) {
                    countryName = args[0];
                }
                GetCountryResponse response = client.getCountry(countryName);
                System.out.println("response: " + response.getCountry().getName());
            };
        }
    }

    运行application,如果你能看到控制台有 "response: Spain" 打出代表数据已经能够正常获取。

    后记:

    本文的代码逻辑主要参考了Spring Web Services官网文档。另外WebService除了通过Spring还有多种实现手段,感兴趣的同学可以看看如何使用wsimport工具以及Tomcat发布WebService的例子。这里不再赘述。

  • 相关阅读:
    html 上传图片前预览
    php获取当月天数及当月第一天及最后一天、上月第一天及最后一天实现方法
    php 计算 pdf文件页数
    php 获取半年内每个月的订单数量, 总价, 月份
    php 获取两个数组之间不同的值
    小程序支付功能
    关于nginx的Job for nginx.service failed because the control process exited with error code.错误
    linux 安装 Apollo
    MongoDB待续。。。
    ABP vNext...待续
  • 原文地址:https://www.cnblogs.com/learnhow/p/7205524.html
Copyright © 2011-2022 走看看