Axis2是一套嶄新的WebService引擎,該版本是對Axis1.x重新設計的產物。Axis2不僅支持SOAP1.1SOAP1.2,還集成了非常流行的REST WebService,同時還支持SpringJSON等技術。這些都將在后面的系列教程中講解。在本文中主要介紹了如何使用Axis2開發一個不需要任何配置文件的WebService,并在客戶端使用JavaC#調用這個WebService

          一、Axis2的下載和安裝

          ??? 讀者可以從如下的網址下載Axis2的最新版本:

          ??? http://ws.apache.org/axis2/

          ??? 在本文使用了目前Axis2的最新版本1.4.1。讀者可以下載如下兩個zip包:

          ??? axis2-1.4.1-bin.zip

          ??? axis2-1.4.1-war.zip

          ??? 其中axis2-1.4.1-bin.zip文件中包含了Axis2中所有的jar文件, axis2-1.4.1-war.zip文件用于將WebService發布到Web容器中。

          ??? axis2-1.4.1-war.zip文件解壓到相應的目錄,將目錄中的axis2.war文件放到<Tomcat安裝目錄>\webapps目錄中(本文使用的Tomcat的版本是6.x),并啟動Tomcat。

          ??? 在瀏覽器地址欄中輸入如下的URL

          ??? http://localhost:8080/axis2/

          ??? 如果在瀏覽器中顯示出如圖1所示的頁面,則表示Axis2安裝成功。



          圖1

          二、編寫和發布WebService

          ??對于用Java實現的服務程序給人的印象就是需要進行大量的配置,不過這一點在Axis2中將被終結。在Axis2中不需要進行任何的配置,就可以直接將一個簡單的POJO發布成WebService。其中POJO中所有的public方法將被發布成WebService方法。

          ??? 下面我們來實現一個簡單的POJO,代碼如下:

          public?class?SimpleService
          {
          ????
          public?String?getGreeting(String?name)
          ????{
          ????????
          return?"你好?"?+?name;
          ????}????
          ????
          public?int?getPrice()
          ????{
          ????????
          return?new?java.util.Random().nextInt(1000);
          ????}????
          }

          ??? SimpleService類中有兩個方法,由于這兩個方法都是public方法,因此,它們都將作為WebService方法被發布。

          ??? 編譯SimpleService類后,將SimpleService.class文件放到<Tomcat安裝目錄>\webapps\axis2\WEB-INF\pojo目錄中(如果沒有pojo目錄,則建立該目錄)?,F在我們已經成功將SimpleService類發布成了WebService。在瀏覽器地址欄中輸入如下的URL

          http://localhost:8080/axis2/services/listServices

          ??? 這時當前頁面將顯示所有在Axis2中發布的WebService,如圖2所示。



          圖2

          ??? 在瀏覽器地址欄中輸入如下的兩個URL來分別測試getGreetinggetPrice方法:

          http://localhost:8080/axis2/services/SimpleService/getGreeting?name=bill

          http://localhost:8080/axis2/services/SimpleService/getPrice

          ??? 3和圖4分別顯示了getGreetinggetPrice方法的測試結果。

          圖3? getGreeting方法的測試結果

          圖4? getPrice方法的測試結果

          ??? 在編寫、發布和測試0配置的WebService時應注意如下幾點:

          ??? 1.?POJO類不能使用package關鍵字聲明包。

          ??? 2.?Axis2在默認情況下可以熱發布WebService,也就是說,將WebService.class文件復制到pojo目錄中時,Tomcat不需要重新啟動就可以自動發布WebService。如果想取消Axis2的熱發布功能,可以打開<Tomcat安裝目錄>\webapps\axis2\WEB-INF\conf\axis2.xml,找到如下的配置代碼:

          <parameter?name="hotdeployment">true</parameter>

          ??? true改為false即可。要注意的是,Axis2在默認情況下雖然是熱發布,但并不是熱更新,也就是說,一旦成功發布了WebService,再想更新該WebService,就必須重啟Tomcat。這對于開發人員調試WebService非常不方便,因此,在開發WebService時,可以將Axis2設為熱更新。在axis2.xml文件中找到<parameter name="hotupdate">false</parameter>,將false改為true即可。

          ??? 3.?在瀏覽器中測試WebService時,如果WebService方法有參數,需要使用URL的請求參數來指定該WebService方法參數的值,請求參數名與方法參數名要一致,例如,要測試getGreeting方法,請求參數名應為name,如上面的URL所示。

          ??? 4.?發布WebServicepojo目錄只是默認的,如果讀者想在其他的目錄發布WebService,可以打開axis2.xml文件,并在<axisconfig>元素中添加如下的子元素:

          ????<deployer?extension=".class"?directory="my"?class="org.apache.axis2.deployment.POJODeployer"/>

          ??? 上面的配置允許在<Tomcat安裝目錄>"webapps"axis2"WEB-INF"my目錄中發布WebService。例如,將本例中的SimpleService.class復制到my目錄中也可以成功發布(但要刪除pojo目錄中的SimpleService.class,否則WebService會重名)。

          三、Java實現調用WebService的客戶端程序

          ??? WebService是為程序服務的,只在瀏覽器中訪問WebService是沒有意義的。因此,在本節使用Java實現了一個控制臺程序來調用上一節發布的WebService。調用WebService的客戶端代碼如下:

          package?client;

          import?javax.xml.namespace.QName;
          import?org.apache.axis2.addressing.EndpointReference;
          import?org.apache.axis2.client.Options;
          import?org.apache.axis2.rpc.client.RPCServiceClient;

          public?class?RPCClient
          {
          ????
          public?static?void?main(String[]?args)?throws?Exception??
          ????{
          ????????
          //??使用RPC方式調用WebService????????
          ????????RPCServiceClient?serviceClient?=?new?RPCServiceClient();
          ????????Options?options?
          =?serviceClient.getOptions();
          ????????
          //??指定調用WebService的URL
          ????????EndpointReference?targetEPR?=?new?EndpointReference(
          ????????????????
          "http://localhost:8080/axis2/services/SimpleService");
          ????????options.setTo(targetEPR);
          ????????
          //??指定getGreeting方法的參數值
          ????????Object[]?opAddEntryArgs?=?new?Object[]?{"超人"};
          ????????
          //??指定getGreeting方法返回值的數據類型的Class對象
          ????????Class[]?classes?=?new?Class[]?{String.class};
          ????????
          //??指定要調用的getGreeting方法及WSDL文件的命名空間
          ????????QName?opAddEntry?=?new?QName("http://ws.apache.org/axis2",?"getGreeting");
          ????????
          //??調用getGreeting方法并輸出該方法的返回值
          ????????System.out.println(serviceClient.invokeBlocking(opAddEntry,?opAddEntryArgs,?classes)[0]);
          ????????
          //??下面是調用getPrice方法的代碼,這些代碼與調用getGreeting方法的代碼類似
          ????????classes?=?new?Class[]?{int.class};
          ????????opAddEntry?
          =?new?QName("http://ws.apache.org/axis2",?"getPrice");
          ????????System.out.println(serviceClient.invokeBlocking(opAddEntry,?
          new?Object[]{},?classes)[0]);
          ????}?
          }

          運行上面的程序后,將在控制臺輸出如下的信息:
          你好?超人
          443

          ??? 在編寫客戶端代碼時應注意如下幾點:

          ??? 1.?客戶端代碼需要引用很多Axis2jar包,如果讀者不太清楚要引用哪個jar包,可以在Eclipse的工程中引用Axis2發行包的lib目錄中的所有jar包。

          ??? 2.?在本例中使用了RPCServiceClient類的invokeBlocking方法調用了WebService中的方法。invokeBlocking方法有三個參數,其中第一個參數的類型是QName對象,表示要調用的方法名;第二個參數表示要調用的WebService方法的參數值,參數類型為Object[];第三個參數表示WebService方法的返回值類型的Class對象,參數類型為Class[]。當方法沒有參數時,invokeBlocking方法的第二個參數值不能是null,而要使用new Object[]{}。

          ??? 3.?如果被調用的WebService方法沒有返回值,應使用RPCServiceClient類的invokeRobust方法,該方法只有兩個參數,它們的含義與invokeBlocking方法的前兩個參數的含義相同。

          ??? 4.?在創建QName對象時,QName類的構造方法的第一個參數表示WSDL文件的命名空間名,也就是<wsdl:definitions>元素的targetNamespace屬性值,下面是SimpleService類生成的WSDL文件的代碼片段:

          <?xml?version="1.0"?encoding="UTF-8"?>
          <wsdl:definitions?xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/"?xmlns:ns1="http://org.apache.axis2/xsd"
          xmlns:ns
          ="http://ws.apache.org/axis2"?xmlns:wsaw="http://www.w3.org/2006/05/addressing/wsdl"
          xmlns:http
          ="http://schemas.xmlsoap.org/wsdl/http/"?xmlns:xs="http://www.w3.org/2001/XMLSchema"
          xmlns:mime
          ="http://schemas.xmlsoap.org/wsdl/mime/"?xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"
          xmlns:soap12
          ="http://schemas.xmlsoap.org/wsdl/soap12/"
          targetNamespace
          ="http://ws.apache.org/axis2"
          >
          ????
          <wsdl:types>
          ?????????
          ????
          </wsdl:types>
          ?????
          </wsdl:definitions>

          四、用wsdl2java簡化客戶端的編寫

          ??? 也許有很多讀者會說“有沒有搞錯啊,只調用兩個WebService方法用要寫這么多代碼,太麻煩了”。

          ??? 不過幸好Axis2提供了一個wsdl2java.bat命令可以根據WSDL文件自動產生調用WebService的代碼。wsdl2java.bat命令可以在<Axis2安裝目錄>"bin目錄中找到。在使用wsdl2java.bat命令之前需要設置AXIS2_HOME環境變量,該變量值是<Axis2安裝目錄>。

          ??? Windows控制臺輸出如下的命令行來生成調用WebService的代碼:

          %AXIS2_HOME%\bin\wsdl2java -uri http://localhost:8080/axis2/services/SimpleService?wsdl -p client -s -o stub

          ??? 其中-url參數指定了wsdl文件的路徑,可以是本地路徑,也可以是網絡路徑。-p參數指定了生成的Java類的包名,-o參數指定了生成的一系列文件保存的根目錄。在執行完上面的命令后,讀者就會發現在當前目錄下多了個stub目錄,在."stub"src"client目錄可以找到一個SimpleServiceStub.java文件,該文件復雜調用WebService,讀者可以在程序中直接使用這個類,代碼如下:

          package?client;

          import?javax.xml.namespace.QName;
          import?org.apache.axis2.addressing.EndpointReference;
          import?org.apache.axis2.client.Options;
          import?org.apache.axis2.rpc.client.RPCServiceClient;

          public?class?StubClient
          {
          ????
          public?static?void?main(String[]?args)?throws?Exception??
          ????{
          ????????SimpleServiceStub?stub?
          =?new?SimpleServiceStub();
          ????????SimpleServiceStub.GetGreeting?gg?
          =?new?SimpleServiceStub.GetGreeting();
          ????????gg.setName(
          "比爾");
          ????????System.out.println(?stub.getGreeting(gg).get_return());
          ????????System.out.println(stub.getPrice().get_return());
          ????}?
          }

          ??? 上面的代碼大大簡化了調用WebService的步驟,并使代碼更加簡潔。但要注意的是,wsdl2java.bat命令生成的Stub類將WebService方法的參數都封裝在了相應的類中,類名為方法名,例如,getGreeting方法的參數都封裝在了GetGreeting類中,要想調用getGreeting方法,必須先創建GetGreeting類的對象實例。

          五、使用C#調用WebService

          ??? 從理論上說,WebService可以被任何支持SOAP協議的語言調用。在Visual Studio中使用C#調用WebService是在所有語言中最容易實現的(VB.net的調用方法類似,也同樣很簡單)。

          ??? 新建一個Visual Studio工程,并在引用Web服務的對話框中輸入如下的URL,并輸入Web引用名為“WebService”:

          ??? http://localhost:8080/axis2/services/SimpleService?wsdl

          ??? 然后引用Web服務的對話框就會顯示該WebService中的所有的方法,如圖5所示。



          圖5

          ??? 在完成上面的工作后,只需要如下三行C#代碼就可以調用getGreetinggetPrice方法,并顯示這兩個方法的返回值:

          WebService.SimpleService?simpleService?=?new?WSC.WebService.SimpleService();
          MessageBox.Show(?simpleService.getGreeting(
          "比爾"));
          MessageBox.Show(simpleService.getPrice().@return.ToString());

          ??? .net解析WSDL文件時直接將getGreeting方法的參數映射為String類型,因此,可以直接進行傳值。
          ??? 從上面的調用過程可以看出,添加Web引用的過程就相當于在Java中調用wsdl2java.bat自動生成stub類的過程。只是在調用stub類時與C#有一定的區別,但從總體上來說,都大大簡化了調用WebService的過程。

          posted @ 2009-02-23 17:37 zhuyongjp 閱讀(924) | 評論 (0)編輯 收藏
           

          接口 interface

          import ?J2EE技術的爬蟲.util.Collection;
          import ?J2EE技術的爬蟲.util.List;

          public ? interface ?IMathService? {
          ?
          /**
          ??*?加
          ??*?
          @param ?a
          ??*?
          @param ?b
          ??*?
          @return
          ??
          */

          ?
          public ? int ?add( int ?a, int ?b);
          ?
          /**
          ??*?減
          ??*?
          @param ?a
          ??*?
          @param ?b
          ??*?
          @return
          ??
          */

          ?
          public ? int ?sub( int ?a, int ?b);
          /**
          上傳二進制文件
          */

          ??
          public ?String?sendFile(String?fileName, byte []?file?);
          ?
          }



          實現 implements

          import ?J2EE技術的爬蟲.io.File;
          import ?J2EE技術的爬蟲.io.FileOutputStream;
          import ?J2EE技術的爬蟲.sql.Connection;
          import ?J2EE技術的爬蟲.sql.ResultSet;
          import ?J2EE技術的爬蟲.sql.Statement;
          import ?J2EE技術的爬蟲.util.ArrayList;
          import ?J2EE技術的爬蟲.util.Collection;
          import ?J2EE技術的爬蟲.util.List;

          import ?com.newsoft.oa.bean.User;
          import ?com.newsoft.oa.uitl.Connector;
          import ?com.thoughtworks.xstream.XStream;

          public ? class ?MathServiceImpl? implements ?IMathService {
          ??
          public ? int ?add( int ?a, int ?b) {
          ???
          return ?a + b;
          ??}

          ??
          ??
          public ? int ?sub( int ?a, int ?b) {
          ???
          return ?a - b;
          ??}
          ?
          ??
          public ?String?getWelComeStr(String?name) {
          ???
          return ? " hi? " + name + " !?歡迎你 " ;
          ??}

          ??
          public ?List?getUsers() {
          ???List?l
          = new ?ArrayList();
          ???l.add(
          " name " );
          ???l.add(
          " password " );
          ???l.add(
          " sex " );
          ???
          return ?l;
          ??}


          public ?String?sendFile(String?fileName,? byte []?filebytes)? {
          ?
          try {
          ??String?path
          = "" ;
          ???
          if (filebytes != null && filebytes.length > 0 ) {
          ????File?file
          = new ?File( " / " + fileName);
          ????
          ????file.createNewFile();
          ????FileOutputStream?fos
          = new ?FileOutputStream(file);
          ????fos.write(filebytes);
          ????fos.close();
          ????path
          = file.getAbsolutePath();
          ????System.out.println(path);
          ????
          ????file
          = null ;
          ????
          ???}

          ???
          ???
          return ?path;
          ?}
          catch (Exception?ex) {
          ??
          return ? " false " ;
          ?}

          }



          }



          ?


          配置文件

          放在 Classes/META-INF/xfire/service.xml;里面

          <? xml?version="1.0"?encoding="UTF-8" ?>
          < beans? xmlns ="http://xfire.codehaus.org/config/1.0" >
          ?
          < service >
          ??
          < name > MathService </ name >
          ??
          < namespace > newsoft/oa/MathService </ namespace >
          ??
          < serviceClass >
          ???com.newsoft.oa.services.IMathService
          ??
          </ serviceClass >
          ??
          < implementationClass >
          ???com.newsoft.oa.services.MathServiceImpl
          ??
          </ implementationClass >

          ?
          </ service >
          </ beans >


          其實是借鑒了Spring的寫法,用過Spring不會對著陌生,(Application-context.xml)

          WEB-XML加上

          < servlet >
          ??
          < servlet-name > XFireServlet </ servlet-name >
          ??
          < servlet-class >
          ???org.codehaus.xfire.transport.http.XFireConfigurableServlet
          ??
          </ servlet-class >
          ?
          </ servlet >

          ?
          < servlet-mapping >
          ??
          < servlet-name > XFireServlet </ servlet-name >
          ??
          < url-pattern > /servlet/XFireServlet/* </ url-pattern >
          ?
          </ servlet-mapping >

          ?
          < servlet-mapping >
          ??
          < servlet-name > XFireServlet </ servlet-name >
          ??
          < url-pattern > /services/* </ url-pattern >
          ?
          </ servlet-mapping >


          最后就是客戶端了

          /?/Create?a?metadata?of?the?service??????????????
          ?Service?serviceModel?
          =?new?ObjectServiceFactory().create(IMathService.class);?
          //?Create?a?proxy?for?the?deployed?service??????
          ??
          ?XFireProxyFactory?factory?
          =?new?XFireProxyFactory(XFireFactory.newInstance().getXFire());???
          ?String?serviceUrl?
          =?"http://localhost:8080/ws/services/MathService";
          ??client?
          =?null;???????
          ?
          try?{??????????
          ???client?
          =?(IMathService)?factory.create(serviceModel,?serviceUrl);??
          ???File?file
          =new?File("c:\\SUPERMAP?白皮書.pdf");
          ???FileInputStream?fis
          =new?FileInputStream(file);
          ???
          byte[]?b=new?byte[fis.available()];
          ???fis.read(b);
          ??System.out.println(client.sendFile(file.getName(),?b));
          ?}
          ?catch?(Exception?ex)?{?????????
          ???ex.printStackTrace();
          ?}
          ???????????????????????????//Invoke?the?service????
          ?int?serviceResponse?=?0;
          ?
          int?a=10,b=20;

          就是

          Service serviceModel = new ObjectServiceFactory().create(IMathService.class);?
          ?XFireProxyFactory factory = new XFireProxyFactory(XFireFactory.newInstance().getXFire());??
          ?String serviceUrl = http://localhost:8080/ws/services/MathService;

          三行字建立連接請求,

          太輕松了

          傳輸文件速度也可以,二進制,2M多的文件,也能輕松傳遞

          用.net和delphi平臺測試,兼容性沒問題(按道理 soap,也不應該有問題)

          這是為客戶搭建的在 檔案系統和OA審批間作文件歸檔的嘗試項目

          哈,完整的項目代碼,就不方便講了。
          posted @ 2009-02-20 15:07 zhuyongjp 閱讀(726) | 評論 (0)編輯 收藏
           
          我們項目的整個架構使用的比較流行的WSH MVC組合,即webwork2 + Spring + Hibernate;
          1.首先集成Apacha CXF WebService 到 Spring 框架中;
          ?? apache cxf 下載地址:http://people.apache.org/dist/incubator/cxf/2.0.4-incubator/apache-cxf-2.0.4-incubator.zip
          ? 在spring context配置文件中引入以下cxf配置
          ?? Xml代碼
          <import?resource="classpath*:META-INF/cxf/cxf.xml"?/>??
          <import?resource="classpath*:META-INF/cxf/cxf-extension-soap.xml"?/>??
          <import?resource="classpath*:META-INF/cxf/cxf-servlet.xml"?/>??

          在web.xml中添加過濾器:
          ?? Xml代碼

          2.開發服務端WebService接口:
          ??? Java代碼
          1. /** ?
          2. ?*?WebService接口定義類. ?
          3. ?*? ?
          4. ?*?使用@WebService將接口中的所有方法輸出為Web?Service. ?
          5. ?*?可用annotation對設置方法、參數和返回值在WSDL中的定義. ?
          6. ?*/??
          7. @WebService??
          8. public?interface?WebServiceSample?{ ??
          9. ??
          10. ??
          11. ????/** ?
          12. ?????*?一個簡單的方法,返回一個字符串 ?
          13. ?????*?@param?hello ?
          14. ?????*?@return ?
          15. ?????*/??
          16. ????String?say(String?hello); ??
          17. ???? ??
          18. ????/** ?
          19. ?????*?稍微復雜一些的方法,傳遞一個對象給服務端處理 ?
          20. ?????*?@param?user ?
          21. ?????*?@return ?
          22. ?????*/??
          23. ????String?sayUserName( ??
          24. ????????????@WebParam(name?=?"user")? ??
          25. ????????????UserDTO?user); ??
          26. ???? ??
          27. ????/** ?
          28. ?????*?最復雜的方法,返回一個List封裝的對象集合 ?
          29. ?????*?@return ?
          30. ?????*/??
          31. ????public? ??
          32. ????@WebResult(partName="o") ??
          33. ????ListObject?findUsers(); ??
          34. ??
          35. }??

          ?? 由簡單到復雜定義了三個接口,模擬業務需求;

          3.實現接口

          ?? Java代碼
          1. /** ?
          2. ?*?WebService實現類. ?
          3. ?*? ?
          4. ?*?使用@WebService指向Interface定義類即可. ?
          5. ?*/??
          6. @WebService(endpointInterface?=?"cn.org.coral.biz.examples.webservice.WebServiceSample") ??
          7. public?class?WebServiceSampleImpl?implements?WebServiceSample?{ ??
          8. ??
          9. ????public?String?sayUserName(UserDTO?user)?{ ??
          10. ????????return?"hello?"+user.getName(); ??
          11. ????} ??
          12. ??
          13. ????public?String?say(String?hello)?{ ??
          14. ????????return?"hello?"+hello; ??
          15. ????} ??
          16. ??
          17. ????public?ListObject?findUsers()?{ ??
          18. ????????ArrayList<Object>?list?=?new?ArrayList<Object>(); ??
          19. ???????? ??
          20. ????????list.add(instancUser(1,"lib")); ??
          21. ????????list.add(instancUser(2,"mld")); ??
          22. ????????list.add(instancUser(3,"lq")); ??
          23. ????????list.add(instancUser(4,"gj")); ??
          24. ????????ListObject?o?=?new?ListObject(); ??
          25. ????????o.setList(list); ??
          26. ????????return?o; ??
          27. ????} ??
          28. ???? ??
          29. ????private?UserDTO?instancUser(Integer?id,String?name){ ??
          30. ????????UserDTO?user?=?new?UserDTO(); ??
          31. ????????user.setId(id); ??
          32. ????????user.setName(name); ??
          33. ????????return?user; ??
          34. ????} ??
          35. }??

          ?4.依賴的兩個類:用戶對象與List對象
          ???? Java代碼

          1. /** ?
          2. ?*?Web?Service傳輸User信息的DTO. ?
          3. ?*? ?
          4. ?*?分離entity類與web?service接口間的耦合,隔絕entity類的修改對接口的影響. ?
          5. ?*?使用JAXB?2.0的annotation標注JAVA-XML映射,盡量使用默認約定. ?
          6. ?*? ?
          7. ?*/??
          8. @XmlAccessorType(XmlAccessType.FIELD) ??
          9. @XmlType(name?=?"User") ??
          10. public?class?UserDTO?{ ??
          11. ??
          12. ????protected?Integer?id; ??
          13. ??
          14. ????protected?String?name; ??
          15. ??
          16. ????public?Integer?getId()?{ ??
          17. ????????return?id; ??
          18. ????} ??
          19. ??
          20. ????public?void?setId(Integer?value)?{ ??
          21. ????????id?=?value; ??
          22. ????} ??
          23. ??
          24. ????public?String?getName()?{ ??
          25. ????????return?name; ??
          26. ????} ??
          27. ??
          28. ????public?void?setName(String?value)?{ ??
          29. ????????name?=?value; ??
          30. ????} ??
          31. }??

          ?? 關于List對象,參照了有關JWS的一個問題中的描述:DK6.0 自帶的WebService中 WebMethod的參數好像不能是ArrayList 或者其他List
          傳遞List需要將List 包裝在其他對象內部才行 (個人理解 如有不對請指出) ,我在實踐中也遇到了此類問題.通過以下封裝的對象即可以傳遞List對象.

          Java代碼
          1. /** ?
          2. ?*?<p>Java?class?for?listObject?complex?type. ?
          3. ?*? ?
          4. ?*?<p>The?following?schema?fragment?specifies?the?expected?content?contained?within?this?class. ?
          5. ?*? ?
          6. ?*?<pre> ?
          7. ?*?<complexType?name="listObject"> ?
          8. ?*???<complexContent> ?
          9. ?*?????<restriction?base="{http://www.w3.org/2001/XMLSchema}anyType"> ?
          10. ?*???????<sequence> ?
          11. ?*?????????<element?name="list"?type="{http://www.w3.org/2001/XMLSchema}anyType"?maxOccurs="unbounded"?minOccurs="0"/> ?
          12. ?*???????</sequence> ?
          13. ?*?????</restriction> ?
          14. ?*???</complexContent> ?
          15. ?*?</complexType> ?
          16. ?*?</pre> ?
          17. ?*? ?
          18. ?*? ?
          19. ?*/??
          20. @XmlAccessorType(XmlAccessType.FIELD) ??
          21. @XmlType(name?=?"listObject",?propOrder?=?{?"list"?}) ??
          22. public?class?ListObject?{ ??
          23. ??
          24. ????@XmlElement(nillable?=?true) ??
          25. ????protected?List<Object>?list; ??
          26. ??
          27. ????/** ?
          28. ?????*?Gets?the?value?of?the?list?property. ?
          29. ?????*? ?
          30. ?????*?<p> ?
          31. ?????*?This?accessor?method?returns?a?reference?to?the?live?list, ?
          32. ?????*?not?a?snapshot.?Therefore?any?modification?you?make?to?the ?
          33. ?????*?returned?list?will?be?present?inside?the?JAXB?object. ?
          34. ?????*?This?is?why?there?is?not?a?<CODE>set</CODE>?method?for?the?list?property. ?
          35. ?????*? ?
          36. ?????*?<p> ?
          37. ?????*?For?example,?to?add?a?new?item,?do?as?follows: ?
          38. ?????*?<pre> ?
          39. ?????*????getList().add(newItem); ?
          40. ?????*?</pre> ?
          41. ?????*? ?
          42. ?????*? ?
          43. ?????*?<p> ?
          44. ?????*?Objects?of?the?following?type(s)?are?allowed?in?the?list ?
          45. ?????*?{@link?Object?} ?
          46. ?????*? ?
          47. ?????*? ?
          48. ?????*/??
          49. ????public?List<Object>?getList()?{ ??
          50. ????????if?(list?==?null)?{ ??
          51. ????????????list?=?new?ArrayList<Object>(); ??
          52. ????????} ??
          53. ????????return?this.list; ??
          54. ????} ??
          55. ??
          56. ????public?void?setList(ArrayList<Object>?list)?{ ??
          57. ????????this.list?=?list; ??
          58. ????} ??
          59. ??
          60. }??

          5.WebService 服務端 spring 配置文件 ws-context.xml
          Xml代碼
          1. <beans?xmlns="http://www.springframework.org/schema/beans"??
          2. ????xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"??
          3. ????xmlns:jaxws="http://cxf.apache.org/jaxws"??
          4. ????xsi:schemaLocation="http://cxf.apache.org/jaxws?http://cxf.apache.org/schemas/jaxws.xsd?http://www.springframework.org/schema/beans??http://www.springframework.org/schema/beans/spring-beans.xsd"??
          5. ????default-autowire="byName"?default-lazy-init="true">??
          6. ???? ??
          7. ????<jaxws:endpoint?id="webServiceSample"??
          8. ????????address="/WebServiceSample"?implementor="cn.org.coral.biz.examples.webservice.WebServiceSampleImpl"/>??
          9. ??
          10. </beans>??

          WebService 客戶端 spring 配置文件 wsclient-context.xml

          Xml代碼
          1. <beans?xmlns="http://www.springframework.org/schema/beans"??
          2. ????xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"??
          3. ????xmlns:jaxws="http://cxf.apache.org/jaxws"??
          4. ????xsi:schemaLocation="http://cxf.apache.org/jaxws?http://cxf.apache.org/schemas/jaxws.xsd?http://www.springframework.org/schema/beans??http://www.springframework.org/schema/beans/spring-beans.xsd"??
          5. ????default-autowire="byName"?default-lazy-init="true">??
          6. ??
          7. ????<!--?ws?client?-->??
          8. ????<bean?id="identityValidateServiceClient"?class="cn.org.coral.admin.service.IdentityValidateService"??
          9. ????????factory-bean="identityValidateServiceClientFactory"?factory-method="create"?/>??
          10. ??
          11. ????<bean?id="identityValidateServiceClientFactory"??
          12. ????????class="org.apache.cxf.jaxws.JaxWsProxyFactoryBean">??
          13. ????????<property?name="serviceClass"??
          14. ????????????value="cn.org.coral.admin.service.IdentityValidateService"?/>??
          15. ????????<property?name="address"??
          16. ????????????value="http://88.148.29.54:8080/coral/services/IdentityValidateService"/>??
          17. ????</bean>??
          18. ???? ??
          19. </beans>??
          6.發布到tomcat服務器以后通過以下地址即可查看自定義的webservice接口生成的wsdl:
          http://88.148.29.54:8080/aio/services/WebServiceSample?wsdl

          7.調用WebService接口的Junit單元測試程序
          Java代碼
          1. package?test.coral.sample; ??
          2. ??
          3. import?org.springframework.test.AbstractDependencyInjectionSpringContextTests; ??
          4. ??
          5. import?cn.org.coral.biz.examples.webservice.WebServiceSample; ??
          6. import?cn.org.coral.biz.examples.webservice.dto.UserDTO; ??
          7. ??
          8. public?class?TestWebServiceSample?extends??
          9. ????????AbstractDependencyInjectionSpringContextTests?{ ??
          10. ????WebServiceSample?webServiceSampleClient; ??
          11. ??
          12. ????public?void?setWebServiceSampleClient(WebServiceSample?webServiceSampleClient)?{ ??
          13. ????????this.webServiceSampleClient?=?webServiceSampleClient; ??
          14. ????} ??
          15. ???? ??
          16. ????@Override??
          17. ????protected?String[]?getConfigLocations()?{ ??
          18. ????????setAutowireMode(AUTOWIRE_BY_NAME); ??
          19. ??????????????????//spring?客戶端配置文件保存位置 ??
          20. ????????return?new?String[]?{?"classpath:/cn/org/coral/biz/examples/webservice/wsclient-context.xml"?}; ??
          21. ????} ??
          22. ???? ??
          23. ????public?void?testWSClinet(){ ??
          24. ????????Assert.hasText(webServiceSampleClient.say("?world")); ??
          25. ????} ??
          26. }??
          posted @ 2009-02-20 13:41 zhuyongjp 閱讀(1450) | 評論 (0)編輯 收藏
           
               摘要: 本文是一個由四篇文章組成的系列的第三篇(請參閱 參考資料),本系列文章旨在介紹創建、描述和發布 Web 服務的過程。在第一部分中,我借助 WSDL 編制示例講解了如何描述一個 Web 服務。在第二部分中,我討論了 SOAP 的體系結構及其語義。在本文中,我將看一看與 SOAP 相關的互操作性問題。 Web 服務模型將整個 B2B 領域分成三個步驟或三個域:描述一個服務、將該服務與具體的實現綁定,然...  閱讀全文
          posted @ 2009-02-19 10:44 zhuyongjp 閱讀(423) | 評論 (0)編輯 收藏
           
               摘要: SOAP 和 WSDL 我在本系列文章的 第 1 部分介紹了 WSDL。WSDL 描述了 Web 服務的接口。Web 服務所有者將用 SOAP 來實現他們的接口。因此, WSDL 服務實際上作為 SOAP 服務一樣存在。一旦 Web 服務用戶擁有 WSDL 文件,他或者她就知曉接口的細節。他或者她就會用 SOAP 來與 Web 服務通信。 可以把 Web 服務考慮為對象,可以通過 WSDL 接口公...  閱讀全文
          posted @ 2009-02-19 10:24 zhuyongjp 閱讀(1459) | 評論 (0)編輯 收藏
           
          可互操作的基于 Web 分布式應用程序的思想并非新近出現。僅舉一例,電子數據交換(EDI)市場需求早在 B2B 在線電子商務獲得任何有意義的實現之前就存在了 ― 并且隨著 B2B 電子市場的普及,互操作性已經成為最迫切的 EDI 需求。

          以任何在線電子市場為例。存在著許多企業,各自提供特有的“服務( services )”(讓我們稱之為“Web 服務( Web services )”)。在當今的電子商務中,尚不存在一種機制,使一個業務能自動發現其預期伙伴提供的服務。所謂的 下一代 .com還是提供這種自動的發現機制。

          什么是 WSDL?

          這種新的 .com 需要一種解決方案來描述它所提供的服務(Web 服務)。具體而言,這意味著您需要一種格式或某種類型的語法,使您可以通過使用它們來描述下列問題的答案:

          您的在線業務提供什么服務?
          您如何調用業務服務?
          當用戶調用您的業務服務時,該業務服務需要他/她提供什么信息?
          用戶將如何提供這些必需信息?
          服務將以什么格式發送返回給用戶的信息?
          很幸運,WSDL 提供了完成所有這些作業的機制。



          WSDL 和 SOAP

          為更好理解 WSDL 是如何工作的,我將首先描述 SOAP 和 HTTP 是如何使用 WSDL 工作的。WSDL 的用途是“描述”您的 Web 服務。業務之間將通過交換 WSDL 文件來理解對方的服務。一旦知道您伙伴的服務并希望調用它們,SOAP 就派上用場了??梢詫⒎湛醋魇峭ㄟ^ SOAP 訪問的對象。

          最有可能的情況是,您將通過因特網或電子郵件與潛在伙伴通信。當然,因特網使用 HTTP 而電子郵件以 SMTP 方式工作,這使得 HTTP 和 SMTP 成為作為 SOAP 的“傳輸服務提供者”的有利候選人。



          WSDL 編寫

          現在,我將講述為 Web 服務編寫 WSDL 的過程。目的是公開現有的 Web 服務。您所處的情況也許就是下列情況之一:

          您有一個現存的服務(例如,一個網站),并希望表示它的功能性。
          您有一個 WSDL,并且希望依照已經決定表示的功能性來實現 Web服務器端的邏輯。(有些人也許會認為這是一個不可能的方案,但是 UDDI 的指紋概念使它變得極為可能;我將在本系列的第四部分討論 UDDI)。
          您正在從零開始,并且既無網站又無 WSDL 界面。
          本文中所涵蓋的信息適用于這些可能性中的任意一種或全部。

          WSDL 編寫的四個步驟

          我將把 WSDL 編寫分成四個簡單步驟。遵循每個步驟,您的 Web 服務將準備就緒用于部署。

          步驟 1:服務接口

          您將構建一個移動電話銷售公司的服務接口作為樣本項目(我將這個服務稱為 MobilePhoneService )。該公司銷售不同型號的移動電話,所以公司 Web 服務的后端數據存儲庫中將包含一個具有兩列( model number 和 price )的表格。(為了將焦點保持在 WSDL 本身,我保持該表格的簡單性)。有兩個關于要使用 WSDL 表示的服務的方法:

          getListOfModels ()
          getPrice (modelNumber)
          GetListOfModels 方法提供了一個字符串數組,其中每個字符串表示一種移動電話的型號。 GetPrice 獲得型號,然后返回它的價格。WSDL 將這些方法作為操作調用?,F在將開始構建“WSDL 接口文件( WSDL interface file )”。

          每個 WSDL 文件的根元素都是 <definitions> ,必須在其中提供服務的完整描述。首先,必須在 <definitions> 元素中提供各種名稱空間的聲明。三個必須做的外部名稱空間聲明是 WSDL、SOAP 和 XSD(XML 模式定義)。還有一個名稱空間 ― TNS,它指您的 MobilePhoneService(這表示 TNS(targetNamespace 的縮寫)包含專為 MobilePhoneService 定義的所有元素和屬性的名稱)。但是 WSDL 是您將在 WSDL 編寫中使用得最多的主要名稱空間。在本系列文章中使用到其它名稱空間時,我將提到它們的效用。

          關于名稱空間只要注意一點:WSDL 廣泛地使用名稱空間這一概念。我鼓勵您到 W3C 的官方網站去學習關于名稱空間的更多知識(請參閱 參考資料)。WSDL 是這種思想的一種實現,因為名稱空間提供了無限的靈活性,而這恰恰是用于電子數據交換的可移植格式所需要的。

          <definitions> 元素包含一個或多個 <portType> 元素,實際上,每個元素都是您希望表示的一系列 operation 。或者,您也可以將單個 portType 元素看作是將各種方法組成類的一個邏輯分組。例如,如果您的供應鏈管理解決方案需要在客戶和供應商之間進行交互,您最可能做的是分別定義與他們交互的功能性;也就是說,您將為用戶和供應商各定義一個 portType。應該將每個 portType 稱為 服務,因此整個 WSDL 文件將成為一個服務集合。

          必須為每個服務提供一個名稱。在本例中,僅有一個服務(因此只有一個 <portType> )。 需要使用該 portType 元素的 name 屬性為移動電話銷售服務指定名稱。

          在每個服務內可以有幾個方法、或者 operation ,WSDL 通過 <operation> 元素來引用它們。樣本應用程序有兩個要表示的方法: getListOfModels 和 getPrice 。因此,您需要提供兩個 <operation> 元素,每個元素有一個 name 。 我已經使用 <operation> 元素的 name 屬性命名了每個操作。

          此時,WSDL 文件看上去象 清單 1。


          清單 1:定義操作

           1<?xml version="1.0" encoding="UTF-8" ?> 
           2<definitions  name="MobilePhoneService" 
           3     targetNamespace="www.mobilephoneservice.com/MobilePhoneService-interface" 
           4     xmlns="http://schemas.xmlsoap.org/wsdl/" 
           5     xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/" 
           6     xmlns:tns="http://www.mobilephoneservice.com/MobilePhoneService" 
           7     xmlns:xsd="http://www.w3.org/1999/XMLSchema"> 
           8     <portType name="MobilePhoneService_port"> 
           9             <operation name="getListOfModels "> 
          10                     
          11                     
          12             </operation> 
          13             <operation name="getPrice"> 
          14                     
          15                         
          16             </operation> 
          17     </portType> 
          18</definitions> 
          19


          步驟 2:指定參數
          Listing 2: Defining parameters 
                  
                 
           1<?xml version="1.0" encoding="UTF-8" ?> 
           2<definitions  name="MobilePhoneService" 
           3targetNamespace="http://www.mobilephoneservice.com/MobilePhoneService-interface" 
           4xmlns="http://schemas.xmlsoap.org/wsdl/" 
           5xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/" 
           6xmlns:tns="http://www.mobilephoneservice.com/MobilePhoneService" 
           7xmlns:xsd="http://www.w3.org/1999/XMLSchema"> 
           8<types> 
           9<xsd:schema targetNamespace="http://www.mobilephoneservice.com/MobilePhoneService" 
          10xmlns="http://www.w3.org/1999/XMLSchema/"> 
          11<xsd:complexType name="Vector"> 
          12<xsd:element name="elementData" type="xsd:String" /> 
          13<xsd:element name="elementCount" type="xsd:int" /> 
          14</xsd:complexType> 
          15</xsd:schema> 
          16</types> 
          17<message name="ListOfPhoneModels"> 
          18<part name="models" type="tns:Vector"> 
          19</message> 
          20<message name="PhoneModel"> 
          21<part name="model" type="xsd:String"> 
          22</message> 
          23<message name="PhoneModelPrice"> 
          24<part name="price" type="xsd:String"> 
          25</message> 
          26<portType name="MobilePhoneService_port"> 
          27<operation name="getListOfModels "> 
          28<output message="ListOfPhoneModels"/> 
          29</operation> 
          30<operation name="getPrice"> 
          31<Input message="PhoneModel"/> 
          32<output message="PhoneModelPrice"/> 
          33</operation> 
          34</portType> 
          35</definitions> 
          36      


          定義好操作(或方法)以后,現在需要指定將向它們發送和從它們返回的參數。在 WSDL 術語中,所有參數稱為“消息”。認為您是在遞送消息而結果得到返回的消息是有用的。方法調用是這樣一種操作:它準備返回“消息”來響應進入的消息。

          請回憶,在第一步驟中有兩個操作要表示。第一個操作 getListOfModels 不必獲得任何參數并且返回一個字符串數組,其中每個字符串表示移動電話的型號。因此,必須定義一個包含字符串數組的 <message> 元素。

          看看 清單 2 中的各種 <message> 元素。其中的第一個元素有一個等于 ListOfPhoneModels 的名稱屬性(該消息的邏輯名稱),以及名稱為 models 的單個 <part> 元素,這意味著該 ListOfPhoneModels 是一個“只含有一個 part 的”消息,其中僅有的這個 part 的名稱是“models”。消息可以有任意多個 part ― 只要為它們起不同的名稱,以唯一標識。

          我已包括了 <part> 元素的另一個屬性,它就是 type 。將這個“type”屬性當作 C++ 或 Java 中的數據類型。我已經將 models 的數據類型指定為 tns:Vector。(請回憶,我在 <definitions> 根元素中指定了一些名稱空間,其中之一是 tns 。)這個類型即指 MobilePhoneService 名稱空間。這意味著當編寫 WSDL 時,您可以創建自己的名稱空間。現在您也許會問兩個邏輯問題:為什么?和怎么做?

          要回答 為什么,讓我們以 getListOfModels 操作返回的字符串數組為例。WSDL 使用 XML 模式定義(XSD)定義的一些原始數據類型(諸如 int、float、long、short、byte、string、Boolean 等等),并允許您直接使用它們,或者以這些原始數據類型構建復雜數據類型后,在消息中使用它們。這就是為什么當引用復雜數據類型時,您需要定義自己的名稱空間。在本例中,需要為 array of strings 構建一個復雜數據類型。

          現在來看 怎么做問題,您將使用 XSD 創建自己的名稱空間。為實現這個目的,我在 <types> 元素中使用了 xsd:complexType 元素用來定義稱為 Vector 的數據類型。 Vector 使用兩個原始數據類型:string(元素數據)和 Integer(元素計數)。因此, Vector 成為名稱空間的一部分并可以通過別名 tns 來引用。

          在 清單 2 中,我以類似的方式定義了另外兩個消息 PhoneModel 和 PhoneModelPrice 。這兩個消息只使用了 xsd 名稱空間中的原始數據類型 string,因此您不必為使用它們而定義任何更復雜的數據類型。

          您也許已經注意到當創建 <message> 元素時,沒有指定這些消息是進入參數還是返回值。這是一個您將在 <portType> 元素內的 <operation> 元素中完成的工作。因此,正如您在 清單 2 中所看到的,我已經將 <input> 和 <output> 元素都添加到這兩個操作中。每個 input 元素通過消息名來引用它并將它當作用戶調用該操作時要提供的參數。類似地,每個 <output> 元素引用一個消息;它將該消息當作操作調用的返回值。

          至今, 清單 2準確地限定了目前的討論的范圍。

          步驟 3:消息傳遞和傳輸

          我以一種抽象方式定義了操作和消息,而不考慮實現的細節。實際上,WSDL 的任務是定義或描述 Web 服務,然后提供一個對外部框架的引用來定義 WSDL 用戶將如何實現這些服務??梢詫⑦@個框架當作 WSDL 抽象定義和它們的實現之間的“綁定( binding )”。

          當前,最流行的綁定( binding )技術是使用簡單對象訪問協議(SOAP)。WSDL 將指定能夠訪問 Web 服務實際實現的 SOAP 服務器,并且從那時起 SOAP 的整個任務就是將用戶從 WSDL 文件帶到它的實現。SOAP 是本系列文章中下一部分的主題,所以我將暫時避免討論 SOAP 細節而繼續集中講述 WSDL 編寫。

          WSDL 編寫的第三個步驟是描述將 SOAP 與 WSDL 文件綁定到一起的過程。您將把 <binding> 元素包括到 <definitions> 元素內。這個 binding 元素應該有 name 和 type 屬性。 name 將標識這個綁定而 type 將標識您希望與這個綁定相關聯的 portType(一組操作)。在 清單 3 中,您會發現 <portType> 元素的 name 與 <binding> 元素的 type 屬性值相匹配。

          WSDL binding 元素包含您將用于綁定用途的外部技術的聲明。因為正在使用 SOAP,所以這里將使用 SOAP 的名稱空間。WSDL 術語中,對外部名稱空間的使用稱為 extensibility 元素。

          在 清單 3 中,您將看見一個空的 <soap:binding/> 元素。該元素的用途是聲明將把 SOAP 作為綁定和傳輸服務使用。

          <soap:binding> 元素有兩個屬性:style 和 transport。style 是一個可選屬性,它描述該綁定內操作的性質。transport 屬性指定 HTTP 作為該綁定將使用的級別較低的傳輸服務。

          SOAP 客戶機將從 WSDL 文件中讀取 SOAP 結構并與另一端的 SOAP 服務器協調,所以必須特別關注 interoperability 。我打算在本系列文章的第三部分詳細講述該問題。

          在空的 <soap:binding/> 元素后面,有兩個 WSDL <operation> 元素,分別表示步驟 1 的操作。每個 <operation> 元素提供各自操作的綁定細節。因此,我提供了另一個 extensibility 元素,即 <soap:operation/> (仍然是一個空元素,與它發生的那個操作相關)。該 <soap:operation/> 元素有一個 soapAction 屬性,SOAP 客戶機將使用該屬性創建 SOAP 請求。

          請回憶步驟 2 中, getListOfModels 操作只有輸出而無任何輸入。因此,必須為該操作提供一個 <output> 元素。該輸出包含 <soap:body/> 元素(仍然是一個空元素,與它發生的那個操作相關)。SOAP 客戶機需要該信息來創建 SOAP 請求。 <soap:body/> 的名稱空間屬性值應該與您將部署到 SOAP 服務器上的 service 的名稱相對應,SOAP 服務器將在在本系列文章的下一部分中講述。

          您已幾乎要完成步驟 3 了。只要將下一個操作復制到這個操作的后面,您將完成 清單 3。

          步驟 4:概括

          您已經生成了一個完整描述服務 interface 的 WSDL 文件?,F在,WSDL 需要一個附加步驟來創建該 WSDL 文件的概要。WSDL 將該文件稱為 implementation 文件,在本系列文章的第四部分中,當您在 UDDI 注冊中心發布 Web 服務時,會使用它。請看 清單 4― 這個 WSDL 實現文件。它的主要特性如下:

          除了
          清單 4(實現文件)引用不同的 targetNamespace 去引用實現文件以外, <definitions> 根元素和 清單 3(WSDL 接口文件)中的完全相同。
          有一個
          <import> 元素,該元素引用 清單 3的接口文件(文件名 MobilePhoneService-interface.wsdl)和它的名稱空間。
          有一個
          <service> 標記,其中有一個表示該服務的邏輯名 name 。在 service 元素內有一個引用在 清單 3中創建的 SOAP 綁定的 port 元素。

          將 IBM 的 Web Services ToolKit(WSTK)用于 WSDL 編寫

          現在,Web 服務已經完全就緒用于部署。我已經展示了如何手工創建這些文件(使用象 emacs 這樣的簡單文本編輯器)。可以使用諸如 IBM 的 WSTK(請參閱 參考資料以獲得該工具箱以及本文提到的其它參考資料的鏈接)之類的 Web 服務編寫工具來生成相同的這些文件。

          WSTK 可以使用向導幫助過程來生成這些文件。用戶可以生成與我在以上教程中演示的同樣兩種方法的 WSDL 文件,并將 WSTK 文件和 清單 3和 4中的 WSDL 文件作比較。

          您將注意到下列差異:

          WSTK
          依照邏輯規則創建了所有名稱屬性;在本示例中,我使用了自己視為方便的名稱。
          WSTK 為每個操作至少生成一個 input
          標記,即使該操作不必獲得任何輸入。 listAllPhoneModels 操作沒有任何 input 元素,但是如果使用 WSTK 生成相同文件,它將因為包含這個方法的一個空 input 元素。
          WSTK 產生了除已生成的兩個文件以外的第三個文件。這第三個文件是
          SOAP 引擎用于服務部署的 SOAP 部署描述符。我將在本系列文章中討論服務部署。
          在這部分中,我演示了手工進行 WSDL 編寫以創建接口和實現文件,并與 IBM 的 Web Services ToolKit 生成的文件作了比較。在本系列的下一部分中,我將討論在 SOAP 服務器上部署這個 WSDL 服務。
          posted @ 2009-02-19 09:50 zhuyongjp 閱讀(439) | 評論 (0)編輯 收藏
          僅列出標題
          共3頁: 上一頁 1 2 3 
           
          主站蜘蛛池模板: 定安县| 敦化市| 道孚县| 江阴市| 峡江县| 仲巴县| 泗水县| 忻城县| 越西县| 南宁市| 华坪县| 布拖县| 泾阳县| 宜春市| 沁水县| 延安市| 田林县| 曲阳县| 贞丰县| 上杭县| 高碑店市| 江油市| 涞水县| 荔波县| 黎城县| 道孚县| 青海省| 信丰县| 中江县| 巴林右旗| 淄博市| 韶关市| 柞水县| 华宁县| 雷波县| 顺昌县| 温州市| 牡丹江市| 大关县| 栾川县| 木里|