Spring Http Invoker使用簡介


一、Spring HTTP Invoker簡介

    Spring HTTP invoker 是 spring 框架中的一個遠程調用模型,執行基於 HTTP 的遠程調用(意味着可以通過防火牆),並使用 java 的序列化機制在網絡間傳遞對象。這需要在遠端和本地都使用Spring才行。客戶端可以很輕松的像調用本地對象一樣調用遠程服務器上的對象,這有點類似於 webservice ,但又不同於 webservice ,區別如下:

WebService Http Invoker
跨平台,跨語言 只支持 java 語言
支持 SOAP ,提供 wsdl 不支持
結構龐大,依賴特定的 webservice 實現,如 xfire等 結構簡單,只依賴於 spring 框架本身

    說明:

    1. 服務器端:通過 HTTP invoker 服務將服務接口的某個實現類提供為遠程服務

    2. 客戶端:通過 HTTP invoker 代理向服務器端發送請求,遠程調用服務接口的方法

    3. 服務器端與客戶端通信的數據均需要序列化

 

二、配置服務器端和客戶端的步驟

 

配置服務器端

1. 添加 springJAR 文件

2. 創建相應的DTO(如果需要用到的話)

3. 創建服務接口

4. 創建服務接口的具體實現類

5. 公開服務

配置客戶端

 

1. 添加 springJAR 文件

2. 創建相應的DTO(如果需要用到的話)

3. 創建服務接口

4. 訪問服務


三、實例講解

 

 

配置服務器端

先來個項目結構圖:

190823_3jge_1434710.png

1). 添加 springJAR 文件,這就不用說了,直接照着圖片添加相應的類庫。

2). 創建服務接口和相應的DTO(Data Transmission Object)

這里我們需要調用遠端的服務來查詢一個User對象,因此需要DTO啦。下面這個User類就是用於在網絡中傳輸的POJO類,也就是DTO啦,因此需要實現Serializable接口:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
package  com.abc.invoke.bean;
 
import  java.io.Serializable;
 
public  class  User  implements  Serializable {
     private  static  final  long  serialVersionUID = -6970967506712260305L;
     private  String name;
     private  int  age;
     private  String email;
 
     public  int  getAge() {
         return  age;
     }
     
     public  void  setAge( int  age) {
         this .age = age;
     }
     
     public  String getName() {
         return  name;
     }
     
     public  void  setName(String name) {
         this .name = name;
     }
     
     public  String getEmail() {
         return  email;
     }
 
     public  void  setEmail(String email) {
         this .email = email;
     }
 
     @Override
     public  String toString() {
         return  "User [name="  + name +  ", age="  + age +  ", email="  + email +  "]" ;
     }
}

3). UserService是一個接口,里面定義了服務的方法,這里面的方法將會被客戶端調用:

?
1
2
3
4
5
6
7
package  com.abc.invoke.server.service;
 
import  com.abc.invoke.bean.User;
 
public  interface  UserService {
     public  User getUserbyName(String name);
}

4). 創建服務接口的具體實現類。這里的UserServiceImpl是實現了UserService方法:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
package  com.abc.invoke.server.service.impl;
 
import  com.abc.invoke.bean.User;
import  com.abc.invoke.server.service.UserService;
 
public  class  UserServiceImpl  implements  UserService {
     public  User getUserbyName(String name) {
         User u =  new  User();
         u.setName(name);
         u.setEmail( "abc@abc.com" );
         u.setAge( 20 );
         return  u;
     }
}

這里面我沒有寫DAO等層面的東西,因為那些不是這篇文章要講述的內容,因而我只是簡單的將傳給服務端的參數封裝到對象里的一個字段就返回了。

5). 公開服務

下面是web.xml文件的內容:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
<? xml  version = "1.0"  encoding = "UTF-8" ?>
< web-app  xmlns:xsi = "http://www.w3.org/2001/XMLSchema-instance"
     xmlns = "http://java.sun.com/xml/ns/javaee" 
     xmlns:web = "http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
     xsi:schemaLocation="http://java.sun.com/xml/ns/javaee 
     http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd">
     < display-name >SpringInvoke</ display-name >
     < servlet >
         < servlet-name >service</ servlet-name >
         < servlet-class >org.springframework.web.servlet.DispatcherServlet</ servlet-class >
         < init-param >
             < param-name >contextConfigLocation</ param-name >
             < param-value >classpath:service-servlet.xml</ param-value >
         </ init-param >
         < load-on-startup >1</ load-on-startup >
     </ servlet >
     < servlet-mapping >
         < servlet-name >service</ servlet-name >
         < url-pattern >/service/*</ url-pattern >
     </ servlet-mapping >
     
     <!-- 其實下面這個welcome-file-list沒啥用,我留着只是為了在起好Tomcat后不會報一個404而已 -->
     < welcome-file-list >
             < welcome-file >index.html</ welcome-file >
     </ welcome-file-list >
</ web-app >

這里我們使用/service作為service的前綴,那么客戶端請求調用時需要加上這個前綴,比如:

http://{host}:{port}/InvokeServer/service/{serviceName}

里面用到的service-servlet文件:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
<? 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:context = "http://www.springframework.org/schema/context"
     xsi:schemaLocation="http://www.springframework.org/schema/beans
     http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
     http://www.springframework.org/schema/context
     http://www.springframework.org/schema/context/spring-context-3.0.xsd">
 
     <!-- 這個Bean映射了當URL是/userService時,處理器為userServiceInvoker -->
     < bean  class = "org.springframework.web.servlet.handler.SimpleUrlHandlerMapping" >
         < property  name = "mappings" >
             < props >
                 < prop  key = "/userService" >userServiceInvoker</ prop >
             </ props >
         </ property >
     </ bean >
 
     <!-- Announce that this interface is a HTTP invoker service. -->
     < bean  id = "userServiceInvoker" 
         class = "org.springframework.remoting.httpinvoker.HttpInvokerServiceExporter" >
         < property  name = "service"  ref = "userServiceImpl"  />
         < property  name = "serviceInterface"  value = "com.abc.invoke.server.service.UserService"  />
     </ bean >
     < bean  id = "userServiceImpl"  class = "com.abc.invoke.server.service.impl.UserServiceImpl"  />
</ beans >

注意:

  1. <prop key="/userService">userServiceInvoker</prop>中的/userService是請求的服務的URL中的一部分,就是說這樣的URL會被userServiceInvoker處理

  2. 這里將com.abc.invoke.server.service.UserService映射給了com.abc.invoke.server.service.impl.UserServiceImpl類了。

到此為止,服務器算是配置好了,接下來開始配置客戶端。

 

配置客戶端

先來看看項目結構圖:

193148_xjok_1434710.png

1). 添加 springJAR 文件,這也不用說了,直接照着圖片添加相應的類庫。

2). 創建服務接口和相應的DTO。

特別注意:這個類和Server端聲明的DTO要一樣,包名和字段名都要一樣才行。因為客戶端發起請求查詢User,服務端處理后先將User序列化后在返回給客戶端,而客戶端拿到這個User后需要將其反序列化。如果包名或者字段名不同,則會被認為是不同的對象,會反序列化失敗,調用也就出錯了。我之前就是將User類的包名寫得不一樣(User類的包名在服務端為com.abc.invoke.server.bean,而在客戶端則為com.abc.invoke.client.bean),報了以下錯誤:

?
1
2
3
4
5
6
7
8
9
Exception in thread  "main"  org.springframework.remoting.RemoteAccessException: 
     Could not deserialize result from HTTP invoker remote service [http: //localhost:8080/InvokeServer/service/userService]; 
     nested exception is java.lang.ClassNotFoundException: com.abc.invoke.server.bean.User
     at org.springframework.remoting.httpinvoker.HttpInvokerClientInterceptor.convertHttpInvokerAccessException(HttpInvokerClientInterceptor.java: 208 )
     at org.springframework.remoting.httpinvoker.HttpInvokerClientInterceptor.invoke(HttpInvokerClientInterceptor.java: 145 )
     at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java: 172 )
     at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java: 202 )
     at com.sun.proxy.$Proxy0.getUserbyName(Unknown Source)
     at com.abc.invoke.client.Test.main(Test.java: 14 )

很明顯可以看出,Could not deserialize result from HTTP invoker remote service......,就是因為Server端與Client端的DTO的包名不同導致反序列化失敗。

3). 創建服務接口

這也沒啥好說的,接口和Server端定義的一樣就行,不一樣肯定報錯。可以直接將DTO和接口定義的類拷貝到客戶端即可。這個接口將會被看做是客戶端和服務端通信的“契約”。

4). 訪問服務

來看看application-context.xml:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<? 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:context = "http://www.springframework.org/schema/context"
     xsi:schemaLocation="http://www.springframework.org/schema/beans
          http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
          http://www.springframework.org/schema/context
          http://www.springframework.org/schema/context/spring-context-3.0.xsd">
     
     <!-- 客戶端使用 HttpInvokerProxyFactoryBean 代理客戶端向服務器端發送請求,請求接口為 UserService 的服務 -->
     < bean  id = "userService"  class = "org.springframework.remoting.httpinvoker.HttpInvokerProxyFactoryBean"  >
         < property  name = "serviceUrl"  value = "http://localhost:8080/InvokeServer/service/userService" />
         < property  name = "serviceInterface"  value = "com.abc.invoke.client.service.UserService"  />
     </ bean >
     
</ beans >

這里使用了org.springframework.remoting.httpinvoker.HttpInvokerProxyFactoryBean這個類來作為一個service的代理類。注意到serviceUrl屬性為http://localhost:8080/InvokeServer/service/userService (當然,我在本機啟動的服務端並在本機通過main函數調用service,我在另一台機器上運行Test類的main函數,調用結果正確)。這個localhost:8080應改為實際的IP地址和端口。),這個URL的地址以/service開始,因此會被Server端攔截下來,而URL中的 /userService則為service路徑,該路徑與在Server端中service-servlet.xml中聲明的

?
1
< prop  key = "/userService" >userServiceInvoker</ prop >

路徑一致,因此這個調用會被userServiceInvoker處理。

最后再寫一個簡單的測試類Test.java:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
package  com.abc.invoke.client;
 
import  org.springframework.context.ApplicationContext;
import  org.springframework.context.support.ClassPathXmlApplicationContext;
 
import  com.abc.invoke.bean.User;
import  com.abc.invoke.client.service.UserService;
 
public  class  Test {
    public  static  void  main(String[] args) {
        ApplicationContext ac =  new  ClassPathXmlApplicationContext(
                        "classpath:application-context.xml" );
        UserService service = (UserService)ac.getBean( "userService" );
        User u = service.getUserbyName( "Alvis" );
        System.out.println(u);
    }
}

這個類也很簡單,就是從Spring的Context中取出了定義的userService這個Bean(這其實就是服務端service的一個代理類),然后直接調用該service的方法獲得結果並打印。

到此為止,客戶端配置完成。

四、啟動服務並測試

直接在項目InvokeServer上啟動Tomcat,可以看到路徑/userService的處理者是userServiceInvoker:

195506_0iND_1434710.png

下面是遠程調用的執行結果:

195616_uwup_1434710.png

從結果中可以看到,我代碼里寫的名字叫Alvis,用客戶端調用服務端的service后,返回的對象中名字是客戶端設置的名字,測試成功。

 

這里是項目源代碼,供需要的朋友參考。

參考頁面:http://hanqunfeng.iteye.com/blog/868210


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM