首頁>技術>

Web Service,即“Web 服務”,簡寫為 WS(不是WebSocket哦),從字面上理解,它其實就是“基於 Web 的服務”。而服務卻是雙方的,有服務需求方,就有服務提供方。服務提供方對外發布服務,服務需求方呼叫服務提供方所釋出的服務。

現階段,國內網際網路企業使用spring boot、spring cloud較多,而這兩者都是基於http通訊的。但webservice仍在銀行、保險、金融機構等相關企業大量使用。那在與這些傳統企業對接時,spring boot 如何整合webservice將是一個亟需解決的問題。

目前較為方便的整合方案主要有apache提供的CXF以及Spring 自己提供的Spring-WS

Apache CXF -- WS-Security

Spring Web Services

本篇教材主要是讓大家快速上手cxf,實現基於Spring Boot 的webservice開發。

專案原始碼: https://github.com/ouyushan/spring-webservice-samples

spring-cxf參考原始碼: https://github.com/code-not-found/cxf-jaxws

spring-ws參考原始碼: https://github.com/code-not-found/spring-ws

二、開發環境

ide::IDEA

jdk:1.8

maven:3.6.2

spring boot:2.4.0

cxf:3.4.1

三、實現步驟3.1、新建父工程

新建父工程spring-webservice-samples,對所有示例子模組進行統一管理。

spring boot與cxf的簡單整合只需引入cxf-spring-boot-starter-jaxws,該依賴會自動引入web及cxf基礎包。

<dependency>    <groupId>org.apache.cxf</groupId>    <artifactId>cxf-spring-boot-starter-jaxws</artifactId>    <version>3.4.1</version></dependency>
3.2、新建子工程

新建子工程spring-cxf-client-begin、spring-cxf-server-begin,分別對應webservice 服務的客戶端和服務端。

spring-webservice-samples工程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 http://maven.apache.org/xsd/maven-4.0.0.xsd">    <modelVersion>4.0.0</modelVersion>    <groupId>org.ouyushan</groupId>    <artifactId>spring-webservice-samples</artifactId>    <version>1.0.0.BUILD-SNAPSHOT</version>    <packaging>pom</packaging>    <name>Spring Web Services Samples</name>    <inceptionYear>2020</inceptionYear>    <modules>        <module>spring-cxf-client-begin</module>        <module>spring-cxf-server-begin</module>    </modules>    <dependencies>        <dependency>            <groupId>org.apache.cxf</groupId>            <artifactId>cxf-spring-boot-starter-jaxws</artifactId>            <version>3.4.1</version>        </dependency>        <dependency>            <groupId>org.apache.commons</groupId>            <artifactId>commons-lang3</artifactId>            <version>3.11</version>        </dependency>        <dependency>            <groupId>org.projectlombok</groupId>            <artifactId>lombok</artifactId>            <optional>true</optional>        </dependency>        <dependency>            <groupId>com.alibaba</groupId>            <artifactId>fastjson</artifactId>            <version>1.2.74</version>        </dependency>    </dependencies></project>

spring-cxf-server-begin服務pom檔案

3.4、服務端釋出服務

服務端主要基於User實體類提供使用者查詢等相關功能,然後透過cxf向外釋出介面供客戶端呼叫。

3.4.1、新建實體類User
package org.ouyushan.cxf.entity;import java.io.Serializable;/** * @Description: * @Author: ouyushan * @Email: ouyushan@hotmail.com * @Date: 2020/11/26 14:42 */public class User implements Serializable {    private static final long serialVersionUID = -3628469724795296287L;    private String userId;    private String userName;    private String email;    public String getUserId() {        return userId;    }    public void setUserId(String userId) {        this.userId = userId;    }    public String getUserName() {        return userName;    }    public void setUserName(String userName) {        this.userName = userName;    }    public String getEmail() {        return email;    }    public void setEmail(String email) {        this.email = email;    }    public User() {    }    public User(String userId, String userName, String email) {        this.userId = userId;        this.userName = userName;        this.email = email;    }    @Override    public String toString() {        return "User{" +                "userId='" + userId + '\'' +                ", userName='" + userName + '\'' +                ", email='" + email + '\'' +                '}';    }}
3.4.2、新建UserService介面類
package org.ouyushan.cxf.service;import org.ouyushan.cxf.entity.User;import javax.jws.WebMethod;import javax.jws.WebParam;import javax.jws.WebService;/** * @Description: * @Author: ouyushan * @Email: ouyushan@hotmail.com * @Date: 2020/11/26 14:42 */@WebService(targetNamespace = "http://ws.cxf.ouyushan.org/")public interface UserService {    @WebMethod    public String getUserName(            /* 指定入參名稱為userId 預設為arg0*/            @WebParam(name = "userId")                    String userId    );    @WebMethod    public User getUser(            @WebParam(name = "userId")                    String userId    );    @WebMethod    public java.util.List<User> getUserList(            @WebParam(name = "userId")                    String userId    );}
3.4.3、新建UserServiceImpl服務類
package org.ouyushan.cxf.service;import org.ouyushan.cxf.entity.User;import org.springframework.stereotype.Service;import javax.jws.WebService;import java.util.ArrayList;import java.util.List;import java.util.Map;import java.util.function.Function;import java.util.stream.Collectors;/** * @Description: * @Author: ouyushan * @Email: ouyushan@hotmail.com * @Date: 2020/11/26 14:42 */@Service@WebService(serviceName = "UserService",        targetNamespace = "http://ws.cxf.ouyushan.org/",        endpointInterface = "org.ouyushan.cxf.service.UserService")public class UserServiceImpl implements UserService{        private static User user1;    private static User user2;    private static User user3;    private static List<User> userList = new ArrayList<>();    private static Map<String, User> userMap;    static {        user1 = new User("1", "tom", "tom@gmail.com");        user2 = new User("2", "jim", "jim@gmail.com");        user3 = new User("3", "jack", "jack@gmail.com");        userList.add(user1);        userList.add(user2);        userList.add(user3);        userMap = userList.stream().collect(Collectors.toMap(User::getUserId, Function.identity()));    }    @Override    public String getUserName(String userId) {        User user = userMap.getOrDefault(userId, new User(userId, "random", "random@gmail.com"));        return user.getUserName();    }    @Override    public User getUser(String userId) {        User user = userMap.getOrDefault(userId, new User(userId, "random", "random@gmail.com"));        return user;    }    @Override    public List<User> getUserList(String userId) {        return userList;    }}
3.4.4、服務端配置類
package org.ouyushan.cxf.config;import org.apache.cxf.Bus;import org.apache.cxf.jaxws.EndpointImpl;import org.apache.cxf.transport.servlet.CXFServlet;import org.ouyushan.cxf.service.UserService;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.boot.web.servlet.ServletRegistrationBean;import org.springframework.context.annotation.Bean;import org.springframework.context.annotation.Configuration;import javax.xml.ws.Endpoint;/** * @Description: 服務端配置類 * @Author: ouyushan * @Email: ouyushan@hotmail.com * @Date: 2020/11/26 14:38 */@Configurationpublic class CxfServerConfig {    @Autowired    private Bus bus;    @Autowired    private UserService userService;    @Bean    public ServletRegistrationBean cxfServlet() {        return new ServletRegistrationBean(new CXFServlet(), "/ws/*");    }    @Bean    public Endpoint userEndpoint() {        EndpointImpl endpoint =                new EndpointImpl(bus, userService);        endpoint.publish("/user");        return endpoint;    }}
3.4.5、服務端驗證

執行啟動類中的main方法啟動服務

SpringCxfServerBeginApplication

訪問:

http://localhost:8080/ws/

或者直接訪問:

http://localhost:8080/ws/user?wsdl

即可檢視:

This XML file does not appear to have any style information associated with it. The document tree is shown below.<wsdl:definitions xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/" xmlns:tns="http://ws.cxf.ouyushan.org/" xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/" xmlns:ns1="http://schemas.xmlsoap.org/soap/http" name="UserService" targetNamespace="http://ws.cxf.ouyushan.org/"><wsdl:types><xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:tns="http://ws.cxf.ouyushan.org/" elementFormDefault="unqualified" targetNamespace="http://ws.cxf.ouyushan.org/" version="1.0"><xs:element name="getUser" type="tns:getUser"/><xs:element name="getUserList" type="tns:getUserList"/><xs:element name="getUserListResponse" type="tns:getUserListResponse"/><xs:element name="getUserName" type="tns:getUserName"/><xs:element name="getUserNameResponse" type="tns:getUserNameResponse"/><xs:element name="getUserResponse" type="tns:getUserResponse"/><xs:complexType name="getUserList"><xs:sequence><xs:element minOccurs="0" name="userId" type="xs:string"/></xs:sequence></xs:complexType><xs:complexType name="getUserListResponse"><xs:sequence><xs:element maxOccurs="unbounded" minOccurs="0" name="return" type="tns:user"/></xs:sequence></xs:complexType><xs:complexType name="user"><xs:sequence><xs:element minOccurs="0" name="email" type="xs:string"/><xs:element minOccurs="0" name="userId" type="xs:string"/><xs:element minOccurs="0" name="userName" type="xs:string"/></xs:sequence></xs:complexType><xs:complexType name="getUserName"><xs:sequence><xs:element minOccurs="0" name="userId" type="xs:string"/></xs:sequence></xs:complexType><xs:complexType name="getUserNameResponse"><xs:sequence><xs:element minOccurs="0" name="return" type="xs:string"/></xs:sequence></xs:complexType><xs:complexType name="getUser"><xs:sequence><xs:element minOccurs="0" name="userId" type="xs:string"/></xs:sequence></xs:complexType><xs:complexType name="getUserResponse"><xs:sequence><xs:element minOccurs="0" name="return" type="tns:user"/></xs:sequence></xs:complexType></xs:schema></wsdl:types><wsdl:message name="getUserResponse"><wsdl:part element="tns:getUserResponse" name="parameters"> </wsdl:part></wsdl:message><wsdl:message name="getUser"><wsdl:part element="tns:getUser" name="parameters"> </wsdl:part></wsdl:message><wsdl:message name="getUserNameResponse"><wsdl:part element="tns:getUserNameResponse" name="parameters"> </wsdl:part></wsdl:message><wsdl:message name="getUserList"><wsdl:part element="tns:getUserList" name="parameters"> </wsdl:part></wsdl:message><wsdl:message name="getUserName"><wsdl:part element="tns:getUserName" name="parameters"> </wsdl:part></wsdl:message><wsdl:message name="getUserListResponse"><wsdl:part element="tns:getUserListResponse" name="parameters"> </wsdl:part></wsdl:message><wsdl:portType name="UserService"><wsdl:operation name="getUserList"><wsdl:input message="tns:getUserList" name="getUserList"> </wsdl:input><wsdl:output message="tns:getUserListResponse" name="getUserListResponse"> </wsdl:output></wsdl:operation><wsdl:operation name="getUserName"><wsdl:input message="tns:getUserName" name="getUserName"> </wsdl:input><wsdl:output message="tns:getUserNameResponse" name="getUserNameResponse"> </wsdl:output></wsdl:operation><wsdl:operation name="getUser"><wsdl:input message="tns:getUser" name="getUser"> </wsdl:input><wsdl:output message="tns:getUserResponse" name="getUserResponse"> </wsdl:output></wsdl:operation></wsdl:portType><wsdl:binding name="UserServiceSoapBinding" type="tns:UserService"><soap:binding style="document" transport="http://schemas.xmlsoap.org/soap/http"/><wsdl:operation name="getUserList"><soap:operation soapAction="" style="document"/><wsdl:input name="getUserList"><soap:body use="literal"/></wsdl:input><wsdl:output name="getUserListResponse"><soap:body use="literal"/></wsdl:output></wsdl:operation><wsdl:operation name="getUserName"><soap:operation soapAction="" style="document"/><wsdl:input name="getUserName"><soap:body use="literal"/></wsdl:input><wsdl:output name="getUserNameResponse"><soap:body use="literal"/></wsdl:output></wsdl:operation><wsdl:operation name="getUser"><soap:operation soapAction="" style="document"/><wsdl:input name="getUser"><soap:body use="literal"/></wsdl:input><wsdl:output name="getUserResponse"><soap:body use="literal"/></wsdl:output></wsdl:operation></wsdl:binding><wsdl:service name="UserService"><wsdl:port binding="tns:UserServiceSoapBinding" name="UserServiceImplPort"><soap:address location="http://localhost:8080/ws/user"/></wsdl:port></wsdl:service></wsdl:definitions>

至此服務端部署完成!

3.5、實現客戶端訪問服務

客戶端向外提供介面,可透過cxf訪問服務端提供的webservice服務。

3.5.1、生成wsdl檔案

訪問服務端:

http://localhost:8080/ws/user?wsdl

將返回結果製作成user.wsdl檔案,並儲存至resources/wsdl目錄下。

將服務端釋出的wsdl檔案內容複製到上述檔案中,並在第一行新增:

<?xml version="1.0" encoding="UTF-8" standalone="no"?>

完整user.wsdl

<?xml version="1.0" encoding="UTF-8" standalone="no"?><wsdl:definitions xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/" xmlns:tns="http://ws.cxf.ouyushan.org/" xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/" xmlns:ns1="http://schemas.xmlsoap.org/soap/http" name="UserService" targetNamespace="http://ws.cxf.ouyushan.org/"><wsdl:types><xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:tns="http://ws.cxf.ouyushan.org/" elementFormDefault="unqualified" targetNamespace="http://ws.cxf.ouyushan.org/" version="1.0"><xs:element name="getUser" type="tns:getUser"/><xs:element name="getUserList" type="tns:getUserList"/><xs:element name="getUserListResponse" type="tns:getUserListResponse"/><xs:element name="getUserName" type="tns:getUserName"/><xs:element name="getUserNameResponse" type="tns:getUserNameResponse"/><xs:element name="getUserResponse" type="tns:getUserResponse"/><xs:complexType name="getUserList"><xs:sequence><xs:element minOccurs="0" name="userId" type="xs:string"/></xs:sequence></xs:complexType><xs:complexType name="getUserListResponse"><xs:sequence><xs:element maxOccurs="unbounded" minOccurs="0" name="return" type="tns:user"/></xs:sequence></xs:complexType><xs:complexType name="user"><xs:sequence><xs:element minOccurs="0" name="email" type="xs:string"/><xs:element minOccurs="0" name="userId" type="xs:string"/><xs:element minOccurs="0" name="userName" type="xs:string"/></xs:sequence></xs:complexType><xs:complexType name="getUserName"><xs:sequence><xs:element minOccurs="0" name="userId" type="xs:string"/></xs:sequence></xs:complexType><xs:complexType name="getUserNameResponse"><xs:sequence><xs:element minOccurs="0" name="return" type="xs:string"/></xs:sequence></xs:complexType><xs:complexType name="getUser"><xs:sequence><xs:element minOccurs="0" name="userId" type="xs:string"/></xs:sequence></xs:complexType><xs:complexType name="getUserResponse"><xs:sequence><xs:element minOccurs="0" name="return" type="tns:user"/></xs:sequence></xs:complexType></xs:schema></wsdl:types><wsdl:message name="getUserResponse"><wsdl:part element="tns:getUserResponse" name="parameters"> </wsdl:part></wsdl:message><wsdl:message name="getUser"><wsdl:part element="tns:getUser" name="parameters"> </wsdl:part></wsdl:message><wsdl:message name="getUserNameResponse"><wsdl:part element="tns:getUserNameResponse" name="parameters"> </wsdl:part></wsdl:message><wsdl:message name="getUserList"><wsdl:part element="tns:getUserList" name="parameters"> </wsdl:part></wsdl:message><wsdl:message name="getUserName"><wsdl:part element="tns:getUserName" name="parameters"> </wsdl:part></wsdl:message><wsdl:message name="getUserListResponse"><wsdl:part element="tns:getUserListResponse" name="parameters"> </wsdl:part></wsdl:message><wsdl:portType name="UserService"><wsdl:operation name="getUserList"><wsdl:input message="tns:getUserList" name="getUserList"> </wsdl:input><wsdl:output message="tns:getUserListResponse" name="getUserListResponse"> </wsdl:output></wsdl:operation><wsdl:operation name="getUserName"><wsdl:input message="tns:getUserName" name="getUserName"> </wsdl:input><wsdl:output message="tns:getUserNameResponse" name="getUserNameResponse"> </wsdl:output></wsdl:operation><wsdl:operation name="getUser"><wsdl:input message="tns:getUser" name="getUser"> </wsdl:input><wsdl:output message="tns:getUserResponse" name="getUserResponse"> </wsdl:output></wsdl:operation></wsdl:portType><wsdl:binding name="UserServiceSoapBinding" type="tns:UserService"><soap:binding style="document" transport="http://schemas.xmlsoap.org/soap/http"/><wsdl:operation name="getUserList"><soap:operation soapAction="" style="document"/><wsdl:input name="getUserList"><soap:body use="literal"/></wsdl:input><wsdl:output name="getUserListResponse"><soap:body use="literal"/></wsdl:output></wsdl:operation><wsdl:operation name="getUserName"><soap:operation soapAction="" style="document"/><wsdl:input name="getUserName"><soap:body use="literal"/></wsdl:input><wsdl:output name="getUserNameResponse"><soap:body use="literal"/></wsdl:output></wsdl:operation><wsdl:operation name="getUser"><soap:operation soapAction="" style="document"/><wsdl:input name="getUser"><soap:body use="literal"/></wsdl:input><wsdl:output name="getUserResponse"><soap:body use="literal"/></wsdl:output></wsdl:operation></wsdl:binding><wsdl:service name="UserService"><wsdl:port binding="tns:UserServiceSoapBinding" name="UserServiceImplPort"><soap:address location="http://localhost:8080/ws/user"/></wsdl:port></wsdl:service></wsdl:definitions>
3.5.2、根據wsdl生成java程式碼

在客戶端pom檔案中新增外掛

<plugin>     <groupId>org.apache.cxf</groupId>     <artifactId>cxf-codegen-plugin</artifactId>     <version>3.4.1</version>     <executions>         <execution>             <id>generate-sources</id>             <phase>generate-sources</phase>             <configuration>                 <sourceRoot>${project.build.directory}/generated-sources/cxf</sourceRoot>                 <wsdlOptions>                     <wsdlOption>                         <wsdl>${basedir}/src/main/resources/wsdl/user.wsdl</wsdl>                     </wsdlOption>                 </wsdlOptions>             </configuration>             <goals>                 <goal>wsdl2java</goal>             </goals>         </execution>     </executions> </plugin>

執行外掛cxf-codegen ==>wsdl2java

執行結束會在target ==> generated-sources中生成對應java檔案,其中org.ouyushan.cxf.ws為指定名稱空間導致生成的包目錄,可將整個包對應複製到客戶端對應包下,當檔案的包路徑發生變化時,UserService中所有className的類路徑都需要調整。

同時對UserService_Service中對應的wsdlLocation都需要改成

wsdlLocation = "classpath:wsdl/user.wsdl",
3.5.3、客戶端配置類及配置檔案

實現客戶端配置類CxfClientConfig

package org.ouyushan.cxf.config;import org.apache.cxf.jaxws.JaxWsProxyFactoryBean;import org.ouyushan.cxf.ws.UserService;import org.springframework.beans.factory.annotation.Value;import org.springframework.context.annotation.Bean;/** * @Description: * @Author: ouyushan * @Email: ouyushan@hotmail.com * @Date: 2020/11/26 16:52 */@Configurationpublic class CxfClientConfig {    @Value("${server.serviceUrl}")    private String serviceUrl;    @Bean    public UserService userService() throws Exception {        JaxWsProxyFactoryBean jaxWsProxyFactoryBean =                new JaxWsProxyFactoryBean();        jaxWsProxyFactoryBean.setServiceClass(UserService.class);        jaxWsProxyFactoryBean.setAddress(serviceUrl);        UserService userService = (UserService) jaxWsProxyFactoryBean.create();        return userService;    }}

application.yml

server:  port: 7070  serviceUrl: http://localhost:8080/ws/user
3.5.4、客戶端服務類

客戶端編寫UserFacadeService

package org.ouyushan.cxf.service;import org.ouyushan.cxf.ws.User;import org.ouyushan.cxf.ws.UserService;import org.springframework.stereotype.Service;import javax.annotation.Resource;import java.util.List;/** * @Description: * @Author: ouyushan * @Email: ouyushan@hotmail.com * @Date: 2020/11/26 17:00 */@Servicepublic class UserFacadeService {    /**     * 資料接入服務     */    @Resource    private UserService userService;    public String getUsername(String userId) {        String userName = userService.getUserName(userId);        return userName;    }    public User getUser(String userId) {        User user = userService.getUser(userId);        return user;    }    public List<User> getUserList(String userId) {        List<User> userList = userService.getUserList(userId);        return userList;    }}
3.5.5、客戶端服務類

客戶端實現UserController

package org.ouyushan.cxf.controller;import org.ouyushan.cxf.service.UserFacadeService;import org.ouyushan.cxf.ws.User;import org.springframework.web.bind.annotation.GetMapping;import org.springframework.web.bind.annotation.RequestMapping;import org.springframework.web.bind.annotation.RestController;import javax.annotation.Resource;import java.util.List;/** * @Description: * @Author: ouyushan * @Email: ouyushan@hotmail.com * @Date: 2020/11/26 17:01 */@RestController@RequestMapping("/user")public class UserController {    @Resource    private UserFacadeService userFacadeService;    @GetMapping("/get")    public String getUsername(String userId){        System.out.println("client#userId:" + userId);        return userFacadeService.getUsername(userId);    }    @GetMapping("/getUser")    public User getUser(String userId){        System.out.println("client#userId:" + userId);        return userFacadeService.getUser(userId);    }    @GetMapping("/getUserList")    public List<User> getUserList(String userId){        System.out.println("client#userId:" + userId);        return userFacadeService.getUserList(userId);    }}
3.5.6、客戶端驗證

啟動客戶端

訪問:

http://localhost:6060/user/get?userId=1

返回:

tom
四、總結4.1、服務端定義cxfServlet

服務端需要定義一個cxfServlet來指定webservice的urlMappings,可以透過配置bean來實現,也可以直接在application中配置

cxf:  servlet: /ws/*

/ws/與服務端路徑中的ws對應,

http://localhost:8080/ws/user?wsdl
4.2、服務端定義endpoint

服務端可透過配置endpoint來發布服務,具體服務路徑可透過publish方法來指定,一個服務對應一個wsdl檔案,統一配置類中可釋出多個不同名的服務。

endpoint.publish("/user");

/user與服務端路徑中的user對應,

http://localhost:8080/ws/user?wsdl
4.3、服務端名稱空間

服務端userservice介面需要指定名稱空間,並顯示指定入參名稱:

名稱空間:

@WebService(targetNamespace = "http://ws.cxf.ouyushan.org/")

顯示指定入參名稱,指定入參名稱為userId 預設為arg0

@WebParam(name = "userId")
4.4、客戶端配置類需指定服務端url
http://localhost:8080/ws/user
4.5、wsdl生成java程式碼

客戶端生成的類檔案(UserService),若包路徑發生變更時,對應介面檔案中的className也需更新。

至此,spring boot整合cxf實現webservice的請求與服務的簡單例項已完成。但是在除錯過程中您會發現,日誌輸出中沒有關於webservice的相關資訊,怎麼實現能?下一章節將會實現這一問題。

19
最新評論
  • BSA-TRITC(10mg/ml) TRITC-BSA 牛血清白蛋白改性標記羅丹明
  • 2021年將主導Python的7大影象處理庫