敞开成长之旅!这是我参加「日新计划 12 月更文应战」的第23天,点击查看活动概况

前语

在上一章节中,壹哥带咱们学习了Web Service的理论知识。那么在接下来的章节中,壹哥会带咱们在SpringBoot中整合Web Service,完成长途接口的调用。

一. 创立通用模块

在本项目中,咱们需求创立长途调用的接口,也便是需求有一个能够在A项目里来调用B项目的接口。但A、B两个项目需求依靠一些共同的内容,所以咱们应该首先创立这个被共同依靠的通用模块C。

SpringBoot2.x系列教程49--SpringBoot整合Web Service实现远程接口调用

1. 创立实体类User

咱们先创立一个封装用户信息的实体类User。

package com.yyg.boot.domain;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.io.Serializable;
/**
 * @Author 逐个哥Sun
 * @Date Created in 2020/5/9
 * @Description Description
 */
@Data
@NoArgsConstructor
@AllArgsConstructor
public class User implements Serializable {
    private Long id;
    private String username;
    private String address;
}

2. 界说通用的MyService接口

这儿壹哥先给咱们解说这个MyService接口的作用及工作原理。

SpringBoot2.x系列教程49--SpringBoot整合Web Service实现远程接口调用

  • 接口类上增加@WebService注解,代表发布一个WebService服务;
  • 接口类增加上@WebService注解后,类中所有的非静态办法都将会对外发布;
  • 假如期望某个办法不对外揭露,能够在办法上增加@WebMethod(exclude=true),阻止对外揭露;
  • 假如一个类上被增加了@WebService注解,则此类必须至少有一个能够揭露的办法,否则将会发动失利。
  • protected、private、final、static办法不能对外揭露。
package com.yyg.boot.service;
import com.yyg.boot.domain.User;
import javax.jws.WebService;
import java.util.List;
/**
 * @Author 逐个哥Sun
 * @Date Created in 2020/5/9
 * @Description Description
 */
@WebService(name = "MyService", // 露出服务称号
        targetNamespace = "http://service.boot.yyg.com"// 命名空间,一般是接口的包名倒序
)
public interface MyService {
    String sayHello(String msg);
    List<User> getUsers();
}

二. 创立Web Service中的Server端项目

接下来咱们先创立服务端项目,该项目能够被其他的客户端项目进行长途调用。

1. 增加依靠包

咱们在pom.xml文件中增加中心依靠包,如下所示:

<dependency>
    <groupId>com.yyg.boot</groupId>
    <artifactId>demo46_commons</artifactId>
    <version>1.0-SNAPSHOT</version>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web-services</artifactId>
</dependency>
<dependency>
    <groupId>org.apache.cxf</groupId>
    <artifactId>cxf-rt-frontend-jaxws</artifactId>
    <version>3.1.12</version>
</dependency>
<dependency>
    <groupId>org.apache.cxf</groupId>
    <artifactId>cxf-rt-transports-http</artifactId>
    <version>3.1.12</version>
</dependency>

留意:
网上有网友用cxf-spring-boot-starter-jaxws这个依靠包,可是我在Spring Boot2.2.5的环境中,运用该依靠会导致如下异常:

java.lang.ClassNotFoundException: org.springframework.boot.context.embedded.EmbeddedServletContainerCustomizer

所以我这儿不适用该依靠,而是运用cxf-rt-frontend-jaxws与cxf-rt-transports-http依靠包。

2. 界说CXFServlet的装备类

然后咱们还需求创立一个装备类,该类的功用作用如下。

  • 经过EndPoint(端点服务)发布一个webService。Endpoint也是jdk供给的一个专门用于发布服务的类,它的publish办法接收两个参数,一个是本地的服务地址,二是供给服务的类。它坐落javax.xml.ws.*包中。
  • Endpoint.publish(String address, Object implementor) 静态办法在给定地址处针对指定的完成者对象创立并发布端点。
package com.yyg.boot.config;
import com.yyg.boot.service.MyService;
import com.yyg.boot.service.impl.MyServiceImpl;
import org.apache.cxf.Bus;
import org.apache.cxf.bus.spring.SpringBus;
import org.apache.cxf.jaxws.EndpointImpl;
import org.apache.cxf.transport.servlet.CXFServlet;
import org.springframework.boot.web.servlet.ServletRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import javax.xml.ws.Endpoint;
/**
 * @Author 逐个哥Sun
 * @Date Created in 2020/5/9
 * @Description Description
 */
@Configuration
public class CxfConfig {
    @Bean
    public ServletRegistrationBean createServletRegistrationBean() {
        return new ServletRegistrationBean(new CXFServlet(), "/myService/*");
    }
    @Bean(name = Bus.DEFAULT_BUS_ID)
    public SpringBus springBus() {
        return new SpringBus();
    }
    @Bean
    public MyService myService() {
        return new MyServiceImpl();
    }
    @Bean
    public Endpoint endpoint() {
        EndpointImpl endpoint = new EndpointImpl(springBus(), myService());
        endpoint.publish("/api");
        return endpoint;
    }
}

留意:
在该装备类中,网上有不少文章,在注册CXFServlet的时分,界说的办法称号都是dispatcherServlet,留意不要这么界说!!!否则会导致ErrorMvcAutoConfiguration错误。

//留意:该办法的称号不能运用dispatcherServlet(),否则会导致ErrorMvcAutoConfiguration错误.
    //org.springframework.boot.autoconfigure.web.servlet.error.ErrorMvcAutoConfiguration
    //required a bean of type 'org.springframework.boot.autoconfigure.web.servlet.DispatcherServletPath'
    // that could not be found.
//    @Bean
//    public ServletRegistrationBean dispatcherServlet() {
//        return new ServletRegistrationBean(new CXFServlet(), "/myService/*");
//    }

3. 界说MyServiceImpl接口完成类

在完成类中也要增加@WebService注解,指定serviceName,targetNamespace,endpointInterface特点。

package com.yyg.boot.service.impl;
import com.yyg.boot.domain.User;
import com.yyg.boot.service.MyService;
import javax.jws.WebService;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
/**
 * @Author 逐个哥Sun
 * @Date Created in 2020/5/9
 * @Description Description
 */
@WebService(serviceName = "MyService", // 与接口中指定的服务name共同
        targetNamespace = "http://service.boot.yyg.com", // 与接口中的命名空间共同,一般是接口的包名倒
        endpointInterface = "com.yyg.boot.service.MyService"// 接口地址
)
public class MyServiceImpl implements MyService {
    @Override
    public String sayHello(String msg) {
        return "收到的信息是:--->" + msg + ",现在时间是:--->" + new Date();
    }
    @Override
    public List<User> getUsers() {
        List<User> users = new ArrayList<>();
        users.add(new User(1L, "逐个哥", "北京"));
        users.add(new User(2L, "逐个哥", "上海"));
        return users;
    }
}

4. 创立application.yml装备文件

咱们还有创立一个application.yml装备文件,对服务端进行必要的装备。

server:
  port: 8080
spring:
  application:
    name: web-service-server

5. 创立进口类

最终创立一个项目进口类,用于发动服务端。

package com.yyg.boot;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
/**
 * @Author 逐个哥Sun
 * @Date Created in 2020/5/9
 * @Description Description
 */
@SpringBootApplication
public class WebServiceApplication {
    public static void main(String[] args) {
        SpringApplication.run(WebServiceApplication.class, args);
    }
}

6. 发动Server端项目进行测验

咱们能够把项目发动起来,然后能够在控制台中打印出如下发动信息。

SpringBoot2.x系列教程49--SpringBoot整合Web Service实现远程接口调用

接着咱们在浏览器中输入如下地址:
http://localhost:8080/myService/api?wsdl

就会看到如下的xml信息:

SpringBoot2.x系列教程49--SpringBoot整合Web Service实现远程接口调用

在这份xml文件中,指明晰咱们在service中界说的接口等信息。

7. 完好项目结构

完好的服务端项目结构如下图所示,各位能够参阅创立。

SpringBoot2.x系列教程49--SpringBoot整合Web Service实现远程接口调用

三. 创立Web Service的client项目

接着咱们再创立一个客户端项目,去调用服务端里的接口。

1. 增加依靠包

客户端项目也需求增加必要的中心依靠包。

<dependency>
    <groupId>com.yyg.boot</groupId>
    <artifactId>demo46_commons</artifactId>
    <version>1.0-SNAPSHOT</version>
</dependency>
<dependency>
    <groupId>org.apache.cxf</groupId>
    <artifactId>cxf-rt-frontend-jaxws</artifactId>
    <version>3.1.12</version>
</dependency>
<dependency>
    <groupId>org.apache.cxf</groupId>
    <artifactId>cxf-rt-transports-http</artifactId>
    <version>3.1.12</version>
</dependency>

2. 创立application.yml装备文件

这儿咱们把客户端的端口号界说为8081。

server:
  port: 8081
spring:
  application:
    name: web-service-client

3. 界说一个Controller进行测验

咱们创立一个Controller,编写接口进行测验。

package com.yyg.boot.web;
import com.yyg.boot.domain.User;
import com.yyg.boot.service.MyService;
import org.apache.cxf.jaxws.JaxWsProxyFactoryBean;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;
/**
 * @Author 逐个哥Sun
 * @Date Created in 2020/5/9
 * @Description Description
 */
@RestController
public class ClientController {
    /**
     * webservice接口地址
     */
    private static String address = "http://localhost:8080/myService/api?wsdl";
    @GetMapping("/show")
    public String showMsg() {
        try {
            // 署理工厂
            JaxWsProxyFactoryBean jaxWsProxyFactoryBean = new JaxWsProxyFactoryBean();
            // 设置署理地址
            jaxWsProxyFactoryBean.setAddress(address);
            //增加用户名密码拦截器
            //jaxWsProxyFactoryBean.getOutInterceptors().add(new LoginInterceptor("root","admin"));;
            // 设置接口类型
            jaxWsProxyFactoryBean.setServiceClass(MyService.class);
            // 创立一个署理接口完成
            MyService service = (MyService) jaxWsProxyFactoryBean.create();
            // 调用署理接口的办法调用并回来成果
            return service.sayHello("hello");
        } catch (Exception e) {
            e.printStackTrace();
            return "error";
        }
    }
    @GetMapping("/users")
    public List<User> showUsers() {
        try {
            // 署理工厂
            JaxWsProxyFactoryBean jaxWsProxyFactoryBean = new JaxWsProxyFactoryBean();
            // 设置署理地址
            jaxWsProxyFactoryBean.setAddress(address);
            // 设置接口类型
            jaxWsProxyFactoryBean.setServiceClass(MyService.class);
            // 创立一个署理接口完成
            MyService service = (MyService) jaxWsProxyFactoryBean.create();
            // 调用署理接口的办法调用并回来成果
            return service.getUsers();
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }
}

4. 创立应用程序进口类

最终也要创立一个项目进口类,用于发动项目。

package com.yyg.boot;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
/**
 * @Author 逐个哥Sun
 * @Date Created in 2020/5/9
 * @Description Description
 */
@SpringBootApplication
public class CxfClientApplication  {
    public static void main(String[] args){
        SpringApplication.run(CxfClientApplication.class,args);
    }
}

5. 发动项目进行测验

5.1 测验show接口

项目发动后,咱们拜访show接口,浏览器中能够展示如下信息。这就说明咱们的客户端现已成功的调用了服务端的接口,完成了RPC跨进程通讯。

SpringBoot2.x系列教程49--SpringBoot整合Web Service实现远程接口调用

5.2 测验users接口

然后咱们再拜访users接口,浏览器中能够展示如下信息,这说明咱们的客户端也现已成功的调用了服务端的接口,也完成了RPC跨进程通讯。

SpringBoot2.x系列教程49--SpringBoot整合Web Service实现远程接口调用

四. WSDL文件解析

现在咱们的功用现已完成了,但Web Service的完成原理是什么?上面的那些XML文件都是什么意义?接下来 壹哥 带各位看看这些xml中的节点意义。

1. WSDL报文概述

咱们先看看wsdl报文是什么意义。

SpringBoot2.x系列教程49--SpringBoot整合Web Service实现远程接口调用

<definitions>
	<types>
	  	界说 web service 运用的数据类型
	</types>
	<message>
		每个音讯均由一个或多个部件组成。能够把它当做java中一个函数调用的参数。
	</message>
	<portType>
		它类似Java中的一个函数库(或一个模块、或一个类)
	</portType>
	<binding>
		为每个端口界说音讯格式和协议细节。
    </binding>
</definitions>

2. WSDL报文之wsdl:definitions

<wsdl:definitions xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/" xmlns:tns="http://service.boot.yyg.com" xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/" xmlns:ns1="http://schemas.xmlsoap.org/soap/http" name="MyService" targetNamespace="http://service.boot.yyg.com">
</wsdl:definitions>

SpringBoot2.x系列教程49--SpringBoot整合Web Service实现远程接口调用

3. WSDL报文之wsdl:types

咱们java中界说的服务接口中某个办法的输入参数和回来值。

SpringBoot2.x系列教程49--SpringBoot整合Web Service实现远程接口调用

4. WSDL报文之wsdl:message

通讯音讯的数据结构的抽象类型化界说,运用Types所界说的类型来界说整个音讯的数据结构。

SpringBoot2.x系列教程49--SpringBoot整合Web Service实现远程接口调用

WebService中每个办法包括两部分:

  • 一个是办法的输入参数,另一个是办法的输出参数。
  • 其实质都是根据SOAP协议将其封装为音讯,所以每一个办法对应有两个音讯,一个输入一个输出回应。简略而言,便是办法和Message的联系是N:2N的联系,一对二。
  • Message中的详细内容是part,结合前面可知,message中的part内容请到前面界说过的types中去看,它会引证之前的type相关内容。

5. WSDL报文之wsdl:portType

  • portType = 接口
  • operation = 接口中界说的办法

SpringBoot2.x系列教程49--SpringBoot整合Web Service实现远程接口调用

6. WSDL报文之wsdl:binding

特定端口类型的详细协议和数据格式标准的绑定。

SpringBoot2.x系列教程49--SpringBoot整合Web Service实现远程接口调用

7. WSDL报文之wsdl:service

担任将网络通讯地址赋给一个详细的绑定。

SpringBoot2.x系列教程49--SpringBoot整合Web Service实现远程接口调用

结语

至此,壹哥 就带各位使用Web Service完成了两个不同项目之间的长途调用,现在你学会了吗?有什么问题,能够在评论区给壹哥留言哦。