背景
最近项目中有些地方相互调用的时候,并不是采用Http(post/get)方式来实现,而是通过webservice方式来实现,之前虽然也接触过webservice的方式,但一直没有机会研究一下,只是依葫芦画瓢去模仿写,也没有自己单独搭建一个webservice的服务出来。这几天正好有空,做一下笔记,有什么地方写的不好的,或者表达的不明确的,希望大家留言。
什么是webservice
WebService是一种跨编程语言、跨操作系统平台的远程调用技术。
这个问题我也是从百度上抄的,但是我还是希望大家能稍微看下,了解一下这个一些名词解释。
- 远程调用技术:远程调用是指一台设备上的程序A可以调用另一台设备上的方法B(我的理解,就是你能看到我的方法名、参数类型、参数个数,而不是一个url接口的定义-。-)
- XML:(Extensible Markup Language)扩展型可标记语言。面向短期的临时数据处理、面向万维网络,是Soap的基础。
- Soap:(Simple Object Access Protocol)简单对象存取协议。是XML Web Service 的通信协议。当用户通过UDDI找到你的WSDL描述文档后,他通过可以SOAP调用你建立的Web服务中的一个或多个操作。SOAP是XML文档形式的调用方法的规范,它可以支持不同的底层接口,像HTTP(S)或者SMTP。
- WSDL:(Web Services Description Language) WSDL 文件是一个 XML 文档,用于说明一组SOAP 消息以及如何交换这些消息。大多数情况下由软件自动生成和使用。
为什么要使用webservice,难道http还不够你用么?
首先明确一点:webservice也算是http的一种传输方式。为什么这么说呢?
首先webservice是基于soap协议,而soap协议是HTTP POST的一个专用版本,遵循一种特殊的xml消息格式Content-type设置为: text/xml任何数据都可以xml化。
HTTP是SOAP消息的最常见的传输工具
优缺点:
- 接口中实现的方法和要求参数一目了然
- 不用担心大小写问题(直接把类给你,你写错了看看,代码都跑不起来)
- 不用担心中文urlencode问题
- 代码中不用多次声明认证(账号,密码)参数
- 传递参数可以为数组,对象等…
springboot中如何集成webservice
服务端
1. 前期准备
开发工具: idea
springboot版本:2.2.4
2. pom.xml
maven引入2个依赖:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web-services</artifactId>
<version>2.0.6.RELEASE</version>
</dependency>
<dependency>
<groupId>org.apache.cxf</groupId>
<artifactId>cxf-spring-boot-starter-jaxws</artifactId>
<version>3.2.4</version>
</dependency>
解释下这两个jar包:
- spring-boot-starter-web-services:
这个没啥好说的吧,你springboot集成webservice,不加入这个包,难道加个什么quartz包? - cxf-spring-boot-starter-jaxws
这个包是给webservice发布使用的。
我们知道 Web Service是一种能够使应用程序在不同的平台使用不同的编程语言进行通讯的技术规范,这种技术规范的实现方式是通过基于XML形式的协议(SOAP)进行通讯或者说是RESTFUL形式的。
如果使用这两种方式进行通讯,那么必须要有规范化的工作,也就是jaxws规范和 jar-rs规范。
而cxf正好是这两种规范的具体实现方式。
换句话说:JAX-WS是标准,CXF与Axis则是具体的框架实现。
附上个图:
3. 接口类
新建一个接口WebServiceDemoService
package com.cj.webservice.service;
import javax.jws.WebMethod;
import javax.jws.WebParam;
import javax.jws.WebResult;
import javax.jws.WebService;
import javax.xml.bind.annotation.XmlSeeAlso;
import javax.xml.ws.RequestWrapper;
import javax.xml.ws.ResponseWrapper;
@WebService
public interface WebServiceDemoService {
@WebMethod
public String hello(@WebParam(name = "name") String name);
}
@WebService:标明是个webservice服务,发布的时候会带上这个类。
@WeBMethod:webservice中发布的方法
@WebParam:对参数的别名,不写也不影响,但是参数在wsdl中看起来是arg0,不利于理解。
4. 实现类
package com.cj.webservice.service;
import org.springframework.stereotype.Service;
import javax.jws.WebMethod;
import javax.jws.WebParam;
import javax.jws.WebService;
/**
* 描述:
*
* @author caojing
* @create 2020-02-14-14:56
*/
@WebService(serviceName = "fileDeviceService",//对外发布的服务名
targetNamespace = "http://service.webservice.cj.com",//指定你想要的名称空间,通常使用使用包名反转
endpointInterface = "com.cj.webservice.service.WebServiceDemoService")
@Service
public class WebServiceDemoServiceImpl implements WebServiceDemoService {
@Override
public String hello(String name) {
return "hello" + name;
}
}
该写的注释我已经写了兄弟们,你们看一看,瞧一瞧。
5. 配置类
package com.cj.webservice.config;
import com.cj.webservice.service.WebServiceDemoService;
import org.apache.cxf.Bus;
import org.apache.cxf.bus.spring.SpringBus;
import org.apache.cxf.jaxws.EndpointImpl;
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 org.apache.cxf.transport.servlet.CXFServlet;
import javax.xml.ws.Endpoint;
/**
* 描述:webservice 配置类
* * @author caojing
* @create 2020-02-14-14:47
*/
@Configuration
public class WebServiceConfig {
@Autowired
private WebServiceDemoService webServiceDemoService;
//* 此方法被注释后:wsdl访问地址为http://127.0.0.1:8080/services/user?wsdl
// * 去掉注释后:wsdl访问地址为:http://127.0.0.1:8080/webservice/user?wsdl
@Bean(name = "cxfServlet")
public ServletRegistrationBean cxfServlet() {
return new ServletRegistrationBean(new CXFServlet(), "/webservice/*");
}
@Bean(name = Bus.DEFAULT_BUS_ID)
public SpringBus springBus() {
return new SpringBus();
}
@Bean(name = "webServiceDemoEndPoint")
public Endpoint webServiceDemoEndPoint() {
EndpointImpl endpoint = new EndpointImpl(springBus(), webServiceDemoService);
endpoint.publish("/webservice");
return endpoint;
}
//如果有其余的服务,继续加
}
cxfServlet 和springBus 两个方法都不要改动,如果有新的服务进来,直接再添加一个endPoint的方法,加入到服务中去。
6.0启动项目
日志如上所示则表明启动成功。
访问地址:http://127.0.0.1:8080/webservice/webservice?wsdl
如下图所示:
至此服务段的代码都已经完成,我们创建一个webservice服务,这个服务器就是你传的参数,返回给你 “heelo” + 参数。
如何测试webservice
这里我说两种方式来测试下webservice服务:
- 自己用工具测,不需要关心客户端怎么去调用(方式自选)
推荐一个灰常好用的webservice测试工具
https://www.soapui.org/
打开工具:
点击ok,如图所示:
- 自己内部用
因为我们自己内部需要调用这个服务,所以,我们的方式就是打包jar包,然后放到maven仓库(自己私有的maven库)上,需要用的的项目直接可以maven拉下来就行了。
idea打包方式:
File -> Project Structure
给个name:com-cj-webservice-service
然后给需要打包的class文件,添加包
一直添加到com.cj.webservice.service这一层
点击 ” +“ 号,添加Directory Content内容,
这里需要注意的是,添加的是class的文件,也就是编译之后的文件。
最终如图所示:
apply 结束,然后
build -> buildArtifacts 生成jar包,jar包的位置,在创建的时候,已经指定好了,忘了的童鞋可以再回去看一下。
客户端使用:
第一步,把刚才的打的jar包作为lib导入到idea中,这个不会的,请出门右转,自行百度。
1. pom文件:
<dependency>
<groupId>org.apache.cxf</groupId>
<artifactId>cxf-rt-transports-http</artifactId>
<version>3.2.2</version>
</dependency>
<dependency>
<groupId>org.apache.cxf</groupId>
<artifactId>cxf-rt-frontend-jaxws</artifactId>
<version>3.3.0</version>
</dependency>
2. 写一个test类:
import com.cj.webservice.service.WebServiceDemoService;
import org.apache.cxf.jaxws.JaxWsProxyFactoryBean;
/**
* 描述:
*
* @author caojing
* @create 2020-02-14-15:37
*/
public class test {
static WebServiceDemoService webServiceDemoService;
public static void main(String[] args) {
JaxWsProxyFactoryBean factory = new JaxWsProxyFactoryBean();
factory.setServiceClass(WebServiceDemoService.class);
factory.setAddress("http://localhost:8080/webservice/webservice");
webServiceDemoService = (WebServiceDemoService) factory.create();
System.out.println(webServiceDemoService.hello("cj"));
}
}
总结
啰嗦了这么久,终于结束了。。。。。。
我总结一下springboot中集成webservice的几个点。
- 先把用的jar依赖加进来。
- 编写自己的对外服务类,加上@webservce@webMethod注解。注意:@Webservice中的几个属性要注意下。serviceName、targetNamespace、endpointInterface这三个都是你的服务对外的一些属性。
- 编写config类,也就是发布,其中springBus、cxfServlet无需改动,唯一需要改动的就是Endpoint,如果有新的webservice服务,直接在新增一个返回值是Endpoint的方法,方法内容依葫芦画瓢就行嘞。