WebService案例入門(基礎篇)
一、簡介
Webservice:跨語言跨平臺的遠端呼叫技術。Web service 即web服務,它是一種跨程式語言和跨作業系統平臺的遠端呼叫技術即跨平臺遠端呼叫技術。
JAVA 中共有三種WebService 規範,分別是JAX-WS(JAX-RPC)、JAXM&SAAJ、JAX-RS。
webService三要素:soap、wsdl、uddi
JAX-WS 的全稱為 Java API for XML-Based Webservices ,早期的基於SOAP 的JAVA 的Web 服務規範JAX-RPC(Java API For XML-Remote Procedure Call).
JAXM(JAVA API For XML Message)主要定義了包含了傳送和接收訊息所需的API,SAAJ(SOAP With Attachment API For Java,JSR 67)是與JAXM 搭配使用的API,為構建SOAP 包和解析SOAP 包提供了重要的支援,支援附件傳輸等.
JAX-RS 是JAVA 針對REST(Representation State Transfer)風格制定的一套Web 服務規範.
二、應用場景
在做企業整體資訊化時,企業中一般都或多或少的存在一些既存系統,這些各種各樣的系統不可能全部推翻,重新規劃和開發,因為很多供應商在某一領域也做的很專業,博眾家之長並進行整合應該是一個比較現實和可取的做法。各個系統之間通過WebService進行整合,不僅縮短了開發週期,降低了風險,還減少了程式碼複雜度,並能夠增強應用程式的可維護性,因為webservice支援跨平臺且遵循標準協議(soap)。
將一個軟體的功能以webservice方式暴露出來,達到軟體重用。例如上邊分析的天氣預報,將天氣查詢功能以webservice介面方式暴露出來非常容易整合在其它系統中;再比如一個第三方物流系統將快遞查詢、快遞登記暴露出來,從而整合在電子商務系統中。
三、soap協議
SOAP 是一種網路通訊協議
SOAP即Simple Object Access Protocol簡易物件訪問協議
SOAP 用於跨平臺應用程式之間的通訊
SOAP 被設計用來通過因特網(http)進行通訊
SOAP = HTTP+XML,其實就是通過HTTP發xml資料
SOAP 很簡單並可擴充套件支援面向物件
SOAP 允許您跨越防火牆
Socket是所有通訊的基礎也是語言個無關平臺無關。
Socket使用的是tcp協議,傳輸效率高。適合傳遞大資料高併發場景,高併發的情況需要實現多執行緒並且使用到執行緒池,編碼複雜。Sockt的高併發框架mina。
Socket只是流的傳輸,傳輸的格式需要程式設計師自己定義。
Webservice使用的是soap協議,soap協議基於http協議的應用層協議,本質就是http+xml。Soap協議是w3c標準,傳輸效率低。使用傳輸資料不是太大的場合,也是支援高併發的,受限於web容器。支援soap協議和wsdl兩者都是國際通用標準,不需要自定義資料格式,只需要面向物件開發。
四、WSDL
Webservice的使用說明書。描述了webservice的服務地址以及webservice服務介面、引數、返回值。
閱讀方法:從下往上讀。
- 先找service節點:每個wsdl中,有且只有一個service節點。也叫服務檢視節點。service中有port節點服務端埠。
- 根據port節點的binding屬性找binding節點。根據binding節點的type屬性找portType節點。
- portType節點就是我們定義的SEI服務的介面型別。Prottype中的operation 節點就是方法名稱。
- operation 節點的input就是引數的定義,output就是返回值的定義。
- Input有個屬性叫做message,message屬性對應message節點。其中有一個element,對應element節點。
- Element節點定義中xsd中。定義了資料的型別。引數和返回值都在其中定義。
五、天氣查詢系統(基礎)
到這裡,對於webservice的基本概念都已經瞭解了,那麼就開始我們愉快的編碼步驟吧!這個的話我們需要新建兩個java工程,一個做服務端,一個作為客戶端。原始碼可以通過文末的連結下載。
5.1 服務端
1、編寫一個SEI,也就是一個介面
public interface WeatherInterface {
String queryWeather(String cityName);
}
2、編寫一個SEI實現類,需要實現SEI介面,而且還需要在這個實現類上面新增一個@Webservice註解
@Webservice
public class WeatherInterfaceImpl implements WeatherInterface {
public String queryWeather(String cityName) {
System.out.println("接收到客戶端傳送的城市名稱:"+cityName);
String result="晴,高溫預警";
return result;
}
}
在這一步,如果你因為webservice的添加註解系統報錯的話,可以先按照報錯提示的先轉變為jase-1.5,然後自己再去build path中重新變回你原來許需要的java1.7或者1.8.
3、釋出服務。使用Endpoint的靜態方法publish。
public class WeatherServer {
public static void main(String[] args) {
//釋出服務
Endpoint.publish("http://127.0.0.1:11111/weather", new WeatherInterfaceImpl());
}
}
看到效果則說明啟動成功了。
5.2 客戶端
對於客戶端,我們可利用只用java中的wsimport來自動生成客戶端程式碼。
使用Wsimport生成客戶端呼叫程式碼,
在jdk的安裝目錄的bin目錄中,有一個wsimport命令。
可以根據wsdl文件生成客戶端呼叫程式碼。
新建一個java工程WebServiceClient,然後到這個工程的src目錄下面,在src目錄下通過cmd執行以下命令:(注意空格)
生成好之後我們就可以直接呼叫了:
1、建立一個服務檢視物件
2、從服務試圖獲得porttype(SEI)物件
3、呼叫服務端方法
4、列印結果
public class WeatherClient {
public static void main(String[] args) {
WeatherInterfaceImplService service=new WeatherInterfaceImplService();
WeatherInterfaceImpl portType=service.getWeatherInterfaceImplPort();
String result=portType.queryWeather("衡陽");
System.out.println(result);
}
}
六、天氣查詢(公網)
剛才使用的方法是我們自己定義了,但是往往生活中,我們需要時時更新的天氣資訊,所以這個時候我們就可以呼叫公網來處理了,和前面的一樣,我們也需要服務端和客戶端。
6.1 服務端
服務端使用第三方的,匯入其已經生成好的多個類,cn.com.webxml.這個可以在我提供的原始碼中直接下載。
6.2 客戶端
public static void main(String[] args) {
//建立服務檢視
//WeatherWebService service=new WeatherWebService();
URL url = null;
try {
url = new URL("http://www.webxml.com.cn/WebServices/WeatherWebService.asmx?WSDL");
} catch (MalformedURLException e) {
e.printStackTrace();
}
QName qName=new QName("http://WebXml.com.cn/", "WeatherWebService");
Service service=Service.create(url,qName);
WeatherWebServiceSoap portType=service.getPort(WeatherWebServiceSoap.class);
//service.getWeatherWebServiceSoap();
ArrayOfString arrayOfString=portType.getWeatherbyCityName("衡陽");
for (String string : arrayOfString.getString()) {
System.out.println(string);
}
}
七、區域查詢系統
建立區域查詢服務系統,對外發布WebService服務,供客戶端呼叫,根據parentid查詢區域資訊。客戶端向服務端傳遞xml格式資料,服務端向客戶端響應xml格式資料。
傳遞xml資料的原因:
- 1、跨語言時可能會花費很多時間去除錯,如果直接傳遞xml會節省除錯的時間。引數和返回值都是字串型別,非常簡單。
- 2、如果引數發生變化後,可以不要修改介面,不能重新生成客戶端程式碼。
- 3、xml格式的資料是跨平臺的。
實現步驟
- 建立一個java工程。
- 匯入mysql資料庫驅動及其相關的jar包。
- 建立一個SEI。
- 建立一個SEI實現類,呼叫dao查詢區域列表。
- 釋出服務,使用Endpoint的publish方法釋出服務。
客戶端:
- 生成客戶端呼叫程式碼
- 建立服務檢視
- 從服務檢視獲得portType
- 呼叫服務端方法
7.1 服務端
1、新建一個區域資訊介面,AreaModel .java
private String areaid;
private String areaname;
private String parentid;
private String arealevel;
實現其get\set方法
2、新建一個AreaDao.java,用於訪問mysql資料庫中的區域資訊,這個資料庫的sql指令碼已放到原始碼中,讀者可自行下載。就是一個連線資料庫的功能。
public List<AreaModel> queryArea(String parentid, int start, int end) {
Connection connection = null;
PreparedStatement pstmt = null;
ResultSet resultSet = null;
List<AreaModel> areaList = new ArrayList<>();
try {
//載入資料庫驅動
Class.forName("com.mysql.jdbc.Driver");
//獲得connection
connection = DriverManager.getConnection("jdbc:mysql:///day15", "zp", "a");
String sql="select *from area where parentid=? limit ?,? ";
pstmt=connection.prepareStatement(sql);
pstmt.setString(1,parentid);
pstmt.setInt(2, start-1);
pstmt.setInt(3, end-start-1);
resultSet=pstmt.executeQuery();
while(resultSet.next()){
AreaModel model=new AreaModel();
model.setAreaid(resultSet.getString("areaid"));
model.setAreaname(resultSet.getString("areaname"));
model.setArealevel(resultSet.getString("arealevel"));
model.setParentid(resultSet.getString("parentid"));
//新增到區域列表
areaList.add(model);
}
}catch(Exception e){
e.printStackTrace();
}finally{
try {
resultSet.close();
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
try {
pstmt.close();
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
try {
connection.close();
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
return areaList;
}
3、寫一個區域查詢SEI
public interface AreaInterface {
String queryArea(String area);
}
4、實現其Sei的dao方法。
@WebService
public class AreaInterfaceImpl implements AreaInterface {
@Override
public String queryArea(String area) {
//解析xml查詢條件
AreaModel model = null;
try {
model = parseXml(area);
} catch (DocumentException e) {
e.printStackTrace();
}
AreaDao dao=new AreaDao();
List<AreaModel> list=dao.queryArea(model.getParentid(), model.getStart(), model.getEnd());
String result = null;
try {
result = list2xml(list);
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return result;
}
private AreaModel parseXml(String xml) throws DocumentException{
Document document=DocumentHelper.parseText(xml);
String parentid=document.selectSingleNode("/queryarea/parentid").getText();
String start=document.selectSingleNode("/queryarea/start").getText();
String end=document.selectSingleNode("/queryarea/end").getText();
AreaModel model=new AreaModel();
model.setParentid(parentid);
model.setStart(Integer.parseInt(start));
model.setEnd(Integer.parseInt(end));
return model;
}
private String list2xml(List<AreaModel> list) throws Exception {
Document document = DocumentHelper.createDocument();
//新增以根節點
Element root = document.addElement("areas");
for (AreaModel areaModel : list) {
Element area = root.addElement("area");
area.addElement("areaid").setText(areaModel.getAreaid());
area.addElement("areaname").setText(areaModel.getAreaname());
area.addElement("arealevel").setText(areaModel.getArealevel());
area.addElement("parentid").setText(areaModel.getParentid());
}
return document.asXML();
}
}
5、釋出服務
public class AreaServer {
public static void main(String[] args) {
Endpoint.publish("http://127.0.0.1:11111/area", new AreaInterfaceImpl());
}
}
通過瀏覽器訪問看到這個介面就說明執行成功了。
7.2 客戶端
和之前同樣的方法,使用wsimport來自動生成客戶端程式碼。
在java工程的src目錄中執行cmd。
wsimport -s . http://127.0.0.1:11111/area?wsdl
生成好之後如下所示:
客戶端進行呼叫
public class AreaClient {
public static void main(String[] args) {
AreaInterfaceImplService service=new AreaInterfaceImplService();
AreaInterfaceImpl portType=service.getAreaInterfaceImplPort();
String result=portType.queryArea(getQueryXml("1.1.",1,10));
System.out.println(result);
}
private static String getQueryXml(String parentid,int start,int end){
String xml="<?xml version=\"1.1\" encoding=\"utf-8\"?>\n" +
"<queryarea>\n"+
"<parentid>"+parentid+"</parentid>\n"+
"<start>"+start+"</start>\n"+
"<end>"+end+"</end>\n"+
"</queryarea>";
return xml;
}
}
輸出結果:
總結:本篇文章關於webservice的內容都是非常非常基礎的,裡面涉及到的一些協議或者使用方法估計有些人是不知道的,我們一方面需要擴充套件自己的視野,瞭解更多的新東西,內心不要對一些自己不是很常見的東西去抵觸,有的朋友會說“嗯,你講的這些東西都太基礎了,我們在實際專案中要如何應用”,有的直接是呼叫第三方的url和參考文件就搞定了,其實是比較方便的,那麼我們在使用別人的東西的時候有沒有考慮過其底層的實現原理,深入到其骨髓才是真的掌握了,不然我們依然是那種只會複製黏貼、只會盲目的呼叫各種第三方API來的人了。共勉!下次會帶來更加實用的cxf實現jax-ws。歡迎關注!