一文搞懂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技術是非常常用的。

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