使用jdk方式發佈web service服務
利用jdk工具包中爲我們開發者提供的工具,來創建Web Service服務端,同時,使用jdk工具包中的工具來實現訪問Web Service的客戶端。
服務端
步驟:
- 創建一個服務接口 - interface
- 創建服務實現類
- 發佈服務
客戶端
步驟:
- 生成客戶端代碼
wsdl2java -d e:/jdk-client http://localhost:8080/hello?wsdl - 調用遠程服務
- 服務器端的代碼實現
- 創建服務接口
package com.golden3young.service;
import javax.jws.WebService;
// 在服務接口和實現類上都要加上@WebService註解
// serviceName對應到 WSDL文件中的<wsdl:service name="HelloServiceWS">
// portName對應到WSDL文件中的<wsdl:port name="HelloServiceWSSoap">
@WebService(serviceName="HelloServiceWS", portName="HelloServiceWSSoap")
public interface HelloService {
//服務方法
String sayHello(String name);
}
- 創建服務實現類
package com.golden3young.service;
import javax.jws.WebService;
@WebService(serviceName="HelloServiceWS", portName="HelloServiceWSSoap")
public class HelloServiceImpl implements HelloService {
@Override
public String sayHello(String name) {
System.out.println("sayHello invoke!");
return "Hello " + name;
}
}
- 發佈服務
package com.golden3young;
import javax.xml.ws.Endpoint;
import com.golden3young.service.HelloServiceImpl;
public class JdkWSServer {
public static void main(String[] args) {
Endpoint.publish("http://localhost:8080/hello?wsdl", new HelloServiceImpl());
}
}
運行此方法即可發佈服務,我們只需在瀏覽器中輸入規定的路徑,即可訪問到響應的xml文件。xml文件中的具體標籤含義,已在本系列(一)的文章中做過介紹。
- 客戶器端的代碼實現
- 生成客戶端代碼
我們藉助的是apache-cxf的第三方工具,這裏面爲我們提供了下載服務器端Web Service的工具,我們只需要在安裝後,bin路徑下,打開cmd命令行面板。輸入“ wsdl2java -d e:/jdk-client http://localhost:8080/hello?wsdl ”命令,即可自動爲我們將指定路徑中的項目代碼進行下載。
wsdl2java -d e:/jdk-client http://localhost:8080/hello?wsdl
解釋一下:
wsdl2java 是一個命令
-d 表示解壓到指定目錄下
e:/jdk-client 是我們指定的文件輸出路徑,第三方工具回將服務器端的代碼下載到我們指定的目錄下,如果文件夾不存在,也會自動創建。
http://localhost:8080/hello?wsdl 這是我們訪問的Web Service服務器端的路徑。只有通過正確的路徑,才能訪問到我們想要的資源代碼。
注意,這個下載過程需要聯網,沒有網絡支持是無法完成的。下載完畢後,將目錄下的代碼文件全部拷貝到我們的工程中,以便我們使用。
其實,下載的所有代碼,其實就是服務器端所部署的代碼,但是,所有獲取數據的代碼都被底層加工成了發送請求獲取的方式,也就是說,我們下載在客戶端的代碼,基本與服務器端的代碼相同,但是所有獲取返回數據的代碼,都會被修改成發送請求到指定路徑的方式來獲取。也就是說,我們客戶端的代碼,在運行起來以後,其實底層就是繼續發送請求到事先指定的路徑拿取數據,然後將數據進行處理,處理所需要的類方法或者類屬性都已經下載在客戶端了。當我們使用的時候,無需關注底層的代碼實現,我們只需要通過調用服務對象即可拿到我們想要的值,中間的所有處理,都是剛纔下載到本地的代碼去執行的。
- 調用遠程服務
import com.golden3young.service.HelloServiceImpl;
import com.golden3young.service.HelloServiceWS;
public class JdkWSClient {
public static void main(String[] args) {
// <wsdl:service name="HelloServiceWS">
HelloServiceWS ws = new HelloServiceWS();
//通過service服務名字獲取port
// 這就相當於是xml文件中的那個<wsdl:port name="HelloServiceWSSoap">標籤
// getHelloServiceWSSoap(); => 會返回一個遠程代理對象
HelloServiceImpl helloServiceWSSoap = ws.getHelloServiceWSSoap();
//簡單打印一下,看看這個對象是個啥
System.out.println(helloServiceWSSoap);
//調用這個對象的sayHello()方法,獲取結果並打印
String result = helloServiceWSSoap.sayHello("Web Service");
System.out.println(result);
}
}
使用CXF方式發佈web service服務
官方地址:cxf.apache.org
簡單介紹:
Apache CXF = Celtix + XFire,開始叫 Apache CeltiXfire,後來更名爲 Apache CXF 了,
以下簡稱爲 CXF。CXF 繼承了 Celtix 和 XFire 兩大開源項目的精華,提供了對 JAX-WS
全面的支持,並且提供了多種 Binding 、DataBinding、Transport 以及各種 Format 的支
持,並且可以根據實際項目的需要,採用代碼優先(Code First)或者 WSDL 優先(WSDL
First)來輕鬆地實現 Web Services 的發佈和使用。Apache CXF已經是一個正式的
Apache頂級項目。
支持 Web Services 標準:CXF 支持多種 Web Services 標準,包含 SOAP、Basic
Profile、WS-Addressing、WS-Policy、WS-ReliableMessaging 和 WS-Security。
Frontends:CXF 支持多種“Frontend”編程模型,CXF 實現了JAX-WS API (遵循 JAX-WS
2.0 TCK 版本),它也包含一個“simple frontend”允許客戶端和 EndPoint 的創建,而不需
要 Annotation 註解。CXF 既支持 WSDL優先開發,也支持從 Java 的代碼優先開發模式。
容易使用: CXF 設計得更加直觀與容易使用。有大量簡單的 API 用來快速地構建代碼優先
的 Services,各種 Maven 的插件也使集成更加容易,支持 JAX-WS API ,支持 Spring 2.0
更加簡化的 XML 配置方式,等等。支持二進制和遺留協議:CXF 的設計是一種可插撥的
架構,既可以支持 XML ,也可以支持非 XML 的類型綁定,比如:JSON 和 CORBA。
引入maven依賴
<dependency>
<groupId>org.apache.cxf</groupId>
<artifactId>cxf-rt-frontend-jaxws</artifactId>
<version>3.1.11</version>
</dependency>
<dependency>
<groupId>org.apache.cxf</groupId>
<artifactId>cxf-rt-transports-http</artifactId>
<version>3.1.11</version>
</dependency>
<!-- Jetty is needed if you're are not using the CXFServlet -->
<dependency>
<groupId>org.apache.cxf</groupId>
<artifactId>cxf-rt-transports-http-jetty</artifactId>
<version>3.1.11</version>
</dependency>
服務端
步驟:
- 開發服務接口
- 開發服務實現
- 暴露服務
3-1 創建服務工廠
3-2 設置工廠地址
3-3 設置要發佈的服務接口
3-4 設置服務實現
3-5 創建服務
3-6 啓動服務
客戶端
步驟:
- 生成客戶端代碼
wsdl2java -d e:/jdk-client http://localhost:9090/user?wsdl - 調用遠程服務
2-1 創建代理工場
2-2 設置服務地址
2-3 設置服務接口
2-4 創建遠程服務代理對象
2-5 調用遠程服務
- 服務器端的代碼實現
1.開發服務接口
package com.golden3young.server;
import javax.jws.WebService;
import com.etoak.bean.User;
@WebService //要記得添加這個註解!以此來表示自己是WebService
public interface UserService {
User queryUserById(int id);
}
2.開發服務實現
package com.golden3young.server.impl;
import javax.jws.WebService;
import com.golden3young.bean.User;
import com.golden3young.server.UserService;
@WebService //必須添加這個註解!以此來表示自己是WebService
public class UserServiceImpl implements UserService {
@Override
public User queryUserById(int id) {
System.out.println("queryById invoke");
return new User(id,"Tom",19);
}
}
- 暴露服務
3-1 創建服務工廠
3-2 設置工廠地址
3-3 設置要發佈的服務接口
3-4 設置服務實現
3-5 創建服務
3-6 啓動服務
package com.golden3young;
import org.apache.cxf.endpoint.Server;
import org.apache.cxf.jaxws.JaxWsServerFactoryBean;
import com.golden3young.server.UserService;
import com.golden3young.server.impl.UserServiceImpl;
public class CxfServer {
public static void main(String[] args) {
//創建服務工廠
JaxWsServerFactoryBean factory = new JaxWsServerFactoryBean();
//設置Wsdl服務地址 -> 也就是客戶只要訪問這個url,就可以拿到我們這個項目對外公開的代碼
factory.setAddress("http://localhost:9090/user?wsdl");
//暴露服務接口
factory.setServiceClass(UserService.class);
//設置服務實現類
factory.setServiceBean(new UserServiceImpl());
//創建服務,並啓動
Server server = factory.create();
server.start();
System.out.println("~~~~服務已啓動~~~~");
}
}
- 客戶器端的代碼實現
- 生成客戶端代碼
wsdl2java -d e:/jdk-client http://localhost:9090/user?wsdl
與jdk中提到的方式一樣,都是需要下載服務器端代碼到本地。
然後將所有代碼拷貝到自己的工程中。
- 調用遠程服務
2-1 創建代理工廠
2-2 設置服務地址
2-3 設置服務接口
2-4 創建遠程服務代理對象
2-5 調用遠程服務
package com.golden3young.cxf;
import org.apache.cxf.jaxws.JaxWsProxyFactoryBean;
import com.golden3young.server.User;
import com.golden3young.server.UserService;
public class CxfClient {
public static void main(String[] args) {
//創建代理工廠
JaxWsProxyFactoryBean factory = new JaxWsProxyFactoryBean();
//設置服務地址
factory.setAddress("http://localhost:9090/user?wsdl");
//設置服務接口
factory.setServiceClass(UserService.class);
//創建代理
UserService service = (UserService) factory.create();
System.out.println(service);
//調用服務
User user = service.queryUserById(10);
System.out.println(user.getId());
System.out.println(user.getName());
System.out.println(user.getAge());
System.out.println(user);
}
}
這裏有個地方需要注意,我們在服務器端生成的代碼,一旦被用戶下載到本地,進行使用的時候,客戶端的程序是依賴於客戶端的代碼執行的。舉一個很直接的例子:如果我們在服務器端自定義的User類中,並沒有重寫toString()方法,那麼一但打印User對象,結果其實是 類名@地址映射值,而如果此時,我們在客戶端下載下來的代碼中,給uUser類重寫toString()方法,那麼在客戶端中其他代碼中再打印User對象的時候,就不會像服務器端那樣打印類名@地址映射值 了,而是會依賴於我們拷貝到工程中的代碼來執行,由於我們重寫了toString,那麼打印的結果就會變成我們指定的內容。
同時,一旦服務器端對代碼進行了修改,是不會自動同步到客戶端代碼上的,而是需要客戶自己重新訪問url,重新下載新的代碼,並拷貝到自己的工程中,才能使用更新後的代碼。
複習一下:如果用 jdk的方式來獲取上面的這個Web Service的服務,也是可以的。
package com.golden3young.jdk;
import com.golden3young.server.User;
import com.golden3young.server.UserService;
import com.golden3young.server.UserServiceService;
public class JdkClient {
public static void main(String[] args) {
//這裏使用是xml中的serviceName,但此時由於服務器端是利用cxf部署的,
//所以不再像剛纔jdk工具生成發xml那種格式了。
//通過UserServiceSerivce就不難看出它們兩個的差別
//獲取Service對象
UserServiceService us = new UserServiceService();
//通過Service服務名字獲取port
UserService service = us.getUserServicePort();
//調用這個對象的方法並獲得返回值
User user = service.queryUserById(10);
//打印驗證
System.out.println(user.getId());
System.out.println(user.getName());
System.out.println(user.getAge());
System.out.println(user);
}
}
CXF整合Spring,開發Web Service
- 整合spring web,開發服務端
步驟:
- 創建spring-web.xml
- 創建spring-context.xml
- 配置CXFServlet
- 開發服務接口
- 開發服務實現
- 配置spring容器
- 部署到tomcat、測試
引入maven依賴
<dependencies>
<dependency>
<groupId>org.apache.cxf</groupId>
<artifactId>cxf-rt-frontend-jaxws</artifactId>
</dependency>
<dependency>
<groupId>org.apache.cxf</groupId>
<artifactId>cxf-rt-transports-http</artifactId>
</dependency>
<!-- spring web -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
</dependency>
<!-- spring-context -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
</dependency>
</dependencies>
修改web.xml
<servlet>
<servlet-name>cxf</servlet-name>
<servlet-class>org.apache.cxf.transport.servlet.CXFServlet</servlet-class>
<init-param>
<param-name>config-location</param-name>
<param-value>classpath:spring-cxf.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>cxf</servlet-name>
<url-pattern>/ws/*</url-pattern>
</servlet-mapping>
開發服務接口
package com.golden3young.service;
import java.util.List;
import javax.jws.WebService;
import com.etoak.bean.User;
@WebService //必須添加這個標籤,用於表明身份
public interface UserService {
int addUser(User user);
List<User> queryAllUsers();
}
開發服務實現
package com.golden3young.service.impl;
import java.util.ArrayList;
import java.util.List;
import javax.jws.WebService;
import org.springframework.stereotype.Service;
import com.golden3young.bean.User;
import com.golden3young.service.UserService;
@Service //爲了告訴spring容器,掃描成一個bean對象。 單例
@WebService //爲了告訴cxf組件,這是一個jaxws服務,表明身份
public class UserServiceImpl implements UserService {
//不建議在真實開發中如此書寫(創建全局集合),僅爲測試使用
List<User> users = new ArrayList<>();
@Override
public int addUser(User user) {
users.add(user);
return users.size();
}
@Override
public List<User> queryAllUsers() {
System.out.println("查詢所有用戶");
return users;
}
}
創建spring-cxf.xml,並移動到指定位置
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:jaxws="http://cxf.apache.org/jaxws"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation=
"http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-4.2.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-4.2.xsd
http://cxf.apache.org/jaxws
http://cxf.apache.org/schemas/jaxws.xsd">
<context:component-scan base-package="com.etoak" />
<!-- http://localhost:8080/contextPath/ws/user?wsdl -->
<jaxws:server address="/user" serviceClass="com.etoak.service.UserService">
<jaxws:serviceBean>
<ref bean="userServiceImpl" />
</jaxws:serviceBean>
</jaxws:server>
</beans>
上面配置的< jaxws:server >標籤其實就是替代了之前我們手動創建工廠的過程,接下來對於工廠的配置,由一開始的setter方法,變成了現在的標籤或者屬性:
JaxWSServiceFactroyBean [標籤<jaxws:server>]
factory.setAddress [屬性address="/user" ]
factory.setServiceClass [屬性serviceClass="com.etoak.service.UserService"]
factory.setServiceBean [標籤<jaxws:serviceBean> + <ref bean="userServiceImpl" />]
factory.create() //spring自動爲我們創建
- 整合spring web,開發客戶端
引入maven依賴
<dependencies>
<dependency>
<groupId>org.apache.cxf</groupId>
<artifactId>cxf-rt-frontend-jaxws</artifactId>
</dependency>
<dependency>
<groupId>org.apache.cxf</groupId>
<artifactId>cxf-rt-transports-http</artifactId>
</dependency>
<!-- spring-context -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
</dependency>
<!--
Jetty is needed if you're are not using the CXFServlet
-->
<dependency>
<groupId>org.apache.cxf</groupId>
<artifactId>cxf-rt-transports-http-jetty</artifactId>
<version>3.1.11</version>
</dependency>
</dependencies>
使用指令,下載web服務端代碼到本地
wsdl2java -d e:/jdk-client http://localhost:9090/user?wsdl
將下載下來的文件,拷貝到自己的工程中,以備使用。
創建測試類,調用使用
package com.golden3young;
import java.util.List;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import com.golden3young.service.User;
import com.golden3young.service.UserService;
public class SpringClient {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("spring-cxf.xml");
UserService service = (UserService)context.getBean("userService");
User user = new User();
user.setId(1);
user.setName("Tom");
user.setAge(22);
int result = service.addUser(user);
//System.out.println(result);
List<User> userList = service.queryAllUsers();
userList.forEach(System.out::println);
}
}
配置spring-cxf.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:jaxws="http://cxf.apache.org/jaxws"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-4.2.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-4.2.xsd
http://cxf.apache.org/jaxws
http://cxf.apache.org/schemas/jaxws.xsd">
<!-- jaxws:client 相當於一個遠程代理 是一個spring的Bean -->
<jaxws:client id="userService" address="http://localhost:8080/cxf-spring-web/ws/user?wsdl"
serviceClass="com.etoak.service.UserService">
</jaxws:client>
</beans>
以上< jaxws:client >標籤,其實相當於我們自己利用cxf組件創建WebService客戶端時書寫的代碼,只不過現在由spring自動爲我們構建。
JaxWSProxyFactoryBean [<jaxws:client>]
factory.setAddress() [address屬性]
factory.setServiceClass() [serviceClass屬性]
factory.create() //返回一個遠程服務的代理對象