上一篇:springboot 1.5.4 之监控Actuator(十四)


 

Spring Boot整合web service

经验引用

之前用webserviceCXF框架,

 

很方便与简洁,但是悲催的是在部署到生产环境的WebSphere(was平台)下后,不能正常运行.

 

网上一查,原来WebSphereCXF的冲突问题由来已久,解决方案也五花八门,会有不必要的麻烦.既然如此趁项目的web service还在刚整合阶段,换个组件吧.

 

问了其它项目组同事以前是怎么实现的,说就是因为冲突问题以前都是采用了httpClient之类的组装xml发送原生http请求调用的.

 

处理方式欠妥,作者当然不能接受。既然spring能在WebSphere下正常运行,那么spring的组件能够成功运行的可能性相对较大。

 

研究考虑之后,决定选用spring-ws来实现webservice。事实证明选择是正确的。

spring-boot相关项目源码,

码云地址:https://git.oschina.net/wyait/springboot1.5.4.git

github地址https://github.com/wyait/spring-boot-1.5.4.git

 

spring-ws的资料相对较少,不像cxf那样一找就是一大堆,不过好在有官方示例和文档。

 

官方示例中使用了spring boot,这跟我当前的环境不谋而合,不过它示例了多个构建工具和Groovy等,看起来比较复杂难懂一些,这里我们就以单纯的maven来实现。

 

1      web Service服务端

新建项目:ws-server(源码地址:

码云地址:https://git.oschina.net/wyait/springboot1.5.4.git

github地址https://github.com/wyait/spring-boot-1.5.4.git


 

spring boot的工程,除了spring boot外还需要添加spring-wswsdl4j的依赖,当然后面生成代码还需要添加mavenjaxb2插件

1.1    pom导入依赖

 

<dependency>

        <groupId>org.springframework.boot</groupId>

        <artifactId>spring-boot-starter-ws</artifactId>

        <version>1.4.5.RELEASE</version>

      </dependency>

      <dependency>

        <groupId>wsdl4j</groupId>

        <artifactId>wsdl4j</artifactId>

      </dependency>

 

1.2    编写schema文件

spring-ws的发布,都是以一个schema文件(xsd)定义开始的,它描述了webservice 的参数以及返回的数据。

 

这是官方示例给出的countries.xsd,这里以它为例,更改下命名空间,因为jaxb2插件自动生成代码是以命名空间来确定包名的。手动生成不影响!

<xs:schemaxmlns:xs="http://www.w3.org/2001/XMLSchema"xmlns:tns="http://www.wyait.com/ws"

           targetNamespace="http://www.wyait.com/ws"elementFormDefault="qualified">

        <xs:elementname="getCountryRequest">

            <xs:complexType>

                <xs:sequence>

                    <xs:elementname="name" type="xs:string"/>

                </xs:sequence>

            </xs:complexType>

        </xs:element>

        <xs:elementname="getCountryResponse">

            <xs:complexType>

                <xs:sequence>

                    <xs:elementname="country" type="tns:country"/>

                </xs:sequence>

            </xs:complexType>

        </xs:element>

        <xs:complexTypename="country">

            <xs:sequence>

                <xs:elementname="name" type="xs:string"/>

                <xs:elementname="population" type="xs:int"/>

                <xs:elementname="capital" type="xs:string"/>

                <xs:elementname="currency" type="tns:currency"/>

            </xs:sequence>

        </xs:complexType>

        <xs:simpleTypename="currency">

            <xs:restrictionbase="xs:string">

                <xs:enumerationvalue="GBP"/>

                <xs:enumerationvalue="EUR"/>

                <xs:enumerationvalue="PLN"/>

            </xs:restriction>

        </xs:simpleType>

    </xs:schema>

1.3    jaxb2插件配置

pom文件配置jaxb2插件:

<plugin>

           <groupId>org.codehaus.mojo</groupId>

           <artifactId>jaxb2-maven-plugin</artifactId>

           <version>2.2</version>

           <configuration>

           <schemaDirectory>${project.basedir}/src/main/resources/schema</schemaDirectory>

              <outputDirectory>${project.basedir}/src/main/java</outputDirectory>

              <clearOutputDir>false</clearOutputDir>

           </configuration>

        </plugin>

eclipse开发工具:选中countries.xsd文件,右键:

wKioL1nPWDny8h3XAAB7xmx6idc226.png

IDEA开发工具:

coruntries.xsd右键,然后选中web service那一项,generatejava code from xml schema using jaxb

wKiom1nPWIaj2sJeAAFSKsmBnC8596.png

选择要生成代码的包位置!生成代码结果:

 

wKioL1nPWEyh8bfrAAAcbmMgbZ0771.png

1.4    编写EndPoint

我们就不再像spring-ws官方那样再建一个Repository了,这里直接返回。需要注意PayloadRoot注解当中的namespacelocalPart需要和xsd中对应。

/**

 *

 * @项目名称:ws-server

 * @类名称:CountryEndPoint

 * @类描述:编写endpoint

 * @创建人:wyait

 * @创建时间:2017714上午11:02:49

 * @version

 */

@Endpoint

public class CountryEndPoint {

 

   privatestatic final String NAMESPACE_URI = "http://www.wyait.com/ws";

 

   @PayloadRoot(namespace= NAMESPACE_URI, localPart = "getCountryRequest")

   @ResponsePayload

   publicGetCountryResponse getCountry(

        @RequestPayloadGetCountryRequest request) {

      GetCountryResponseresponse = new GetCountryResponse();

      Countrypoland = new Country();

      poland.setName("Poland-"+ request.getName());

      poland.setCapital("Warsaw");

      poland.setCurrency(Currency.PLN);

      poland.setPopulation(38186860);

      response.setCountry(poland);

      returnresponse;

   }

}

 

1.5    Spring Boot整合web Service

/**

 *

 * @项目名称:ws-server

 * @类名称:WebServiceConfig

 * @类描述:spring boot整合web service

 * @创建人:wyait

 * @创建时间:2017714上午11:24:22

 * @version

 */

@EnableWs

@Configuration

public class WebServiceConfigextends WsConfigurerAdapter {

   @Bean

   publicServletRegistrationBean messageDispatcherServlet(

        ApplicationContextapplicationContext) {

      MessageDispatcherServletservlet = new MessageDispatcherServlet();

      servlet.setApplicationContext(applicationContext);

      servlet.setTransformWsdlLocations(true);

      returnnew ServletRegistrationBean(servlet, "/ws/*");

   }

 

   @Bean(name= "countries")

   publicDefaultWsdl11Definition defaultWsdl11Definition(

        XsdSchemacountriesSchema) {

      DefaultWsdl11Definitionwsdl11Definition = new DefaultWsdl11Definition();

      wsdl11Definition.setPortTypeName("CountriesPort");

      wsdl11Definition.setSchema(countriesSchema);

      returnwsdl11Definition;

   }

 

   @Bean

   publicXsdSchema countriesSchema() {

      returnnew SimpleXsdSchema(

           newClassPathResource("schema/countries.xsd"));

   }

}

 

到这里spring-ws的所有配置和工作都已经完成了,上面的DefaultWsdl11Definitionid默认就是发布的ws的访问路径。

 

1.6    启动项目

启动后访问 http://localhost:8080/ws/countries.wsdl 发现web service已经成功发布了。

 

wKiom1nPWJuRaXmkAADfq94uGXg010.png

 

这里要注意一下spring-ws发布的webservice是以后缀.wsdl访问的,跟传统的?wsdl不大一样,也看过它的源码,发现是在判断后缀时写死的,所以没办法配置修改了。

 

还有就是spring-ws实际上把发布wsdl和真正的服务实现Endpoint分开了,如果你的Endpoint不正确,很可能会出现浏览器访问.wsdl地址看起来正常而客户端调用却出现Not Found 404的错误。

 

2      web Service客户端

前面我们已经整合spring-ws实现了webservice的服务端:Spring Boot整合spring-ws开发web service客户端

 

接下来就是实现客户端进行调用了。

 

新建:ws-client(项目源码:https://git.oschina.net/wyait/springboot1.5.4.git

2.1    添加依赖

<dependency>

        <groupId>org.springframework.boot</groupId>

        <artifactId>spring-boot-starter-ws</artifactId>

        <version>1.4.5.RELEASE</version>

      </dependency>

      <dependency>

        <groupId>wsdl4j</groupId>

        <artifactId>wsdl4j</artifactId>

      </dependency>

 

 

服务端由一个xsd文件开始,客户端则是由一个wsdl文件开始。

 

获取wsdl文件也十分简单,用浏览器访问webservice地址,然后另存为即可。当然也可以直接用url地址来生成代码。

方式一:本地另存:

完整的wsdl文件如下:

<wsdl:definitionsxmlns:wsdl="http://schemas.xmlsoap.org/wsdl/"

   xmlns:sch="http://www.wyait.com/ws"xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"

   xmlns:tns="http://www.wyait.com/ws"targetNamespace="http://www.wyait.com/ws">

   <wsdl:types>

      <xs:schemaxmlns:xs="http://www.w3.org/2001/XMLSchema"

        elementFormDefault="qualified"targetNamespace="http://www.wyait.com/ws">

        <xs:elementname="getCountryRequest">

           <xs:complexType>

              <xs:sequence>

                 <xs:elementname="name" type="xs:string" />

              </xs:sequence>

           </xs:complexType>

        </xs:element>

        <xs:elementname="getCountryResponse">

           <xs:complexType>

              <xs:sequence>

                 <xs:elementname="country" type="tns:country" />

              </xs:sequence>

           </xs:complexType>

        </xs:element>

        <xs:complexTypename="country">

           <xs:sequence>

              <xs:elementname="name" type="xs:string" />

              <xs:elementname="population" type="xs:int" />

              <xs:elementname="capital" type="xs:string" />

              <xs:elementname="currency" type="tns:currency" />

           </xs:sequence>

        </xs:complexType>

        <xs:simpleTypename="currency">

           <xs:restrictionbase="xs:string">

              <xs:enumerationvalue="GBP" />

              <xs:enumerationvalue="EUR" />

              <xs:enumerationvalue="PLN" />

           </xs:restriction>

        </xs:simpleType>

      </xs:schema>

   </wsdl:types>

   <wsdl:messagename="getCountryResponse">

      <wsdl:partelement="tns:getCountryResponse" name="getCountryResponse"></wsdl:part>

   </wsdl:message>

   <wsdl:messagename="getCountryRequest">

      <wsdl:partelement="tns:getCountryRequest"name="getCountryRequest"></wsdl:part>

   </wsdl:message>

   <wsdl:portTypename="CountriesPort">

      <wsdl:operationname="getCountry">

        <wsdl:inputmessage="tns:getCountryRequest"name="getCountryRequest"></wsdl:input>

        <wsdl:outputmessage="tns:getCountryResponse"name="getCountryResponse"></wsdl:output>

      </wsdl:operation>

   </wsdl:portType>

   <wsdl:bindingname="CountriesPortSoap11" type="tns:CountriesPort">

      <soap:bindingstyle="document"

        transport="http://schemas.xmlsoap.org/soap/http"/>

      <wsdl:operationname="getCountry">

        <soap:operationsoapAction="" />

        <wsdl:inputname="getCountryRequest">

           <soap:bodyuse="literal" />

        </wsdl:input>

        <wsdl:outputname="getCountryResponse">

           <soap:bodyuse="literal" />

        </wsdl:output>

      </wsdl:operation>

   </wsdl:binding>

   <wsdl:servicename="CountriesPortService">

      <wsdl:portbinding="tns:CountriesPortSoap11"name="CountriesPortSoap11">

        <soap:address/>

      </wsdl:port>

   </wsdl:service>

</wsdl:definitions>

 

方式二:jaxb2插件配置生成wsdl文件

跟服务端根据xsd来生成代码类似,客户端同样可以根据wsdl来生成代码。maven插件依赖:

<plugin>

           <groupId>org.jvnet.jaxb2.maven2</groupId>

           <artifactId>maven-jaxb2-plugin</artifactId>

           <version>0.12.3</version>

           <executions>

              <execution>

                 <goals>

                    <goal>generate</goal>

                 </goals>

              </execution>

           </executions>

           <configuration>

              <schemaLanguage>WSDL</schemaLanguage>

              <generatePackage>com.wyait.ws.domain</generatePackage>

              <generateDirectory>${basedir}/src/main/java</generateDirectory>

              <schemas>

                 <schema>

                    <fileset>

                      <!--Defaults to schemaDirectory. -->

                       <directory>${basedir}/src/main/resources/schema</directory>

                      <!--Defaults to schemaIncludes. -->

                      <includes>

                         <include>*.wsdl</include>

                      </includes>

                      <!--Defaults to schemaIncludes -->

                      <!--<excludes>-->

                      <!--<exclude>*.xs</exclude>-->

                      <!--</excludes>-->

                    </fileset>

                    <!--<url>http://localhost:8080/ws/countries.wsdl</url>-->

                 </schema>

              </schemas>

           </configuration>

        </plugin>

配置完,install将生成客户端代码。这里生成的代码跟我们前面发布的服务端代码应该是一样的,当然包名可能不同这个由自己指定。

 

在生成代码的同时会生成META-INF文件夹,这个可以移到resources目录下或者直接删除都没有关系。生成后的项目结构图:

wKioL1nPWGWSZEYDAAA60ofoGdM026.png

 

2.2    编写ws客户端

编写ws 客户端代码:

public class WsClient extendsWebServiceGatewaySupport {

   publicGetCountryResponse getCountry(String name) {

      GetCountryRequestrequest = new GetCountryRequest();

      request.setName(name);

      GetCountryResponseresponse = (GetCountryResponse) getWebServiceTemplate()

           .marshalSendAndReceive(

                 "http://http://127.0.0.1:9111/ws/countries.wsdl",

                 request);

      returnresponse;

   }

}

 

2.3    spring boot配置ws客户端

编写完一切代码之后,同样需要配置到spring boot才行,ContextPath指定刚才生成代码所在的包名,它会到该包下去寻找相应的类自动进行数据转换:

@Configuration

public class WSConfig {

   @Bean

   publicJaxb2Marshaller marshaller() {

      Jaxb2Marshallermarshaller = new Jaxb2Marshaller();

      marshaller.setContextPath("com.wyait.ws.domain");

      returnmarshaller;

   }

 

   @Bean

   publicWsClient wsClient(Jaxb2Marshaller marshaller) {

      WsClientclient = new WsClient();

      client.setDefaultUri("http://127.0.0.1:9111/ws/countries.wsdl");

      client.setMarshaller(marshaller);

      client.setUnmarshaller(marshaller);

      returnclient;

   }

}

 

2.4    编写controller

使用了RestController,直接将调用ws返回的数据用json格式输出到页面。

@RestController

public class IndexController {

    @Autowired

    private WsClient wsClient;

    @RequestMapping("callws")

    public Object callWs() {

        GetCountryResponse response =wsClient.getCountry("hello");

        return response.getCountry();

    }

}

 

端口改为:9112 避免和服务端端口9111冲突

 

启动,访问:http://127.0.0.1:9112/callws

wKiom1nPWLTgKNS3AAAI6JUg9JI968.png

 

回顾1.6章节末尾:

“还有就是spring-ws实际上把发布wsdl和真正的服务实现Endpoint分开了,如果你的Endpoint不正确,很可能会出现浏览器访问.wsdl地址看起来正常而客户端调用却出现Not Found 404的错误。”

 

排查ws服务端endpoint代码,没问题。是包命名的时候,单词写错了:

wKiom1nPWLzjay8iAABwAvj0AqY160.png

wyati改成:wyait。启动,访问:http://127.0.0.1:9112/callws

wKioL1nPWH-j8Pt4AAAGhicN8UA744.png

 

3      附录

ws-server /ws-client项目源码:

码云地址:https://git.oschina.net/wyait/springboot1.5.4.git

github地址https://github.com/wyait/spring-boot-1.5.4.git

 

spring boot系列文章:

spring boot 1.5.4 概述(一)

spring boot 1.5.4 入门和原理(二)

spring boot 1.5.4 之web开发(三)

spring boot 1.5.4 整合JSP(四)

spring boot 1.5.4 集成devTools(五)

spring boot 1.5.4 集成JdbcTemplate(六)

spring boot 1.5.4 集成spring-Data-JPA(七)

spring boot 1.5.4 配置文件详解(八)

spring boot 1.5.4 统一异常处理(九)

spring boot 1.5.4 定时任务和异步调用(十)

spring boot 1.5.4 整合log4j2(十一)

spring boot 1.5.4 整合 mybatis(十二)

spring boot 1.5.4 整合 druid(十三)

spring boot 1.5.4 之监控Actuator(十四)

spring boot 1.5.4 整合webService(十五)

spring boot 1.5.4 整合redis、拦截器、过滤器、监听器、静态资源配置(十六)

spring boot 1.5.4 整合rabbitMQ(十七)

spring boot 1.5.4 集成Swagger2构建Restful API(十八)

spring boot 1.5.9 整合redis(十九