廢話不多說,本周的技術要點如下:
一、webService原理
webService的有關知識網上太多了,但是我沒有看懂,webService有關的三個要素:SOAP、UDDI 、WSDL ,我只知道了WSDL的使用方法。我所理解的webService如下:
首先,webService是為了解決什么問題呢?因為我們知道系統中前端可以使用REST接口對后台進行數據請求,但是這是程序內部的數據接口,不可能直接發布給外部讓別人使用url請求的,系統都會對這些開發的接口進行攔截和身份校驗。那么如果我們想在外界獲取到某個系統的數據,就需要webService了。比如天氣信息網站的天氣信息等公開分享的信息,我們都可以使用webService技術來獲取。
接下來webService是什么原理呢?我們都知道使用java進行接口開發的時候,參數和返回的結果一般會使用到實體類,比如有這樣的一個接口,是通過用戶的名字獲取數據,那么此時的controller的方法可能會是這樣寫:
@RequestMapping(value = {"/list","/checkList"}) public ResponseResult getUser(User u){ ResponseResult re = this.createResult(u); return re; }
接口中參數User為用戶的實體類,前台傳過來的用戶名傳到后台會自動封裝成User對象。后台接口查詢到數據后,會將數據封裝成ResponseResule對象,傳遞給前台。這樣前台就可以根據傳遞過來的信息展現數據了。
通常開發的數據接口是前台和后台進行交互,之所以前台可以很方便的和后台進行交互,是因為前台對於參數對象和結果對象是使用JSON進行轉換了,任你后台是什么實體類,在我前台面前一律全是JSON對象。
但是對於webService的話,並不是前台和后台進行交互,而是后台和后台進行交互。比如系統A的后台語言是java,系統B的后台語言也是java,那么當系統B使用接口對系統A發起請求的時候,也是需要將一個實體對象作為參數傳遞過去,將一個實體對象作為查詢結果進行返回。但是系統B怎么可能知道系統A的這個接口的參數實體對象和結果實體對象的構成呢?而如何讓系統B知道接口中這兩個對象的構成就成為了webService的重點。
那么webService是怎么實現將系統A的接口的構成告訴給系統B的呢?
首先webService需要一個服務端和客戶端,很顯然,系統A就是服務端,系統B就是客戶端了。另外這個服務端需要新開一個servlet,以專供進行服務接口的請求。此時系統A會通過webService的服務端發布一個接口,這個接口和程序開發的接口不同之處在於:
1、這個接口可以在外部訪問,如果這個接口可以對外部共享的話,那么任何人都可以對這個接口進行訪問;
2、接口的結尾都是?WSDL(好像還有別的可能是實現技術不同,可通過查詢天氣的服務接口查看http://www.webxml.com.cn/zh_cn/weather_icon.aspx
3、返回的是正常人看不懂的XML文檔。
這些東西光說是沒用的,直接掛上天氣服務的WSDL服務接口的url路徑:http://www.webxml.com.cn/WebServices/WeatherWS.asmx?wsdl。
這樣系統B在對服務接口進行請求后會獲得一個xml文檔,這個文檔包含了想要請求到數據的前期一切信息。我們可以通過這個文檔構建系統A的接口環境,重建參數對象和結果對象,構成一個客戶端。這樣當我們在對系統A進行數據請求的時候(此時的數據接口不是前面的獲得wsdl的地址,可能放在xml文檔中)就可以通過對象進行數據傳輸了。
那么問題來了,怎么通過wsdl的這個xml文檔重建接口環境,生成一個webService的客戶端呢?
其實這個方法有很多種,可以網上去查,我使用的方法是使用編輯器的內置的方法,idea和eclipse應該都有(后面的CXF的具體實現會有體現)。另外jdk的bin中也有一個wsimport.exe,可以進行轉換具體使用方法為:wsimport -keep -p 自定義包名 -d 存放的地址 (wsdl地址)。不過我沒有試過。。。。
二、使用CXF框架構建webService
CXF是webService的一個框架,可以很簡單方便的搭建一個webService。現在我們做一個demo,具體過程如下:
1、下載CXF http://cxf.apache.org/
2、創建項目,將cxf的lib中的所有jar包引入到項目中
3、創建服務端
我所生成的項目結構如下:
其中,HelloWord是個接口,HellowWorldImp是其實現類,Servier用來創建一個服務,User是一個實體類,作為一個結構的參數對象。因此前三個是我們構成webServie服務端所必須的文件。先將三個文件的代碼貼下:
HelloWorld.java
package com; import javax.jws.WebService; import java.util.List; /** * @InterfaceName HelloWorld * @Description TODO * @Author jyy * @Date 2019/7/18 10:45 * @Version 1.0 **/ @WebService public interface HelloWorld { String sayHi(String text); String getUser(User user); List<User> getListUser(); }
重要的是加上@WebService注解。接口里面的方法類似於controller中url對應的方法,可以理解為這個webService提供了三個接口服務:sayHi,getUser,getListUser。
HelloWorldImpl.java
package com; import javax.jws.WebService; import java.util.ArrayList; import java.util.List; /** * @ClassName HelloWorldImp * @Description TODO * @Author jyy * @Date 2019/7/18 10:49 * @Version 1.0 **/ @WebService(endpointInterface = "com.HelloWorld", serviceName = "HelloWorldService",portName="HelloWorldServicePort") public class HelloWorldImp implements HelloWorld { @Override public String sayHi(String text) { System.out.println("sayHi called..."); return "Hi :" + text; } @Override public String getUser(User user) { System.out.println("sayUser called..."); return "User:[id=" + user.getId() + "][name=" + user.getName() + "]"; } @Override public List<User> getListUser() { System.out.println("getListUser called..."); List<User> lst = new ArrayList<User>(); lst.add(new User(2, "u2")); lst.add(new User(3, "u3")); lst.add(new User(4, "u4")); lst.add(new User(5, "u5")); lst.add(new User(6, "u6")); return lst; } }
標紅的注解是必須的,具體的值可自己配置
Server.java
package com; import javax.xml.ws.Endpoint; public class Server { protected Server() throws Exception { // START SNIPPET: publish System.out.println("Starting Server"); HelloWorldImp implementor = new HelloWorldImp(); String address = "http://localhost:8080/helloWorld"; Endpoint.publish(address, implementor); // END SNIPPET: publish } public static void main(String[] args) throws Exception { new Server(); System.out.println("服務端已啟動"); } }
然后啟動server.java中的main方法,啟動一個服務器。然后在瀏覽器中輸入網址:http://localhost:8080/helloWorld?wsdl,可以看到如下網頁。
此時,服務端搭建成功。
4、創建客戶端
另開一個項目,用來模擬系統B
然后構建客戶端,我是用的idea,內置的構建方法截圖如下:
右鍵項目根目錄,然后點擊下面兩個紅箭頭,出現如下截圖:
將wsdl的url地址填入第一個箭頭所示,第二個為所創建的文件存放的文件夾,第三個是使用什么進行構建,其他的我都沒有安裝,使用的是如圖的方法。完成后,idea會在demo目錄先自動創建如下文件:
記得eclipse可以通過wsdl的xml文檔生成api,應該使用到了這些文件。
假設知道了服務接口的api,那么就知道了怎么去請求了,接下來就需要構建客戶端了。創建一個文件Main.java
package client; import demo.HelloWorld; import demo.HelloWorldService; /** * @ClassName * @Description TODO * @Author jyy * @Date 2019/7/18 13:30 * @Version 1.0 **/ public class Main { public static void main(String[] args) { HelloWorldService factory = new HelloWorldService(); // 此處返回的只是遠程WebService的代理 HelloWorld cxfWebService = factory.getHelloWorldServicePort(); System.out.println(cxfWebService.sayHi("jyy")); } }
執行文件后,控制台顯示:Hi :jyy
至此,服務端和客戶端搭建完成,webService流程演示完畢。
三、將CXF集成到SpringMVC
想要用於實際項目,就需要和項目中的框架集成,我是用的是SpingMVC。
1、maven引入
<dependency> <groupId>org.apache.cxf</groupId> <artifactId>cxf-rt-frontend-jaxws</artifactId> <version>3.0.4</version> </dependency> <dependency> <groupId>org.apache.cxf</groupId> <artifactId>cxf-rt-transports-http</artifactId> <version>3.0.4</version> </dependency>
需要注意的是版本。我使用的springmvc版本是4,那么CXF需要是3。之前看別人的技術帖子CXF用的版本是2,所以一直報錯。要是報錯,如果代碼沒有問題的話,一定就是CXF的版本引入錯了。切記切記
2、配置文件中添加bean
<import resource="classpath:META-INF/cxf/cxf.xml" /> <import resource="classpath:META-INF/cxf/cxf-servlet.xml" /> <jaxws:endpoint id="helloService" implementor="com.zzdc.cxf.CXFWebServiceImpl" address="/cxfService" />
注意這里的使用的是項目實際中的文件,所以服務接口路徑和文件和上一個demo中的是不同的。
3、web.xml中創建一個servlet
<servlet> <servlet-name>CXFServlet</servlet-name> <servlet-class>org.apache.cxf.transport.servlet.CXFServlet</servlet-class> <load-on-startup>1</load-on-startup> </servlet> <servlet-mapping> <servlet-name>CXFServlet</servlet-name> <url-pattern>/cxf/*</url-pattern> </servlet-mapping>
啟動后發布,此時url的地址應該是:localhost:8080/cxf/cxfService?wsdl,也就是由上面配置文件中bean的address和web.xml中的url-pattern所組成,需要注意。
第一周技術博客完成!