一文搞懂WebService基于CXF框架【jax-ws】

什么是服务中间件WebService

中间件是一种独立的系统软件或服务程序,分布式应用软件借助这种软件在不同的技术之间共享资源。中间件位于客户机/ 服务器的操作系统之上,管理计算机资源和网络通讯。是连接两个独立应用程序或独立系统的软件。

WebService作用是什么

  • 基于Web的服务:服务器端提供一些资源让客户端应用访问
  • 一个跨语言、跨平台的规范
  • 多个跨平台、跨语言的应用间通信整合的方案

WebService能解决什么业务场景

  • 跨平台调用(不同os系统)
  • 跨语言调用(不同开发语言)
  • 远程调用(不同服务)

关于WebService的重要知识点

  • WSDL(web service definition language)

WSDL是webservice定义语言, 对应.wsdl文档, 一个webservice会对应一个唯一的wsdl文档, 定义了客户端与服务端发送请求和响应的数据格式和过程

  • SOAP(simple object access protocal)

soap是”简单对象访问协议”是一种简单的、基于XML格式的协议, 用于在WEB上交换结构化的数据。SOAP 提供了一种标准的方法,使得运行在不同的操作系统并使用不同的技术和编程语言的应用程序可以互相进行通信。
soap消息:请求消息和响应消息

  • SEI(WebService EndPoint Interface)

SEI是web service的终端接口(javax自带),就是WebService服务器端用来处理请求的接口

  • CXF(Celtix + XFire)

apache的用于开发webservice服务器端和客户端的框架。

什么是JAX-WS

JAX-WS(Java API for XML Web Services)规范是一组XML web services的JAVA API。在 JAX-WS中,一个远程调用可以转换为一个基于XML的协议例如SOAP,在使用JAX-WS过程中,开发者不需要编写任何生成和处理SOAP消息的代码。JAX-WS的运行时实现会将这些API的调用转换成为对应的SOAP消息。

API实现JAX-WS代码

服务提供方

web-service服务方(提供服务)主要是写webservice的接口定义及接口实现类的定义。

  • pom.xml依赖配置
    这里cxf依赖使用3.1.4的版本
<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>cxf-ws</groupId>
  <artifactId>spring-webservice</artifactId>
  <version>0.0.1-SNAPSHOT</version>
  <packaging>war</packaging>
  
  <dependencies>
        <!-- https://mvnrepository.com/artifact/org.apache.cxf/cxf-rt-frontend-jaxws -->
        <dependency>
            <groupId>org.apache.cxf</groupId>
            <artifactId>cxf-rt-transports-http</artifactId>
            <version>3.1.4</version>
        </dependency>
        <dependency>
            <groupId>org.apache.cxf</groupId>
            <artifactId>cxf-rt-frontend-jaxws</artifactId>
            <version>3.1.4</version>
        </dependency>
        <dependency>
            <groupId>org.apache.cxf</groupId>
            <artifactId>cxf-rt-transports-http-jetty</artifactId>
            <version>3.1.4</version>
        </dependency>


        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>4.2.4.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-web</artifactId>
            <version>4.2.4.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-test</artifactId>
            <version>4.2.4.RELEASE</version>
        </dependency>
        <dependency>
        	<groupId>org.slf4j</groupId>
        	<artifactId>slf4j-log4j12</artifactId>
        	<version>1.7.12</version>
        </dependency>
    </dependencies>
   
    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.2</version>
                <configuration>
                    <source>1.8</source>
                    <target>1.8</target>
                    <encoding>UTF-8</encoding>
                    <showWarnings>true</showWarnings>
                </configuration>
            </plugin>
            <!-- 运行tomcat7方法:tomcat7:run -->
<!--             <plugin> -->
<!--                 <groupId>org.apache.tomcat.maven</groupId> -->
<!--                 <artifactId>tomcat7-maven-plugin</artifactId> -->
<!--                 <version>2.2</version> -->
<!--                 <configuration> -->
<!--                     指定端口 -->
<!--                     <port>8080</port> -->
<!--                     请求路径 -->
<!--                     <path>/</path> -->
<!--                 </configuration> -->
<!--             </plugin> -->
        </plugins>
    </build>

</project>

如果要用API进行接口测试,必须引入cxf-rt-transports-http-jetty依赖,使用内置web服务器,提供Servlet服务

  • web.xml配置
<!--cxfsevlet配置-->
  <servlet>
    <servlet-name>cxfservlet</servlet-name>
    <servlet-class>org.apache.cxf.transport.servlet.CXFServlet</servlet-class>
  </servlet>
  <servlet-mapping>
    <servlet-name>cxfservlet</servlet-name>
    <url-pattern>/ws/*</url-pattern>
  </servlet-mapping>

拦截/ws为路径的URL请求

  • 服务接口
package com.cxf.ws.service;

import javax.jws.WebMethod;
import javax.jws.WebService;
import javax.xml.ws.BindingType;

@WebService
@BindingType(value = javax.xml.ws.soap.SOAPBinding.SOAP12HTTP_BINDING)
public interface UserService {

	@WebMethod
	public String sayHello();

}

@WebService:在接口上申明,表示该接口是一个webService服务;
@BindingType:指定发布服务采用SOAP11或SOAP12协议(非必须)
@WebMethod:指定WebService接口方法

  • 服务接口实现类
package com.cxf.ws.service.impl;

import javax.jws.WebService;

import com.cxf.ws.service.UserService;

@WebService(endpointInterface = "com.cxf.ws.service.UserService", serviceName = "/userService")
public class UserServiceImpl implements UserService {

	@Override
	public String sayHello() {

		return "=============hello jax-ws!!!=================";
	}

}

endpointInterface : 配置所实现的service接口的完整路径
serviceName:配置服务的服务名(如果和xml配置不相同,以xml配置为主)

开启服务端服务测试

package com.cxf.ws.test;

import org.apache.cxf.interceptor.LoggingInInterceptor;
import org.apache.cxf.interceptor.LoggingOutInterceptor;
import org.apache.cxf.jaxws.JaxWsServerFactoryBean;

import com.cxf.ws.service.UserService;
import com.cxf.ws.service.impl.UserServiceImpl;

public class serverTest {

	public static void jaxWsServer() {
		// CXF框架提供的工厂对象(Server服务发布)
		JaxWsServerFactoryBean server = new JaxWsServerFactoryBean();
		// 设置服务发布接口类
		server.setServiceClass(UserService.class);
		// 设置服务发布地址(项目名/CXFServlet拦截地址/服务接口serviceName地址)
		server.setAddress("http://localhost:8086/spring-webservice/ws/userService");
		// 设置服务接口实现类
		server.setServiceBean(new UserServiceImpl());
		// 服务消息拦截日志
		// 拦截客户端往服务端 发送的请求的消息
		server.getInInterceptors().add(new LoggingInInterceptor());
		// 拦截服务端往客户端返回的消息...
		server.getOutInterceptors().add(new LoggingOutInterceptor());
		// 发布服务
		server.create();
		System.out.println("jaxWsServer服务启动成功!!!");
	}

	public static void endpointTest() {
		//服务接口类对象
		UserService us = new UserServiceImpl();
		//自定义服务接口地址
		String address = "http://localhost:8086/spring-webservice/aaa";
		//使用javax内置的方法发布服务
		javax.xml.ws.Endpoint.publish(address, us);
		System.out.println("Endpoint服务启动成功!!!");
	}

	public static void main(String[] args) {
		serverTest.endpointTest();
		// serverTest.jaxWsServer();

	}

}

JaxWsServerFactoryBean 和 Endpoint两种发布方式区别:

  • JaxWsServerFactoryBean 是CXF框架提供的,Endpoing是java自带的
  • JaxWsServerFactoryBean 提供更多的功能扩展
  • 访问wsdl地址设置不同Endpoint可以直接设(自定义),JaxWsServerFactoryBean要根据CXFServlet拦截和接口定义的serviceName决定
  • Endpoint多方法发布不容易扩展,JaxWsServerFactoryBean很容易扩展
  • Endpoint默认支持SOAP11,JaxWsServerFactoryBean默认支持SOAP12

浏览器访问配置的地址:http://localhost:8086/spring-webservice/ws/userService?wsdl (有xml页面显示则开启成功)

<?xml version='1.0' encoding='UTF-8'?>
<wsdl:definitions xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/" xmlns:tns="http://service.ws.cxf.com/" xmlns:soap12="http://schemas.xmlsoap.org/wsdl/soap12/" xmlns:ns1="http://schemas.xmlsoap.org/soap/http" name="UserServiceService" targetNamespace="http://service.ws.cxf.com/">
    <wsdl:types>
        <xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:tns="http://service.ws.cxf.com/" elementFormDefault="unqualified" targetNamespace="http://service.ws.cxf.com/" version="1.0">
            <xs:element name="sayHello" type="tns:sayHello"/>
            <xs:element name="sayHelloResponse" type="tns:sayHelloResponse"/>
            <xs:complexType name="sayHello">
                <xs:sequence/>
            </xs:complexType>
            <xs:complexType name="sayHelloResponse">
                <xs:sequence>
                    <xs:element minOccurs="0" name="return" type="xs:string"/>
                </xs:sequence>
            </xs:complexType>
        </xs:schema>
    </wsdl:types>
    <wsdl:message name="sayHello">
        <wsdl:part element="tns:sayHello" name="parameters">
    </wsdl:part>
    </wsdl:message>
    <wsdl:message name="sayHelloResponse">
        <wsdl:part element="tns:sayHelloResponse" name="parameters">
    </wsdl:part>
    </wsdl:message>
    <wsdl:portType name="UserService">
        <wsdl:operation name="sayHello">
            <wsdl:input message="tns:sayHello" name="sayHello">
    </wsdl:input>
            <wsdl:output message="tns:sayHelloResponse" name="sayHelloResponse">
    </wsdl:output>
        </wsdl:operation>
    </wsdl:portType>
    <wsdl:binding name="UserServiceServiceSoapBinding" type="tns:UserService">
        <soap12:binding style="document" transport="http://schemas.xmlsoap.org/soap/http"/>
        <wsdl:operation name="sayHello">
            <soap12:operation soapAction="" style="document"/>
            <wsdl:input name="sayHello">
                <soap12:body use="literal"/>
            </wsdl:input>
            <wsdl:output name="sayHelloResponse">
                <soap12:body use="literal"/>
            </wsdl:output>
        </wsdl:operation>
    </wsdl:binding>
    <wsdl:service name="UserServiceService">
        <wsdl:port binding="tns:UserServiceServiceSoapBinding" name="UserServicePort">
            <soap12:address location="http://localhost:8086/spring-webservice/ws/userService"/>
        </wsdl:port>
    </wsdl:service>
</wsdl:definitions>

开启客户端调用服务测试

package com.cxf.ws.test;

import org.apache.cxf.interceptor.LoggingInInterceptor;
import org.apache.cxf.interceptor.LoggingOutInterceptor;
import org.apache.cxf.jaxws.JaxWsProxyFactoryBean;

import com.cxf.ws.service.UserService;

public class clientTest {

	public static void getWS() {
		// CXF框架提供的工厂对象
		JaxWsProxyFactoryBean client = new JaxWsProxyFactoryBean();
		// 设置接口对象
		client.setServiceClass(UserService.class);
		// 设置远程调用路径(这里用的是Enpoint方式发布的路径)
		client.setAddress("http://localhost:8086/spring-webservice/aaa");
		// 日志设置
		// 输入日志
		client.getInInterceptors().add(new LoggingInInterceptor());
		// 输出日志
		client.getOutInterceptors().add(new LoggingOutInterceptor());
		// 创建远程接口代理对象
		UserService us = (UserService) client.create();
		// 代理对象调用远程方法
		System.out.println(us.sayHello());
		System.out.println("===========接口调用成功!!!==============");
	}

	public static void main(String[] args) {

		clientTest.getWS();

	}

}

通过框架提供的JaxWsproxyFactoryBean(代理工厂bean对象).设置服务端的接口对象和服务端的地址,通过create()方法,创建服务端接口的代理对象,通过代理对象调用方法。

WebService和PRC的关系

结论是:WebService也是RPC远程调用的一种实现方式
先来看看PRC调用模型和WebService调用模型区别
在这里插入图片描述
两者的模型有非常多的相似之处,实际运用过程中也是一样,都是通过网络传输,可以实现跨终端,跨平台,基于请求-响应,注重调用过程,不关心具体细节(框架封装好了)。
WebService是基于soap传输协议(基于Xml)和HTTP协议建立连接(通信协议)而RPC框架选择性更大一些,通信协议可以支持HTTP协议,TCP协议,自定义协议等,传输协议可以基于Xml和Json。

Spring实现JAX-WS服务[复制即用]

上述API调用中配置的pom.xml依赖和web.xml配置是搭建需要的

配置server服务方(提供方):

注意名称空间引入,否则无法识别:
xmlns:jaxws=http://cxf.apache.org/jaxws
xsi:schemaLocation=“http://cxf.apache.org/jaxws http://cxf.apache.org/schemas/jaxws.xsd”

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:cxf="http://cxf.apache.org/core"
	xmlns:jaxws="http://cxf.apache.org/jaxws"
	xsi:schemaLocation="
        http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd
        http://cxf.apache.org/core
        http://cxf.apache.org/schemas/core.xsd
        http://cxf.apache.org/jaxws
        http://cxf.apache.org/schemas/jaxws.xsd">

	<!-- Spring整合cxf发布服务,关键点:
	 address:客户端访问服务路径 
	 serviceClass:服务接口 
	 serviceBean:服务接口实现类 
		服务完整访问地址: http://localhost:8086/spring-webservice/ws/userService -->
	<jaxws:server id="userws" address="/userService"
		serviceClass="com.cxf.ws.service.UserService">
		<jaxws:serviceBean>
			<bean class="com.cxf.ws.service.impl.UserServiceImpl"></bean>
		</jaxws:serviceBean>
		<jaxws:inInterceptors>
			<bean class="org.apache.cxf.interceptor.LoggingInInterceptor" />
		</jaxws:inInterceptors>
		<jaxws:outInterceptors>
			<bean class="org.apache.cxf.interceptor.LoggingOutInterceptor" />
		</jaxws:outInterceptors>
	</jaxws:server>
</beans>

serviceClass:服务提供方service接口对象全限定名
serviceBean:具体提供服务的service接口实现类

服务方访问路径:http://localhost:端口号/项目名/ws/userService?wsdl
在被Spring整合后,服务端的启动,是根据配置的tomcat一起启动,web.xml配置名称为CXFservice的servlet会拦截所有/ws/*的方法。

配置Client客户端(调用方):

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:cxf="http://cxf.apache.org/core"
	xmlns:jaxws="http://cxf.apache.org/jaxws"
	xsi:schemaLocation="
        http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd
        http://cxf.apache.org/core
        http://cxf.apache.org/schemas/core.xsd
        http://cxf.apache.org/jaxws
        http://cxf.apache.org/schemas/jaxws.xsd">
        
        <!-- Spring整合cxf发布服务,关键点:
	 serviceClass:服务接口 
		address:客户端访问服务路径 : http://localhost:8086/spring-webservice/ws/userService -->

	<jaxws:client id="userServiceClient" serviceClass="com.cxf.ws.service.UserService"
		address="http://localhost:8086/spring-webservice/ws/userService">
		<jaxws:inInterceptors>
			<bean class="org.apache.cxf.interceptor.LoggingInInterceptor" />
		</jaxws:inInterceptors>
		<jaxws:outInterceptors>
			<bean class="org.apache.cxf.interceptor.LoggingOutInterceptor" />
		</jaxws:outInterceptors>
	</jaxws:client>
</beans>

serviceClass:服务提供方service接口对象全限定名
adress:客户端访问服务路径

客户端调用测试:

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = "classpath:applicationContext.xml")
public class clientTest {

	@Autowired
	private UserService UserService;

	@Test
	public void wsClient() {

		System.out.println("==========开始调用jax-ws接口===========");
		String sayHello = UserService.sayHello();
		System.out.println(sayHello);
	}

}

通过@Autowired注入 客户端配置的服务端接口(实际上就是服务端接口的代理对象proxy),通过代理对象调用方法,执行客户端调用服务端

控制台输出日志

==========开始调用jax-ws接口===========
三月 30, 2020 12:22:07 下午 org.apache.cxf.services.UserServiceService.UserServicePort.UserService
信息: Outbound Message
---------------------------
ID: 1
Address: http://localhost:8086/spring-webservice/ws/userService
Encoding: UTF-8
Http-Method: POST
Content-Type: text/xml
Headers: {Accept=[*/*], SOAPAction=[""]}
Payload: <soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"><soap:Body><ns2:sayHello xmlns:ns2="http://service.ws.cxf.com/"/></soap:Body></soap:Envelope>
--------------------------------------
三月 30, 2020 12:22:07 下午 org.apache.cxf.services.UserServiceService.UserServicePort.UserService
信息: Inbound Message
----------------------------
ID: 1
Response-Code: 200
Encoding: UTF-8
Content-Type: text/xml; charset=UTF-8
Headers: {content-type=[text/xml; charset=UTF-8], Date=[Mon, 30 Mar 2020 04:22:07 GMT], Server=[Jetty(9.2.11.v20150529)], transfer-encoding=[chunked]}
Payload: <soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"><soap:Body><ns2:sayHelloResponse xmlns:ns2="http://service.ws.cxf.com/"><return>=============hello jax-ws!!!=================</return></ns2:sayHelloResponse></soap:Body></soap:Envelope>
--------------------------------------
=============hello jax-ws!!!=================

至此WebService基于CXF框架实现JAX-WS服务成功,是不是非常简单了,在不同公司/应用之间调取服务,使用WebService技术是非常常用的。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章