SpringBoot2.x系列教程--SpringBoot整合Web Service实现远程接口调用
作者:一一哥
上一章节中,我带大家学习了Web Service的理论知识,接下来在本节中,我带大家实现SpringBoot中整合Web Service,实现远程接口调用。
本项目中,我们需要进行远程接口的调用,也就是需要有一个A项目来调用B项目里的接口,然后A,B两个项目都需要依赖一些共同的内容,所以我们首先创建这个被共同依赖的通用模块C。
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@AllArgsConstructorpublic class User implements Serializable { private Long id; private String username; private String address;}
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 getUsers();}
完整项目结构如下图所示:
com.yyg.boot demo46_commons 1.0-SNAPSHOTorg.springframework.boot spring-boot-starter-web-servicesorg.apache.cxf cxf-rt-frontend-jaxws 3.1.12org.apache.cxf cxf-rt-transports-http 3.1.12
注意:网上有网友用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依赖包。
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 */@Configurationpublic 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/*");// }
在实现类中也有添加@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 getUsers() { List users = new ArrayList<>(); users.add(new User(1L, "一一哥", "北京")); users.add(new User(2L, "一一哥", "上海")); return users; }}
server: port: 8080spring: application: name: web-service-server
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 */@SpringBootApplicationpublic class WebServiceApplication { public static void main(String[] args) { SpringApplication.run(WebServiceApplication.class, args); }}
会发现在控制台中打印出如下启动信息。
然后我们在浏览器中输入如下地址:http://localhost:8080/myService/api?wsdl
会看到如下xml信息:
在这份xml文件中,指明了我们在service中定义的接口等信息。
com.yyg.boot demo46_commons 1.0-SNAPSHOTorg.apache.cxf cxf-rt-frontend-jaxws 3.1.12org.apache.cxf cxf-rt-transports-http 3.1.12
定义端口为8081。
server: port: 8081spring: application: name: web-service-client
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 */@RestControllerpublic 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 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; } }}
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 */@SpringBootApplicationpublic class CxfClientApplication { public static void main(String[] args){ SpringApplication.run(CxfClientApplication.class,args); }}
发现展示如下信息,说明我们的客户端已经成功的调用了服务端的接口,实现了RPC跨进程通信。
测试users接口:
发现展示如下信息,说明我们的客户端已经成功的调用了服务端的接口,也实现了RPC跨进程通信。
定义 web service 使用的数据类型 每个消息均由一个或多个部件组成。可以把它当做java中一个函数调用的参数。 它类似Java中的一个函数库(或一个模块、或一个类) 为每个端口定义消息格式和协议细节。
我们java中定义的服务接口中某个方法的输入参数和返回值。
通信消息的数据结构的抽象类型化定义,使用Types所定义的类型来定义整个消息的数据结构。
WebService中每个方法包含两部分:
特定端口类型的具体协议和数据格式规范的绑定。
负责将网络通信地址赋给一个具体的绑定。